diff --git a/.github/workflows/IKVM.yml b/.github/workflows/IKVM.yml index 8adeee8014..d7e07166a1 100644 --- a/.github/workflows/IKVM.yml +++ b/.github/workflows/IKVM.yml @@ -15,20 +15,20 @@ on: jobs: build-openjdk: name: Build OpenJDK - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout Source - uses: actions/checkout@v1 + uses: actions/checkout@v3 with: submodules: recursive - name: Cache OpenJDK Build uses: actions/cache@v3 with: path: openjdk/build/linux-x86_64-normal-server-release - key: openjdk-build-linux-x86_64-normal-server-release--${{ runner.os }}--${{ hashFiles('openjdk/**', '!openjdk/build') }}-8 + key: openjdk-build-linux-x86_64-normal-server-release--${{ runner.os }}--${{ hashFiles('openjdk/**', '!openjdk/build') }}-10 - name: Check OpenJDK Build Stamp id: openjdk-build-stamp - uses: andstor/file-existence-action@v1 + uses: andstor/file-existence-action@v2 with: files: openjdk/build/linux-x86_64-normal-server-release/stamp - name: Fetch OpenJDK 7 @@ -74,13 +74,7 @@ jobs: - name: Package OpenJDK run: | zip -r openjdk-build-linux-x86_64-normal-server-release.zip \ - linux-x86_64-normal-server-release/langtools/gensrc \ - linux-x86_64-normal-server-release/corba/gensrc \ - linux-x86_64-normal-server-release/jdk/gensrc \ - linux-x86_64-normal-server-release/jdk/impsrc \ - linux-x86_64-normal-server-release/jdk/lib \ - linux-x86_64-normal-server-release/jdk/classes \ - linux-x86_64-normal-server-release/images + linux-x86_64-normal-server-release/* working-directory: openjdk/build - name: Upload OpenJDK uses: actions/upload-artifact@v3 @@ -94,7 +88,7 @@ jobs: key: jtreg-build--${{ runner.os }}--${{ hashFiles('jtreg/**', 'openjdk/build/linux-x86_64-normal-server-release/images/j2sdk-image', '!jtreg/build') }}-6 - name: Check OpenJDK Test Harness Build Stamp id: jtreg-build-stamp - uses: andstor/file-existence-action@v1 + uses: andstor/file-existence-action@v2 with: files: jtreg/build/stamp - name: Build OpenJDK Test Harness @@ -113,20 +107,30 @@ jobs: name: Build IKVM needs: - build-openjdk - timeout-minutes: 120 + timeout-minutes: 240 runs-on: windows-latest steps: + - name: Set Paths (Windows) + shell: pwsh + run: | + $WORKPATH="C:\work" + mkdir $WORKPATH + mkdir $WORKPATH\temp + mkdir $WORKPATH\dotnet + mkdir $WORKPATH\nuget + mkdir $WORKPATH\nuget\packages + mkdir $WORKPATH\ikvm + Add-Content $env:GITHUB_ENV "`nWORKPATH=$WORKPATH" + Add-Content $env:GITHUB_ENV "`nTMP=$WORKPATH\temp`nTEMP=$WORKPATH\temp`nTMPDIR=$WORKPATH\temp" + Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=$WORKPATH\dotnet" + Add-Content $env:GITHUB_ENV "`nNUGET_PACKAGES=$WORKPATH\nuget\packages" + New-Item -Path "$env:GITHUB_WORKSPACE\work" -ItemType SymbolicLink -Value "$WORKPATH\ikvm" - name: Checkout Source uses: actions/checkout@v3 with: + path: work fetch-depth: 0 submodules: recursive - - name: Move Temporary Directory - shell: pwsh - run: Add-Content $env:GITHUB_ENV "`nTMP=${{ runner.temp }}`nTEMP=${{ runner.temp }}`nTMPDIR=${{ runner.temp }}" - - name: Setup .NET - shell: pwsh - run: Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=${{ runner.temp }}\dotnet" - name: Setup .NET 3.1 uses: actions/setup-dotnet@v3 with: @@ -139,64 +143,67 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x + - name: Cache LLVM and Clang + id: cache-llvm + uses: actions/cache@v3 + with: + path: C:/Program Files/LLVM + key: llvm-15.0 + - name: Install LLVM and Clang + uses: KyleMayes/install-llvm-action@v1 + with: + version: "15.0" + cached: ${{ steps.cache-llvm.outputs.cache-hit }} - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.13 + uses: gittools/actions/gitversion/setup@v0.9.15 with: versionSpec: 5.x - name: Execute GitVersion - uses: gittools/actions/gitversion/execute@v0.9.13 + uses: gittools/actions/gitversion/execute@v0.9.15 with: + targetPath: ${{ env.WORKPATH }}\ikvm useConfigFile: true + configFilePath: ${{ env.WORKPATH }}\ikvm\GitVersion.yml - name: Setup WSL - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 with: additional-packages: zlib1g-dev libmpc-dev libmpfr-dev libgmp-dev - clang - llvm - llvm-dev + libxml2 gcc g++ - g++-arm-linux-gnueabihf - gcc-arm-linux-gnueabihf - libc6-armhf-cross - gcc-aarch64-linux-gnu - g++-aarch64-linux-gnu - libc6-arm64-cross - - name: Move NuGet Directory - shell: pwsh - run: Add-Content $env:GITHUB_ENV "`nNUGET_PACKAGES=${{ runner.temp }}\nuget\packages" - name: Cache NuGet uses: actions/cache@v3 with: - path: ${{ runner.temp }}\nuget\packages - key: ${{ runner.os }}-nuget-${{ hashFiles('IKVM.sln', 'src\**\*.csproj', 'src\**\*.msbuildproj') }} - restore-keys: ${{ runner.os }}-nuget- + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-2-${{ hashFiles('IKVM.sln', 'src\**\*.csproj', 'src\**\*.msbuildproj') }} + restore-keys: ${{ runner.os }}-nuget-2- - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 - name: Download OpenJDK 8 Build uses: actions/download-artifact@v3 with: name: openjdk-build-linux-x86_64-normal-server-release - path: openjdk/build + path: ${{ env.WORKPATH }}/ikvm/openjdk/build - name: Restore OpenJDK 8 Build shell: pwsh run: Expand-Archive openjdk-build-linux-x86_64-normal-server-release.zip . - working-directory: openjdk/build + working-directory: ${{ env.WORKPATH }}\ikvm\openjdk\build - name: Download JTReg Build uses: actions/download-artifact@v3 with: name: jtreg-build - path: jtreg + path: ${{ env.WORKPATH }}/ikvm/jtreg - name: Restore JTReg Build shell: pwsh run: Expand-Archive jtreg-build.zip . - working-directory: jtreg + working-directory: ${{ env.WORKPATH }}\ikvm\jtreg - name: NuGet Restore run: dotnet restore IKVM.sln + working-directory: ${{ env.WORKPATH }}\ikvm - name: Build Artifacts run: | msbuild /m /bl ` @@ -217,15 +224,16 @@ jobs: /p:CreateHardLinksForPublishFilesIfPossible=true ` /p:ContinuousIntegrationBuild=true ` IKVM.dist.msbuildproj + working-directory: ${{ env.WORKPATH }}\ikvm - name: Upload MSBuild Log if: ${{ always() }} uses: actions/upload-artifact@v3 with: name: msbuild.binlog - path: msbuild.binlog + path: ${{ env.WORKPATH }}\ikvm\msbuild.binlog - name: Package NuGet Packages run: tar czvf C:\nuget.tar.gz nuget - working-directory: dist + working-directory: ${{ env.WORKPATH }}\ikvm\dist - name: Upload NuGet Packages uses: actions/upload-artifact@v3 with: @@ -233,7 +241,7 @@ jobs: path: C:\nuget.tar.gz - name: Package Binaries run: tar czvf C:\bin.tar.gz bin - working-directory: dist + working-directory: ${{ env.WORKPATH }}\ikvm\dist - name: Upload Binaries uses: actions/upload-artifact@v3 with: @@ -241,7 +249,7 @@ jobs: path: C:\bin.tar.gz - name: Package Tools run: tar czvf C:\tools.tar.gz tools - working-directory: dist + working-directory: ${{ env.WORKPATH }}\ikvm\dist - name: Upload Tools uses: actions/upload-artifact@v3 with: @@ -249,7 +257,7 @@ jobs: path: C:\tools.tar.gz - name: Package Image run: tar czvf C:\image.tar.gz image - working-directory: dist + working-directory: ${{ env.WORKPATH }}\ikvm\dist - name: Upload Image uses: actions/upload-artifact@v3 with: @@ -257,7 +265,7 @@ jobs: path: C:\image.tar.gz - name: Package Tests run: tar czvf C:\tests.tar.gz tests - working-directory: dist + working-directory: ${{ env.WORKPATH }}\ikvm\dist - name: Upload Tests uses: actions/upload-artifact@v3 with: @@ -267,12 +275,14 @@ jobs: run: | git reset --hard git clean -qfdx + working-directory: ${{ env.WORKPATH }}\ikvm test: strategy: matrix: run: + - IKVM.ByteCode.Tests - IKVM.Tests - - ikvmc.Tests + - IKVM.Tools.Importer.Tests - IKVM.Tools.Exporter.Tests - IKVM.Tools.Tests - IKVM.JTReg.TestAdapter.Tests @@ -296,7 +306,7 @@ jobs: sys: linux - run: IKVM.Tools.Exporter.Tests tfm: net6.0 - - run: ikvmc.Tests + - run: IKVM.Tools.Importer.Tests tfm: net6.0 - run: IKVM.OpenJDK.Tests?TestPartition=0 tfm: net6.0 @@ -342,7 +352,7 @@ jobs: - run: IKVM.NET.Sdk.Tests tfm: net6.0 sys: linux - name: Test (${{ matrix.run }}:${{ matrix.tfm }}:${{ matrix.sys }} + name: Test (${{ matrix.run }}:${{ matrix.tfm }}:${{ matrix.sys }}) needs: - build-ikvm timeout-minutes: 240 @@ -352,7 +362,7 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | - $dir="C:\run" + $dir="C:\work" mkdir $dir mkdir $dir\temp mkdir $dir\dotnet @@ -368,7 +378,7 @@ jobs: if: runner.os == 'Linux' shell: pwsh run: | - $dir="${{ runner.temp }}/run" + $dir="${{ runner.temp }}/work" mkdir $dir mkdir $dir/temp mkdir $dir/dotnet @@ -394,7 +404,7 @@ jobs: dotnet-version: 7.0.x - name: Setup WSL if: matrix.sys == 'windows' - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 - name: Download Tests uses: actions/download-artifact@v3 with: @@ -464,12 +474,12 @@ jobs: with: dotnet-version: 7.0.x - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.13 + uses: gittools/actions/gitversion/setup@v0.9.15 with: versionSpec: 5.x - name: Execute GitVersion id: GitVersion - uses: gittools/actions/gitversion/execute@v0.9.13 + uses: gittools/actions/gitversion/execute@v0.9.15 with: useConfigFile: true - name: Download NuGet Packages diff --git a/.gitmodules b/.gitmodules index 6a79c78f6a..9bdb8ca246 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "openjdk"] path = openjdk - url = https://github.com/ikvm-revived/jdk8u.git + url = https://github.com/ikvmnet/jdk8u.git ignore = dirty [submodule "jtreg"] path = jtreg - url = https://github.com/ikvm-revived/jtreg.git + url = https://github.com/ikvmnet/jtreg.git ignore = dirty diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2306698d42..0dbe637aea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,13 +2,24 @@ ## Build -Project should open within Visual Studio. Project can also be built with MSBuild on a Windows host. Project cannot currently be built on a Linux host, nor with an exclusively .NET Core version of MSBuild. +The project can be opened in Visual Studio, or it can be be built with MSBuild on a Windows host. The project cannot currently be built on a Linux host, nor with an exclusively .NET Core version of MSBuild. -The OpenJDK JDK8u source hierarchy and build results for Linux/x64 is required. These build artifacts cannot be built on Windows, or modern Linux hosts. Instead, they must be built on a host with GCC 4.3 available. Debian Lenny is known to work acceptably. The CI/CD GitHub action can serve as a demonstration of this. +Prerequisites for building the project: +* A clone of the IKVM repository which includes submodules (e.g. `git clone --recurse-submodules https://github.com/ikvm-revived/ikvm.git`) +* Visual Studio configured for: .NET desktop development, Desktop development with C++ (including C++ Clang tools for Windows), Linux and Embedded Development with C++ +* A JDK 8 installation. The `JAVA_HOME` environment variable needs to be point to the JDK 8 directory, or the version of `javac` available on the path needs to be from JDK 8 (You can download a suitable JDK from [Adoptium](https://adoptium.net/)) +* The OpenJDK JDK8u build result for Linux/x64. This build artifact cannot be built on Windows, or modern Linux hosts. Instead, it must be built on a host with GCC 4.3 available. (Debian Lenny is known to work acceptably, the CI/CD GitHub action can serve as a demonstration of this). The easiest way to acquire this artifact without building it yourself is to use one generated by the IKVM respository's [GitHub Actions](https://github.com/ikvm-revived/ikvm/actions). Click on a build result for a relevant branch (typically you'll want to use `develop`) and scroll down to the bottom of the list to locate `openjdk-build-linux-x86_64-normal-server-release`. Download this zip file from this link and unzip into + `openjdk/build`. + * LLVM installed with `clang` available on the path. LLVM is shipped with Visual Studio, but you will need to update your PATH to include the relevant bin directory (typically: `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin`) or you can install a [standalone distribution of LLVM](https://releases.llvm.org/). + * Windows Subsystem for Linux enabled, and a Linux distribution with g++ installed + * 200GB of free disk space + + Once the prerequisites are in place, to build IKVM from the command line you will use the following commands: -The GitHub action's generated artifact can simply be extract into the appropriate spot in `openjdk/build` to avoid building it yourself. Navigate to the GitHub Actions, find the latest successfuly build for the branch you're concerned with, and download the `openjdk-build-linux-x86_64-normal-server-release` artifact. Extract this zip file into `openjdk/build`. - -IKVM includes a native library named 'ikvm-native' which must be built for the JNI functionality to work. The solution includes `.vcxproj` files that build both the win-x64, linux-x86 and linux-x64 versions of these libraries. However, the linux-* version require WSL to be enabled on your development machine. Within this WSL distribution ensure you have installed the GCC toolset. For Debian based distributions, this should be as simple as typing `apt-get install g++`. + ``` + dotnet restore IKVM.sln + msbuild IKVM.dist.msbuildproj + ``` ## Project diff --git a/Directory.Build.props b/Directory.Build.props index 519fe0b748..5469fd0dd1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,9 +4,11 @@ $(NoWarn);1591;1573;CS8002;NU5100;NU5118;NU5128 true true - 15 - 15000 false + 30 + 15000 + win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64;osx-x64 + win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64;osx-x64 @@ -32,9 +34,9 @@ - https://github.com/ikvm-revived/ikvm.git + https://github.com/ikvmnet/ikvm.git git - https://github.com/ikvm-revived/ikvm + https://github.com/ikvmnet/ikvm $(Version) diff --git a/Directory.Build.targets b/Directory.Build.targets index 7b0e9df682..58a5d0c409 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,7 +1,6 @@ - - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FUNDING.yml b/FUNDING.yml index 037ca9e474..f9fe3fdd8a 100644 --- a/FUNDING.yml +++ b/FUNDING.yml @@ -1,2 +1,2 @@ -github: [wasabii, NightOwl888, joniles] +github: [wasabii, NightOwl888] custom: ["https://www.paypal.me/ShadStorhaug"] diff --git a/IKVM.deps.props b/IKVM.deps.props index 58ddf05804..cc8c723926 100644 --- a/IKVM.deps.props +++ b/IKVM.deps.props @@ -3,7 +3,6 @@ - @@ -11,6 +10,7 @@ + @@ -28,6 +28,7 @@ + diff --git a/IKVM.dist.msbuildproj b/IKVM.dist.msbuildproj index 2e8612da99..8a372581d2 100644 --- a/IKVM.dist.msbuildproj +++ b/IKVM.dist.msbuildproj @@ -10,23 +10,23 @@ - dist\dist-bin:Publish + dist%5Cdist-bin:Publish PublishDir=$(DistDir)\bin - dist\dist-tools:Publish + dist%5Cdist-tools:Publish PublishDir=$(DistDir)\tools - dist\dist-image:Publish + dist%5Cdist-image:Publish PublishDir=$(DistDir)\image - dist\dist-nuget:Publish + dist%5Cdist-nuget:Publish PublishDir=$(DistDir)\nuget - dist\dist-tests:Publish + dist%5Cdist-tests:Publish PublishDir=$(DistDir)\tests diff --git a/IKVM.refs.props b/IKVM.refs.props index eb72955dfc..51ed217988 100644 --- a/IKVM.refs.props +++ b/IKVM.refs.props @@ -1,6 +1,10 @@ + + true + all + true all @@ -11,64 +15,4 @@ - - - Platform=x64 - runtimes\win-x64\native - - - Platform=Win32 - runtimes\win-x86\native - - - Platform=ARM - runtimes\win-arm\native - - - Platform=x64 - runtimes\linux-x64\native - - - Platform=x86 - runtimes\linux-arm\native - - - Platform=x64 - runtimes\linux-arm64\native - - - - - - Platform=x64 - runtimes\win-x64\native - false - - - Platform=Win32 - runtimes\win-x86\native - false - - - Platform=ARM - runtimes\win-arm\native - false - - - Platform=x64 - runtimes\linux-x64\native - false - - - Platform=x86 - runtimes\linux-arm\native - false - - - Platform=x64 - runtimes\linux-arm64\native - false - - - diff --git a/IKVM.sln b/IKVM.sln index 764818466f..58acd14991 100644 --- a/IKVM.sln +++ b/IKVM.sln @@ -39,20 +39,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tests", "src\IKVM.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Runner", "src\IKVM.Tools.Runner\IKVM.Tools.Runner.csproj", "{8267A3D1-2B36-471C-9E9F-2E1B0C223632}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ikvm-native", "ikvm-native", "{0606E530-F834-44A3-9B4E-37852A06D11B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-native-win", "src\ikvm-native\ikvm-native-win.vcxproj", "{D8B580D1-D12B-39CD-A42B-BAEE36602AA1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-native-linux", "src\ikvm-native\ikvm-native-linux.vcxproj", "{F2A81A31-C65F-4B4E-A6FE-B14E71515EA3}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-tests-native-win", "src\ikvm-tests-native\ikvm-tests-native-win.vcxproj", "{A275C11D-F06E-475F-AEBA-7F7A1EF8D140}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-tests-native-linux", "src\ikvm-tests-native\ikvm-tests-native-linux.vcxproj", "{E566FE94-C551-43A7-843C-5E8199CA7373}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ikvm-tests-native", "ikvm-tests-native", "{B6083619-C4E8-4D8E-95BD-217E3663134C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ikvmc.Tests", "src\ikvmc.Tests\ikvmc.Tests.csproj", "{CF80B061-AF1B-4C15-A725-10C0A5D267CC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tests.Util", "src\IKVM.Tests.Util\IKVM.Tests.Util.csproj", "{E2C71F2E-2940-4990-A618-E7E47C00D6A6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Util", "src\IKVM.Util\IKVM.Util.csproj", "{ADACA319-6E64-4351-8E00-FB03B98BFC8C}" @@ -75,9 +67,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.MSBuild.Tests", "src\I {95C8865C-AB87-4998-880E-38B7F76BAC57} = {95C8865C-AB87-4998-880E-38B7F76BAC57} {95F23908-2D65-40C7-9EF1-B5C8543503AA} = {95F23908-2D65-40C7-9EF1-B5C8543503AA} {9AE06A6D-1335-482C-8ABF-2FE1BADF5AD7} = {9AE06A6D-1335-482C-8ABF-2FE1BADF5AD7} - {A204C3A9-F559-4E50-B667-F8B5D27501D1} = {A204C3A9-F559-4E50-B667-F8B5D27501D1} {AE5BFBC6-7DD4-4ACA-BEA7-86F842F38B64} = {AE5BFBC6-7DD4-4ACA-BEA7-86F842F38B64} - {B02CE9AA-AB52-491A-A089-A1BDF297A800} = {B02CE9AA-AB52-491A-A089-A1BDF297A800} {BE31AC7C-79CC-4180-A5E8-BEB3FD7579DC} = {BE31AC7C-79CC-4180-A5E8-BEB3FD7579DC} {CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D} = {CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D} {DF09A175-C790-434D-9810-A6BBB3EEC548} = {DF09A175-C790-434D-9810-A6BBB3EEC548} @@ -93,7 +83,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Java", "src\IKVM.Java\IKVM.Java.msbuildproj", "{ABD81C7E-F986-4018-986E-ACAF82C64D3A}" ProjectSection(ProjectDependencies) = postProject {0A87FF49-AC9D-479A-ACBB-F4028A0BE1F7} = {0A87FF49-AC9D-479A-ACBB-F4028A0BE1F7} + {17979A73-C0CE-41CD-B54F-4E72B998E6D0} = {17979A73-C0CE-41CD-B54F-4E72B998E6D0} {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} = {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} + {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7} = {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.NET.Sdk", "src\IKVM.NET.Sdk\IKVM.NET.Sdk.msbuildproj", "{704BD7C7-7746-4D72-A86E-ECFE7BBD80CC}" @@ -111,7 +103,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Exporter", "src\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Exporter.Tests", "src\IKVM.Tools.Exporter.Tests\IKVM.Tools.Exporter.Tests.csproj", "{EFF93C65-3F45-48B6-9178-1E68ADD41974}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Tests", "src\IKVM.Tools.Tests\IKVM.Tools.Tests.csproj", "{E4E6EAB9-2C68-49AC-A212-D464F1BB483B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Tests", "src\IKVM.Tools.Tests\IKVM.Tools.Tests.csproj", "{8C95ECC6-E996-4DA0-A4C8-F05373CBB0EA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "javac", "src\javac\javac.msbuildproj", "{78BE5834-EC02-48C5-A83A-2693D777375A}" EndProject @@ -129,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "rmic", "src\rmic\rmic.msbui EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jar", "src\jar\jar.msbuildproj", "{B240F8BA-6C35-4C19-9035-2821CC6BFEA8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "orbd", "src\orbd\orbd.msbuildproj", "{B240F8BA-6C35-4C19-9035-2821CC6BFFA8}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jarsigner", "src\jarsigner\jarsigner.msbuildproj", "{C6C34134-D80C-43CC-A477-32B4172374D2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "javadoc", "src\javadoc\javadoc.msbuildproj", "{4371190E-AD8B-4625-A520-B745B05DE005}" @@ -141,6 +135,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "javah", "src\javah\javah.ms EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "javap", "src\javap\javap.msbuildproj", "{03F00630-3625-440D-BE43-51751FCE7806}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "schemagen", "src\schemagen\schemagen.msbuildproj", "{03F00630-3625-440D-BE43-51751FCE7802}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jdeps", "src\jdeps\jdeps.msbuildproj", "{FB1F02F2-3A18-49CC-A288-EC0834D90EED}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wsgen", "src\wsgen\wsgen.msbuildproj", "{ECF6B110-DF3B-4D29-91C8-45AD4C7D4B17}" @@ -169,10 +165,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK.runtime.win7 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image", "src\IKVM.Image\IKVM.Image.csproj", "{95C8865C-AB87-4998-880E-38B7F76BAC57}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK", "src\IKVM.Image.JDK\IKVM.Image.JDK.csproj", "{B02CE9AA-AB52-491A-A089-A1BDF297A800}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE", "src\IKVM.Image.JRE\IKVM.Image.JRE.csproj", "{A204C3A9-F559-4E50-B667-F8B5D27501D1}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE.runtime.win7-x86", "src\IKVM.Image.JRE.runtime.win7-x86\IKVM.Image.JRE.runtime.win7-x86.csproj", "{CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D}" ProjectSection(ProjectDependencies) = postProject {162A1F6D-7752-4686-A6C2-923633F4E4C0} = {162A1F6D-7752-4686-A6C2-923633F4E4C0} @@ -219,6 +211,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE.runtime.win8 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.JTReg.TestAdapter.Core", "src\IKVM.JTReg.TestAdapter.Core\IKVM.JTReg.TestAdapter.Core.csproj", "{DD5AEB88-DC55-43C8-81C1-2879F73C8DE0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK.runtime.osx-x64", "src\IKVM.Image.JDK.runtime.osx-x64\IKVM.Image.JDK.runtime.osx-x64.csproj", "{011FF139-A9E6-4F10-BBC8-28BC1A38D966}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE.runtime.osx-x64", "src\IKVM.Image.JRE.runtime.osx-x64\IKVM.Image.JRE.runtime.osx-x64.csproj", "{FD39DA19-C81E-4752-B308-43594CC201BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.ByteCode", "src\IKVM.ByteCode\IKVM.ByteCode.csproj", "{032B77B4-199E-4077-841E-951BAD862050}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dist", "dist", "{F8B6B3D8-006C-4D8F-8883-EB48D9EB417F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dist-bin", "src\dist-bin\dist-bin.csproj", "{B60EC8AA-3CDF-45E4-9ACD-3246B375C027}" @@ -240,12 +238,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.MSBuild.Tools.runtime. EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.MSBuild.Tools.runtime.linux-x64", "src\IKVM.MSBuild.Tools.runtime.linux-x64\IKVM.MSBuild.Tools.runtime.linux-x64.csproj", "{F162126C-DA9C-4195-B295-CF1020DBA98A}" - ProjectSection(ProjectDependencies) = postProject - {0A87FF49-AC9D-479A-ACBB-F4028A0BE1F7} = {0A87FF49-AC9D-479A-ACBB-F4028A0BE1F7} - {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} = {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} - EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-native-linux-arm64", "src\ikvm-native\ikvm-native-linux-arm64.vcxproj", "{BE117661-1954-42C3-8A16-3F89815F7222}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.MSBuild.Tools.runtime.osx-x64", "src\IKVM.MSBuild.Tools.runtime.osx-x64\IKVM.MSBuild.Tools.runtime.osx-x64.csproj", "{E690EC25-3A73-408F-93B8-36990B2A9D0E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK.runtime.linux-arm64", "src\IKVM.Image.JDK.runtime.linux-arm64\IKVM.Image.JDK.runtime.linux-arm64.csproj", "{DF09A175-C790-434D-9810-A6BBB3EEC548}" ProjectSection(ProjectDependencies) = postProject @@ -269,8 +263,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.MSBuild.Tools.runtime. {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} = {2F29E48C-63C3-4E47-BCBA-A7454B9119CF} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ikvm-native-linux-arm", "src\ikvm-native\ikvm-native-linux-arm.vcxproj", "{8A321DFF-99AB-4279-828A-A9BA872010FD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK.runtime.linux-arm", "src\IKVM.Image.JDK.runtime.linux-arm\IKVM.Image.JDK.runtime.linux-arm.csproj", "{437506E7-B124-4808-8AE7-8864AC9EA36A}" ProjectSection(ProjectDependencies) = postProject {CCE3DE31-5DE7-4157-90D1-BAFA08C181EC} = {CCE3DE31-5DE7-4157-90D1-BAFA08C181EC} @@ -283,6 +275,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE.runtime.linu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "native2ascii", "src\native2ascii\native2ascii.msbuildproj", "{7BC3FC79-5938-4CD7-9528-47D5DC18729E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JDK", "src\IKVM.Image.JDK\IKVM.Image.JDK.csproj", "{7C97E1C8-7FEB-4C2A-A149-7D77E15838BF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Image.JRE", "src\IKVM.Image.JRE\IKVM.Image.JRE.csproj", "{819C752E-1F74-4447-9214-86EF337A1DEF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Importer", "src\IKVM.Tools.Importer\IKVM.Tools.Importer.csproj", "{50954AE0-E513-4CE7-AC8E-F3896CA0BEB7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tools.Importer.Tests", "src\IKVM.Tools.Importer.Tests\IKVM.Tools.Importer.Tests.csproj", "{6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.Tests.Java", "src\IKVM.Tests.Java\IKVM.Tests.Java.msbuildproj", "{87CB99AE-16D1-4D58-BEAC-72485E02B321}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IKVM.ByteCode.Tests", "src\IKVM.ByteCode.Tests\IKVM.ByteCode.Tests.csproj", "{60E804E4-B25B-4C9A-A3E6-F83D618CFD26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -321,14 +325,6 @@ Global {8267A3D1-2B36-471C-9E9F-2E1B0C223632}.Debug|Any CPU.Build.0 = Debug|Any CPU {8267A3D1-2B36-471C-9E9F-2E1B0C223632}.Release|Any CPU.ActiveCfg = Release|Any CPU {8267A3D1-2B36-471C-9E9F-2E1B0C223632}.Release|Any CPU.Build.0 = Release|Any CPU - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1}.Debug|Any CPU.ActiveCfg = Debug|x64 - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1}.Debug|Any CPU.Build.0 = Debug|x64 - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1}.Release|Any CPU.ActiveCfg = Release|x64 - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1}.Release|Any CPU.Build.0 = Release|x64 - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3}.Debug|Any CPU.ActiveCfg = Debug|x64 - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3}.Debug|Any CPU.Build.0 = Debug|x64 - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3}.Release|Any CPU.ActiveCfg = Release|x64 - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3}.Release|Any CPU.Build.0 = Release|x64 {A275C11D-F06E-475F-AEBA-7F7A1EF8D140}.Debug|Any CPU.ActiveCfg = Debug|x64 {A275C11D-F06E-475F-AEBA-7F7A1EF8D140}.Debug|Any CPU.Build.0 = Debug|x64 {A275C11D-F06E-475F-AEBA-7F7A1EF8D140}.Release|Any CPU.ActiveCfg = Release|x64 @@ -337,10 +333,6 @@ Global {E566FE94-C551-43A7-843C-5E8199CA7373}.Debug|Any CPU.Build.0 = Debug|x64 {E566FE94-C551-43A7-843C-5E8199CA7373}.Release|Any CPU.ActiveCfg = Release|x64 {E566FE94-C551-43A7-843C-5E8199CA7373}.Release|Any CPU.Build.0 = Release|x64 - {CF80B061-AF1B-4C15-A725-10C0A5D267CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CF80B061-AF1B-4C15-A725-10C0A5D267CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CF80B061-AF1B-4C15-A725-10C0A5D267CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CF80B061-AF1B-4C15-A725-10C0A5D267CC}.Release|Any CPU.Build.0 = Release|Any CPU {E2C71F2E-2940-4990-A618-E7E47C00D6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2C71F2E-2940-4990-A618-E7E47C00D6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2C71F2E-2940-4990-A618-E7E47C00D6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -389,10 +381,10 @@ Global {EFF93C65-3F45-48B6-9178-1E68ADD41974}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFF93C65-3F45-48B6-9178-1E68ADD41974}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFF93C65-3F45-48B6-9178-1E68ADD41974}.Release|Any CPU.Build.0 = Release|Any CPU - {E4E6EAB9-2C68-49AC-A212-D464F1BB483B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4E6EAB9-2C68-49AC-A212-D464F1BB483B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4E6EAB9-2C68-49AC-A212-D464F1BB483B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4E6EAB9-2C68-49AC-A212-D464F1BB483B}.Release|Any CPU.Build.0 = Release|Any CPU + {8C95ECC6-E996-4DA0-A4C8-F05373CBB0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C95ECC6-E996-4DA0-A4C8-F05373CBB0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C95ECC6-E996-4DA0-A4C8-F05373CBB0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C95ECC6-E996-4DA0-A4C8-F05373CBB0EA}.Release|Any CPU.Build.0 = Release|Any CPU {78BE5834-EC02-48C5-A83A-2693D777375A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {78BE5834-EC02-48C5-A83A-2693D777375A}.Debug|Any CPU.Build.0 = Debug|Any CPU {78BE5834-EC02-48C5-A83A-2693D777375A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -425,6 +417,10 @@ Global {B240F8BA-6C35-4C19-9035-2821CC6BFEA8}.Debug|Any CPU.Build.0 = Debug|Any CPU {B240F8BA-6C35-4C19-9035-2821CC6BFEA8}.Release|Any CPU.ActiveCfg = Release|Any CPU {B240F8BA-6C35-4C19-9035-2821CC6BFEA8}.Release|Any CPU.Build.0 = Release|Any CPU + {B240F8BA-6C35-4C19-9035-2821CC6BFFA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B240F8BA-6C35-4C19-9035-2821CC6BFFA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B240F8BA-6C35-4C19-9035-2821CC6BFFA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B240F8BA-6C35-4C19-9035-2821CC6BFFA8}.Release|Any CPU.Build.0 = Release|Any CPU {C6C34134-D80C-43CC-A477-32B4172374D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C6C34134-D80C-43CC-A477-32B4172374D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {C6C34134-D80C-43CC-A477-32B4172374D2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -449,6 +445,10 @@ Global {03F00630-3625-440D-BE43-51751FCE7806}.Debug|Any CPU.Build.0 = Debug|Any CPU {03F00630-3625-440D-BE43-51751FCE7806}.Release|Any CPU.ActiveCfg = Release|Any CPU {03F00630-3625-440D-BE43-51751FCE7806}.Release|Any CPU.Build.0 = Release|Any CPU + {03F00630-3625-440D-BE43-51751FCE7802}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03F00630-3625-440D-BE43-51751FCE7802}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03F00630-3625-440D-BE43-51751FCE7802}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03F00630-3625-440D-BE43-51751FCE7802}.Release|Any CPU.Build.0 = Release|Any CPU {FB1F02F2-3A18-49CC-A288-EC0834D90EED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FB1F02F2-3A18-49CC-A288-EC0834D90EED}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB1F02F2-3A18-49CC-A288-EC0834D90EED}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -481,14 +481,6 @@ Global {95C8865C-AB87-4998-880E-38B7F76BAC57}.Debug|Any CPU.Build.0 = Debug|Any CPU {95C8865C-AB87-4998-880E-38B7F76BAC57}.Release|Any CPU.ActiveCfg = Release|Any CPU {95C8865C-AB87-4998-880E-38B7F76BAC57}.Release|Any CPU.Build.0 = Release|Any CPU - {B02CE9AA-AB52-491A-A089-A1BDF297A800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B02CE9AA-AB52-491A-A089-A1BDF297A800}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B02CE9AA-AB52-491A-A089-A1BDF297A800}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B02CE9AA-AB52-491A-A089-A1BDF297A800}.Release|Any CPU.Build.0 = Release|Any CPU - {A204C3A9-F559-4E50-B667-F8B5D27501D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A204C3A9-F559-4E50-B667-F8B5D27501D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A204C3A9-F559-4E50-B667-F8B5D27501D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A204C3A9-F559-4E50-B667-F8B5D27501D1}.Release|Any CPU.Build.0 = Release|Any CPU {CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {CBB5E3B3-2B0F-41F3-8110-FDD9614FCE5D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -521,6 +513,18 @@ Global {DD5AEB88-DC55-43C8-81C1-2879F73C8DE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD5AEB88-DC55-43C8-81C1-2879F73C8DE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD5AEB88-DC55-43C8-81C1-2879F73C8DE0}.Release|Any CPU.Build.0 = Release|Any CPU + {011FF139-A9E6-4F10-BBC8-28BC1A38D966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {011FF139-A9E6-4F10-BBC8-28BC1A38D966}.Debug|Any CPU.Build.0 = Debug|Any CPU + {011FF139-A9E6-4F10-BBC8-28BC1A38D966}.Release|Any CPU.ActiveCfg = Release|Any CPU + {011FF139-A9E6-4F10-BBC8-28BC1A38D966}.Release|Any CPU.Build.0 = Release|Any CPU + {FD39DA19-C81E-4752-B308-43594CC201BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD39DA19-C81E-4752-B308-43594CC201BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD39DA19-C81E-4752-B308-43594CC201BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD39DA19-C81E-4752-B308-43594CC201BB}.Release|Any CPU.Build.0 = Release|Any CPU + {032B77B4-199E-4077-841E-951BAD862050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {032B77B4-199E-4077-841E-951BAD862050}.Debug|Any CPU.Build.0 = Debug|Any CPU + {032B77B4-199E-4077-841E-951BAD862050}.Release|Any CPU.ActiveCfg = Release|Any CPU + {032B77B4-199E-4077-841E-951BAD862050}.Release|Any CPU.Build.0 = Release|Any CPU {B60EC8AA-3CDF-45E4-9ACD-3246B375C027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B60EC8AA-3CDF-45E4-9ACD-3246B375C027}.Debug|Any CPU.Build.0 = Debug|Any CPU {B60EC8AA-3CDF-45E4-9ACD-3246B375C027}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -553,12 +557,10 @@ Global {F162126C-DA9C-4195-B295-CF1020DBA98A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F162126C-DA9C-4195-B295-CF1020DBA98A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F162126C-DA9C-4195-B295-CF1020DBA98A}.Release|Any CPU.Build.0 = Release|Any CPU - {BE117661-1954-42C3-8A16-3F89815F7222}.Debug|Any CPU.ActiveCfg = Debug|x64 - {BE117661-1954-42C3-8A16-3F89815F7222}.Debug|Any CPU.Build.0 = Debug|x64 - {BE117661-1954-42C3-8A16-3F89815F7222}.Debug|Any CPU.Deploy.0 = Debug|x64 - {BE117661-1954-42C3-8A16-3F89815F7222}.Release|Any CPU.ActiveCfg = Release|x64 - {BE117661-1954-42C3-8A16-3F89815F7222}.Release|Any CPU.Build.0 = Release|x64 - {BE117661-1954-42C3-8A16-3F89815F7222}.Release|Any CPU.Deploy.0 = Release|x64 + {E690EC25-3A73-408F-93B8-36990B2A9D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E690EC25-3A73-408F-93B8-36990B2A9D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E690EC25-3A73-408F-93B8-36990B2A9D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E690EC25-3A73-408F-93B8-36990B2A9D0E}.Release|Any CPU.Build.0 = Release|Any CPU {DF09A175-C790-434D-9810-A6BBB3EEC548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF09A175-C790-434D-9810-A6BBB3EEC548}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF09A175-C790-434D-9810-A6BBB3EEC548}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -575,12 +577,6 @@ Global {9AE06A6D-1335-482C-8ABF-2FE1BADF5AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9AE06A6D-1335-482C-8ABF-2FE1BADF5AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9AE06A6D-1335-482C-8ABF-2FE1BADF5AD7}.Release|Any CPU.Build.0 = Release|Any CPU - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Debug|Any CPU.ActiveCfg = Debug|x86 - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Debug|Any CPU.Build.0 = Debug|x86 - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Debug|Any CPU.Deploy.0 = Debug|x86 - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Release|Any CPU.ActiveCfg = Release|x86 - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Release|Any CPU.Build.0 = Release|x86 - {8A321DFF-99AB-4279-828A-A9BA872010FD}.Release|Any CPU.Deploy.0 = Release|x86 {437506E7-B124-4808-8AE7-8864AC9EA36A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {437506E7-B124-4808-8AE7-8864AC9EA36A}.Debug|Any CPU.Build.0 = Debug|Any CPU {437506E7-B124-4808-8AE7-8864AC9EA36A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -593,13 +589,35 @@ Global {7BC3FC79-5938-4CD7-9528-47D5DC18729E}.Debug|Any CPU.Build.0 = Debug|Any CPU {7BC3FC79-5938-4CD7-9528-47D5DC18729E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7BC3FC79-5938-4CD7-9528-47D5DC18729E}.Release|Any CPU.Build.0 = Release|Any CPU + {7C97E1C8-7FEB-4C2A-A149-7D77E15838BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C97E1C8-7FEB-4C2A-A149-7D77E15838BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C97E1C8-7FEB-4C2A-A149-7D77E15838BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C97E1C8-7FEB-4C2A-A149-7D77E15838BF}.Release|Any CPU.Build.0 = Release|Any CPU + {819C752E-1F74-4447-9214-86EF337A1DEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {819C752E-1F74-4447-9214-86EF337A1DEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {819C752E-1F74-4447-9214-86EF337A1DEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {819C752E-1F74-4447-9214-86EF337A1DEF}.Release|Any CPU.Build.0 = Release|Any CPU + {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50954AE0-E513-4CE7-AC8E-F3896CA0BEB7}.Release|Any CPU.Build.0 = Release|Any CPU + {6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C942D7C-654A-4CBE-B3A9-887A37D2FA4C}.Release|Any CPU.Build.0 = Release|Any CPU + {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87CB99AE-16D1-4D58-BEAC-72485E02B321}.Release|Any CPU.Build.0 = Release|Any CPU + {60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60E804E4-B25B-4C9A-A3E6-F83D618CFD26}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1} = {0606E530-F834-44A3-9B4E-37852A06D11B} - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3} = {0606E530-F834-44A3-9B4E-37852A06D11B} {A275C11D-F06E-475F-AEBA-7F7A1EF8D140} = {B6083619-C4E8-4D8E-95BD-217E3663134C} {E566FE94-C551-43A7-843C-5E8199CA7373} = {B6083619-C4E8-4D8E-95BD-217E3663134C} {B60EC8AA-3CDF-45E4-9ACD-3246B375C027} = {F8B6B3D8-006C-4D8F-8883-EB48D9EB417F} @@ -607,8 +625,6 @@ Global {6C106109-0131-4413-8D41-348256EADC7E} = {F8B6B3D8-006C-4D8F-8883-EB48D9EB417F} {5BBD66DA-83E0-4FCA-967E-75A87C21E89A} = {F8B6B3D8-006C-4D8F-8883-EB48D9EB417F} {04344989-84DC-4D21-BC9F-C4A23D251568} = {F8B6B3D8-006C-4D8F-8883-EB48D9EB417F} - {BE117661-1954-42C3-8A16-3F89815F7222} = {0606E530-F834-44A3-9B4E-37852A06D11B} - {8A321DFF-99AB-4279-828A-A9BA872010FD} = {0606E530-F834-44A3-9B4E-37852A06D11B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A03ACB78-6C3F-4E09-BAF1-16E41D5A3945} diff --git a/README.md b/README.md index c53413e93b..a9a2156703 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ These tasks can be done **without porting source code** to .NET. - .NET Core 3.1 and higher - .NET 5 and higher - Java SE 8 -- Windows x86/x64 -- Linux x64 +- Windows x86/x64/ARM/ARM64 +- Linux x64/ARM/ARM64 ## Documentation @@ -63,7 +63,7 @@ PM> Install-Package IKVM.Maven.Sdk ### Tools -The tools are available for download on the [Releases](https://github.com/ikvm-revived/ikvm/releases) page. +The tools are available for download on the [Releases](https://github.com/ikvmnet/ikvm/releases) page. ### Runtime Images @@ -74,7 +74,7 @@ PM> Install-Package IKVM.Image.JRE PM> Install-Package IKVM.Image.JDK ``` -A standalone JDK distributable is available for download on the [Releases](https://github.com/ikvm-revived/ikvm/releases) page. This directory structure should suffice as a `JAVA_HOME` path for standard Java applications. +A standalone JDK distributable is available for download on the [Releases](https://github.com/ikvmnet/ikvm/releases) page. This directory structure should suffice as a `JAVA_HOME` path for standard Java applications. ## Usage @@ -174,4 +174,14 @@ The `Automatic-Module-Name` is either a specified attribute of the JAR manifest, ### MavenReference -See the [ikvm-maven Readme](https://github.com/ikvm-revived/ikvm-maven#readme) for usage instructions. +See the [ikvm-maven Readme](https://github.com/ikvmnet/ikvm-maven#readme) for usage instructions. + +### Notice To Project Owners + +The IKVM project recommends that people do not redistribute FOSS Java libraries compiled with IKVM over public systems such as NuGet.org, unless you are the original owner of that software and have a compelling reason. + +Creating copies of FOSS Java libraries and publishing them to distribution mechanisms such as NuGet.org creates eco-system confusion and dependency conflicts downstream. We provide a system so that .NET users of Java libraries can reference those libraries directly out of the standard Java ecosystem mechanisms: Maven Central, etc though IKVM.Maven. Remember, very few libraries exist in a vacuum. Libraries often depend on dozens of other libraries. Two unrelated Java libraries often depend on the same underlying Java library. A complex method of dependency conflict resolution and version unification has to be involved in resolving this hierarchy for any individual downstream project. You are likely going to be introducing duplicate classes into the users of your versions, or causing your users to depend upon the wrong version of other libraries. + +There are exceptions to this advice, such as the library not being published to Maven. In that case, guidance would be to advocate that the original Java library in fact be published to Maven, or do the work yourself, as that is the appropriate place for Java libraries. + +We cannot force you to conform to this advice. But for the health of the ecosystem, we urge you to take it under consideration yourself. diff --git a/ext/helloworld-mod.jar b/ext/helloworld-mod.jar new file mode 100644 index 0000000000..95a213d79e Binary files /dev/null and b/ext/helloworld-mod.jar differ diff --git a/openjdk b/openjdk index 83077ab59f..297326379a 160000 --- a/openjdk +++ b/openjdk @@ -1 +1 @@ -Subproject commit 83077ab59f33101225d769d03209063d54cce371 +Subproject commit 297326379ab44b57d59e25ed3fbb56b22cdc53e5 diff --git a/openjdk.props b/openjdk.props index b837105021..edb95f3064 100644 --- a/openjdk.props +++ b/openjdk.props @@ -1,8 +1,8 @@  - $(MSBuildThisFileDirectory)openjdk - OpenJDK 8u45 b37 - 1.8.0_45-b37 + $(MSBuildThisFileDirectory)openjdk\ + OpenJDK 8u60 b32 + 1.8.0_60-b32 Oracle 1.8.0 Oracle Corporation @@ -17,416 +17,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -504,13 +95,7 @@ - - - - - - - + @@ -616,92 +201,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -727,142 +227,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -2275,6007 +1640,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -9737,902 +3107,48 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -10654,16 +3170,7 @@ - - - - - - - - - - + @@ -10750,49 +3257,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -10810,16 +3275,7 @@ - - - - - - - - - - + @@ -10829,25 +3285,7 @@ - - - - - - - - - - - - - - - - - - - + @@ -10863,17 +3301,7 @@ - - - - - - - - - - - + @@ -10882,12 +3310,7 @@ - - - - - - + @@ -10897,52 +3320,12 @@ - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -10958,1698 +3341,18 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + @@ -13029,6 +3732,7 @@ + @@ -13164,119 +3868,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -13462,558 +4080,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + @@ -14612,9 +4686,7 @@ - - - + @@ -14622,24 +4694,13 @@ - - - - - - - - - - - - - - - + + + + @@ -14778,502 +4839,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -15355,522 +4921,12 @@ - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -15935,6 +4991,7 @@ + diff --git a/src/IKVM.ByteCode.Tests/0.class b/src/IKVM.ByteCode.Tests/0.class new file mode 100644 index 0000000000..b5b6c8f2b5 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/0.class differ diff --git a/src/IKVM.ByteCode.Tests/ClassReaderTests.cs b/src/IKVM.ByteCode.Tests/ClassReaderTests.cs new file mode 100644 index 0000000000..6b8f8b2631 --- /dev/null +++ b/src/IKVM.ByteCode.Tests/ClassReaderTests.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using FluentAssertions; + +using IKVM.ByteCode.Parsing; +using IKVM.ByteCode.Reading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.ByteCode.Tests +{ + + [TestClass] + public class ClassReaderTests + { + + [TestMethod] + public async Task CanLoadClassAsync() + { + using var file = File.OpenRead(Path.Combine(Path.GetDirectoryName(typeof(ClassReaderTests).Assembly.Location), "0.class")); + var clazz = await ClassReader.ReadAsync(file); + clazz.Should().NotBeNull(); + clazz.This.Name.Value.Should().Be("0"); + clazz.Constants.ToList(); + clazz.Interfaces.ToList(); + clazz.Fields.Should().HaveCount(0); + clazz.Fields.ToList(); + clazz.Methods.Should().HaveCount(2); + clazz.Methods.ToList(); + + clazz.Methods[0].Attributes.Code.Code.Should().NotBeNull(); + clazz.Methods[1].Attributes.Code.Code.Should().NotBeNull(); + } + + [TestMethod] + public void CanLoadClass() + { + using var file = File.OpenRead(Path.Combine(Path.GetDirectoryName(typeof(ClassReaderTests).Assembly.Location), "0.class")); + var clazz = ClassReader.Read(file); + clazz.Should().NotBeNull(); + clazz.This.Name.Value.Should().Be("0"); + clazz.Constants.ToList(); + clazz.Interfaces.ToList(); + clazz.Fields.Should().HaveCount(0); + clazz.Fields.ToList(); + clazz.Methods.Should().HaveCount(2); + clazz.Methods.ToList(); + + clazz.Methods[0].Attributes.Code.Code.Should().NotBeNull(); + clazz.Methods[1].Attributes.Code.Code.Should().NotBeNull(); + } + + [TestMethod] + public void CanLoadValidClassFiles() + { + var d = Path.Combine(Path.GetDirectoryName(typeof(ClassReaderTests).Assembly.Location), "resources"); + var l = Directory.GetFiles(d, "*.class", SearchOption.AllDirectories); + + foreach (var i in l) + { + using var f = File.OpenRead(i); + var c = ClassReader.Read(f); + c.This.Should().NotBeNull(); + c.Constants.ToList(); + + foreach (var constant in c.Constants) + TestConstant(constant); + + c.Interfaces.ToList(); + c.Interfaces.Should().OnlyHaveUniqueItems(); + c.Fields.ToList(); + c.Fields.Should().OnlyHaveUniqueItems(); + c.Methods.ToList(); + c.Methods.Should().OnlyHaveUniqueItems(); + + foreach (var iface in c.Interfaces) + iface.Class.Name.Value.Should().NotBeNull(); + + foreach (var field in c.Fields) + { + field.Should().NotBeNull(); + field.Name.Value.Should().NotBeNull(); + field.Descriptor.Value.Should().NotBeNull(); + field.Attributes.ToList(); + + foreach (var attribute in field.Attributes) + TestAttribute(attribute); + } + + foreach (var method in c.Methods) + { + method.Name.Should().NotBeNull(); + method.Descriptor.Should().NotBeNull(); + method.Attributes.ToList(); + + foreach (var attribute in method.Attributes) + TestAttribute(attribute); + } + + c.Attributes.ToList(); + foreach (var attribute in c.Attributes) + TestAttribute(attribute); + } + } + + void TestConstant(IConstantReader constant) + { + if (constant is Utf8ConstantReader utf8) + TestConstant(utf8); + if (constant is IntegerConstantReader integer) + TestConstant(integer); + if (constant is MethodHandleConstantReader methodHandle) + TestConstant(methodHandle); + } + + void TestConstant(Utf8ConstantReader utf8) + { + utf8.Value.Should().NotBeNull(); + } + + void TestConstant(IntegerConstantReader integer) + { + integer.Value.GetType().Should().Be(typeof(int)); + } + + void TestConstant(MethodHandleConstantReader methodHandle) + { + if (methodHandle.ReferenceKind is ReferenceKind.GetField or ReferenceKind.GetStatic or ReferenceKind.PutField or ReferenceKind.PutStatic) + methodHandle.Reference.Should().BeOfType(); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeVirtual or ReferenceKind.NewInvokeSpecial) + methodHandle.Reference.Should().BeOfType(); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeStatic or ReferenceKind.InvokeSpecial && methodHandle.DeclaringClass.Version < new ClassFormatVersion(52, 0)) + methodHandle.Reference.Should().BeOfType(); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeStatic or ReferenceKind.InvokeSpecial && methodHandle.DeclaringClass.Version >= new ClassFormatVersion(52, 0)) + methodHandle.Reference.Should().Match(i => i is MethodrefConstantReader || i is InterfaceMethodrefConstantReader); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeInterface) + methodHandle.Reference.Should().BeOfType(); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeVirtual or ReferenceKind.InvokeStatic or ReferenceKind.InvokeSpecial or ReferenceKind.InvokeInterface && methodHandle.Reference is MethodrefConstantReader methodRef) + methodRef.NameAndType.Name.Value.Should().NotBe("").And.NotBe(""); + if (methodHandle.ReferenceKind is ReferenceKind.InvokeVirtual or ReferenceKind.InvokeStatic or ReferenceKind.InvokeSpecial or ReferenceKind.InvokeInterface && methodHandle.Reference is InterfaceMethodrefConstantReader interfaceMethodRef) + interfaceMethodRef.Class.Name.Value.Should().NotBe("").And.NotBe(""); + } + + void TestAttribute(AttributeReader attribute) + { + if (attribute is RuntimeVisibleAnnotationsAttributeReader runtimeVisibleAnnotationsAttributeReader) + TestAttribute(runtimeVisibleAnnotationsAttributeReader); + if (attribute is RuntimeInvisibleAnnotationsAttributeReader runtimeInvisibleAnnotationsAttributeReader) + TestAttribute(runtimeInvisibleAnnotationsAttributeReader); + if (attribute is RuntimeVisibleTypeAnnotationsAttributeReader runtimeVisibleTypeAnnotationsAttributeReader) + TestAttribute(runtimeVisibleTypeAnnotationsAttributeReader); + if (attribute is RuntimeInvisibleTypeAnnotationsAttributeReader runtimeInvisibleTypeAnnotationsAttributeReader) + TestAttribute(runtimeInvisibleTypeAnnotationsAttributeReader); + } + + void TestAttribute(RuntimeVisibleAnnotationsAttributeReader attribute) + { + attribute.Info.Name.Value.Should().NotBeEmpty(); + TestAnnotations(attribute.Annotations); + } + + void TestAttribute(RuntimeInvisibleAnnotationsAttributeReader attribute) + { + attribute.Info.Name.Value.Should().NotBeEmpty(); + TestAnnotations(attribute.Annotations); + } + + void TestAttribute(RuntimeVisibleTypeAnnotationsAttributeReader attribute) + { + attribute.Info.Name.Value.Should().NotBeEmpty(); + TestAnnotations(attribute.Annotations); + } + + void TestAttribute(RuntimeInvisibleTypeAnnotationsAttributeReader attribute) + { + attribute.Info.Name.Value.Should().NotBeEmpty(); + TestAnnotations(attribute.Annotations); + } + + void TestAnnotations(IReadOnlyList annotations) + { + foreach (var annotation in annotations) + TestAnnotation(annotation); + } + + void TestAnnotations(IReadOnlyList annotations) + { + foreach (var annotation in annotations) + TestAnnotation(annotation); + } + + void TestAnnotation(AnnotationReader annotation) + { + annotation.Type.Value.Should().NotBeEmpty(); + TestElementValuePair(annotation.Elements); + } + + void TestAnnotation(TypeAnnotationReader annotation) + { + annotation.Type.Value.Should().NotBeEmpty(); + TestElementValuePair(annotation.Elements); + } + + void TestElementValuePair(ElementValueKeyReaderCollection elements) + { + elements.Count.Should().BeLessThan(256); + + foreach (var element in elements) + TestElement(element.Key, element.Value); + } + + void TestElement(string name, ElementValueReader value) + { + name.Should().NotBeEmpty(); + value.Should().NotBeNull(); + + TestElementValue(value); + } + + void TestElementValue(ElementValueReader value) + { + if (value is ElementValueConstantReader elementValueConstantReader) + TestElementValue(elementValueConstantReader); + if (value is ElementValueAnnotationReader elementAnnotationValueReader) + TestElementValue(elementAnnotationValueReader); + if (value is ElementValueArrayReader elementArrayValueReader) + TestElementValue(elementArrayValueReader); + if (value is ElementValueClassReader elementClassInfoValueReader) + TestElementValue(elementClassInfoValueReader); + } + + void TestElementValue(ElementValueConstantReader elementValueConstantReader) + { + Enum.GetName(typeof(ElementValueTag), elementValueConstantReader.Tag).Should().NotBeNullOrEmpty(); + elementValueConstantReader.Value.Should().NotBeNull(); + TestConstant(elementValueConstantReader.Value); + } + + void TestElementValue(ElementValueClassReader elementClassInfoValueReader) + { + elementClassInfoValueReader.Class.Value.Should().NotBeEmpty(); + } + + void TestElementValue(ElementValueAnnotationReader elementAnnotationValueReader) + { + TestAnnotation(elementAnnotationValueReader.Annotation); + } + + void TestElementValue(ElementValueArrayReader elementArrayValueReader) + { + foreach (var value in elementArrayValueReader.Values) + TestElementValue(value); + } + + } + +} diff --git a/src/IKVM.ByteCode.Tests/ClassVersionTests.cs b/src/IKVM.ByteCode.Tests/ClassVersionTests.cs new file mode 100644 index 0000000000..4c8e2066f6 --- /dev/null +++ b/src/IKVM.ByteCode.Tests/ClassVersionTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.ByteCode.Tests +{ + + [TestClass] + public class ClassVersionTests + { + + [TestMethod] + public void CompareToShouldBeZeroForEqual() + { + new ClassFormatVersion(1, 0).CompareTo(new ClassFormatVersion(1, 0)).Should().Be(0); + } + + [TestMethod] + public void CompareToShouldBeNegativeOneForGreaterMajor() + { + new ClassFormatVersion(1, 0).CompareTo(new ClassFormatVersion(2, 0)).Should().Be(-1); + } + + [TestMethod] + public void CompareToShouldBeNegativeOneForGreaterMinor() + { + new ClassFormatVersion(1, 0).CompareTo(new ClassFormatVersion(1, 1)).Should().Be(-1); + } + + [TestMethod] + public void CompareToShouldBeOneForGreaterMajor() + { + new ClassFormatVersion(2, 0).CompareTo(new ClassFormatVersion(1, 0)).Should().Be(1); + } + + [TestMethod] + public void CompareToShouldBeOneForGreaterMinor() + { + new ClassFormatVersion(1, 1).CompareTo(new ClassFormatVersion(1, 0)).Should().Be(1); + } + + [TestMethod] + public void ImplicitOperatorsShouldReturnCorrectValues() + { + (new ClassFormatVersion(1, 0) > new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(1, 0) < new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(1, 0) >= new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(1, 0) <= new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(1, 0) == new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(1, 0) != new ClassFormatVersion(1, 0)).Should().BeFalse(); + + (new ClassFormatVersion(1, 1) > new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(1, 1) < new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(1, 1) >= new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(1, 1) <= new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(1, 1) == new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(1, 1) != new ClassFormatVersion(1, 0)).Should().BeTrue(); + + (new ClassFormatVersion(2, 0) > new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(2, 0) < new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(2, 0) >= new ClassFormatVersion(1, 0)).Should().BeTrue(); + (new ClassFormatVersion(2, 0) <= new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(2, 0) == new ClassFormatVersion(1, 0)).Should().BeFalse(); + (new ClassFormatVersion(2, 0) != new ClassFormatVersion(1, 0)).Should().BeTrue(); + } + + } + +} diff --git a/src/IKVM.ByteCode.Tests/IKVM.ByteCode.Tests.csproj b/src/IKVM.ByteCode.Tests/IKVM.ByteCode.Tests.csproj new file mode 100644 index 0000000000..ac7a2377c0 --- /dev/null +++ b/src/IKVM.ByteCode.Tests/IKVM.ByteCode.Tests.csproj @@ -0,0 +1,27 @@ + + + + net461;netcoreapp3.1;net6.0 + true + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/IKVM.ByteCode.Tests/Parsing/TypeAnnotationRecordTests.cs b/src/IKVM.ByteCode.Tests/Parsing/TypeAnnotationRecordTests.cs new file mode 100644 index 0000000000..d9e21eb130 --- /dev/null +++ b/src/IKVM.ByteCode.Tests/Parsing/TypeAnnotationRecordTests.cs @@ -0,0 +1,45 @@ +using System; + +using FluentAssertions; + +using IKVM.ByteCode.Parsing; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.ByteCode.Tests.Parsing +{ + + [TestClass] + public class TypeAnnotationRecordTests + { + + [TestMethod] + public void CanRoundTripTypeAnnotation() + { + var a = new TypeAnnotationRecord( + TypeAnnotationTargetType.Field, + new TypeAnnotationEmptyTargetRecord(), + new TypePathRecord(new TypePathItemRecord(TypePathKind.ArrayType, 0)), + 1, + new ElementValuePairRecord(2, new ElementValueRecord(ElementValueTag.Integer, new ElementValueConstantValueRecord(3)))); + + var b1 = new byte[a.GetSize()]; + var w1 = new ClassFormatWriter(b1); + if (a.TryWrite(ref w1) == false) + throw new Exception(); + + var r = new ClassFormatReader(b1); + if (TypeAnnotationRecord.TryReadTypeAnnotation(ref r, out var b) == false) + throw new Exception(); + + var b2 = new byte[b.GetSize()]; + var w2 = new ClassFormatWriter(b2); + if (b.TryWrite(ref w2) == false) + throw new Exception(); + + b1.Should().BeEquivalentTo(b2); + } + + } + +} diff --git a/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.class b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.class new file mode 100644 index 0000000000..e2af1771c0 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.class differ diff --git a/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.cs b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.cs new file mode 100644 index 0000000000..236b0f24b4 --- /dev/null +++ b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.Linq; + +using FluentAssertions; + +using IKVM.ByteCode.Reading; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.ByteCode.Tests.Reading +{ + + [TestClass] + public class ConstantReaderTests + { + + ClassReader ReadClass() + { + var f = File.OpenRead(Path.Combine(Path.GetDirectoryName(typeof(ConstantReaderTests).Assembly.Location), "Reading", "ConstantReaderTests.class")); + var c = ClassReader.Read(f); + return c; + } + + [TestMethod] + public void CanReadIntegerConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Value == 394892); + } + + [TestMethod] + public void CanReadLongConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Value == 34182132); + } + + [TestMethod] + public void CanReadFloatConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Value == 221.03f); + } + + [TestMethod] + public void CanReadDoubleConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Value == 2212133.1d); + } + + [TestMethod] + public void CanReadStringConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Value == "STRING"); + } + + [TestMethod] + public void CanReadClassConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Name.Value == "java/lang/Object"); + } + + [TestMethod] + public void CanReadMethodrefConstant() + { + var c = ReadClass(); + c.Constants.OfType().Should().Contain(i => i.Class.Name.Value == "java/lang/Object" && i.NameAndType.Name.Value == "" && i.NameAndType.Type.Value == "()V"); + } + + } + +} diff --git a/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.java b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.java new file mode 100644 index 0000000000..e0d8907c7a --- /dev/null +++ b/src/IKVM.ByteCode.Tests/Reading/ConstantReaderTests.java @@ -0,0 +1,9 @@ +final class ConstantReaderTests { + + static int i = 394892; + static long j = 34182132; + static float f = 221.03f; + static double d = 2212133.1; + static String o = "STRING"; + +} diff --git a/src/IKVM.ByteCode.Tests/Text/MUTF8EncodingTests.cs b/src/IKVM.ByteCode.Tests/Text/MUTF8EncodingTests.cs new file mode 100644 index 0000000000..ea79075aab --- /dev/null +++ b/src/IKVM.ByteCode.Tests/Text/MUTF8EncodingTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Linq; + +using FluentAssertions; + +using IKVM.ByteCode.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.ByteCode.Tests.Text +{ + + [TestClass] + public class MUTF8EncodingTests + { + + string asciiChars = new string(Enumerable.Range(32, 126 - 32).Select(i => (char)i).ToArray()); + + [TestMethod] + public void CanRoundTripASCIICharactersForJDK_1_4() + { + var e = MUTF8Encoding.GetMUTF8(48); + var b = e.GetBytes(asciiChars); + var c = e.GetString(b); + c.Should().Be(asciiChars); + } + + [TestMethod] + public void CanRoundTripASCIICharactersForJDK_1_0() + { + var e = MUTF8Encoding.GetMUTF8(46); + var b = e.GetBytes(asciiChars); + var c = e.GetString(b); + c.Should().Be(asciiChars); + } + + [TestMethod] + public void CanEncodeNullByte() + { + var l = MUTF8Encoding.GetMUTF8(48).GetByteCount("\0"); + l.Should().Be(2); + var b = MUTF8Encoding.GetMUTF8(48).GetBytes("\0"); + b.Should().HaveCount(2); + b[0].Should().Be(0b11000000); + b[1].Should().Be(0b10000000); + } + + [TestMethod] + public void CanDecodeNullByte() + { + var s = MUTF8Encoding.GetMUTF8(48).GetChars(new byte[] { 0b11000000, 0b10000000 }); + s.Should().HaveCount(1); + s[0].Should().Be('\0'); + } + + [TestMethod] + public unsafe void CanFindNullByte() + { + fixed (byte* ptr = new byte[] { 0x01, 0x00 }) + MUTF8Encoding.GetMUTF8(48).IndexOfNull(ptr).Should().Be(1); + fixed (byte* ptr = new byte[] { 0x00, 0x00 }) + MUTF8Encoding.GetMUTF8(48).IndexOfNull(ptr).Should().Be(0); + fixed (byte* ptr = new byte[] { 0x01, 0x01, 0x00 }) + MUTF8Encoding.GetMUTF8(48).IndexOfNull(ptr).Should().Be(2); + } + + [TestMethod] + public void CanEncodeNull() + { + MUTF8Encoding.GetMUTF8(48).GetBytes("\0").Should().HaveCount(2); + MUTF8Encoding.GetMUTF8(48).GetBytes("a\0").Should().HaveCount(3); + MUTF8Encoding.GetMUTF8(48).GetBytes("a\0a").Should().HaveCount(4); + MUTF8Encoding.GetMUTF8(48).GetBytes("\0\0").Should().HaveCount(4); + MUTF8Encoding.GetMUTF8(48).GetBytes("a\0\0").Should().HaveCount(5); + MUTF8Encoding.GetMUTF8(48).GetBytes("a\0\0a").Should().HaveCount(6); + } + + [TestMethod] + public void CanHandleEmptyString() + { + MUTF8Encoding.GetMUTF8(48).GetBytes("").Should().BeEmpty(); + MUTF8Encoding.GetMUTF8(48).GetString(Array.Empty()).Should().Be(""); + } + + } + +} diff --git a/src/IKVM.ByteCode.Tests/resources/CharSequence.class b/src/IKVM.ByteCode.Tests/resources/CharSequence.class new file mode 100644 index 0000000000..29734d2c09 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/CharSequence.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/ColorEditor.class b/src/IKVM.ByteCode.Tests/resources/ColorEditor.class new file mode 100644 index 0000000000..30132d7b9b Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/ColorEditor.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/Databinding$Builder.class b/src/IKVM.ByteCode.Tests/resources/Databinding$Builder.class new file mode 100644 index 0000000000..8e35c60e98 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/Databinding$Builder.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/Databinding.class b/src/IKVM.ByteCode.Tests/resources/Databinding.class new file mode 100644 index 0000000000..8d71ac405f Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/Databinding.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/DatabindingFactory.class b/src/IKVM.ByteCode.Tests/resources/DatabindingFactory.class new file mode 100644 index 0000000000..2af6a40b1f Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/DatabindingFactory.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/DatabindingMode.class b/src/IKVM.ByteCode.Tests/resources/DatabindingMode.class new file mode 100644 index 0000000000..a7778f88f4 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/DatabindingMode.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature$Builder.class b/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature$Builder.class new file mode 100644 index 0000000000..8d486c4107 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature$Builder.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature.class b/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature.class new file mode 100644 index 0000000000..1d75b37720 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/DatabindingModeFeature.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/DefaultPackage.class b/src/IKVM.ByteCode.Tests/resources/DefaultPackage.class new file mode 100644 index 0000000000..0e6cce8c66 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/DefaultPackage.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/Serializable.class b/src/IKVM.ByteCode.Tests/resources/Serializable.class new file mode 100644 index 0000000000..bde4e91583 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/Serializable.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/ThreadDeath.class b/src/IKVM.ByteCode.Tests/resources/ThreadDeath.class new file mode 100644 index 0000000000..9b59f5237f Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/ThreadDeath.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/ICA.class b/src/IKVM.ByteCode.Tests/resources/annotations/ICA.class new file mode 100644 index 0000000000..bb479f5bc1 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/ICA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/IFA.class b/src/IKVM.ByteCode.Tests/resources/annotations/IFA.class new file mode 100644 index 0000000000..c3cd37f5be Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/IFA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/IMA.class b/src/IKVM.ByteCode.Tests/resources/annotations/IMA.class new file mode 100644 index 0000000000..cee7bd63f2 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/IMA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/IPA.class b/src/IKVM.ByteCode.Tests/resources/annotations/IPA.class new file mode 100644 index 0000000000..816a37cb94 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/IPA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/ITA.class b/src/IKVM.ByteCode.Tests/resources/annotations/ITA.class new file mode 100644 index 0000000000..c05ccb2d97 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/ITA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/ITPA.class b/src/IKVM.ByteCode.Tests/resources/annotations/ITPA.class new file mode 100644 index 0000000000..3734e57f70 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/ITPA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/ITUA.class b/src/IKVM.ByteCode.Tests/resources/annotations/ITUA.class new file mode 100644 index 0000000000..374440360c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/ITUA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/IVA.class b/src/IKVM.ByteCode.Tests/resources/annotations/IVA.class new file mode 100644 index 0000000000..e5de15cd48 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/IVA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VCA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VCA.class new file mode 100644 index 0000000000..9cdf532bfb Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VCA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VFA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VFA.class new file mode 100644 index 0000000000..59ffcf8d0d Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VFA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VMA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VMA.class new file mode 100644 index 0000000000..82c909bebf Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VMA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VPA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VPA.class new file mode 100644 index 0000000000..d9e6e5ba1c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VPA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VTA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VTA.class new file mode 100644 index 0000000000..84c0a95772 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VTA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VTPA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VTPA.class new file mode 100644 index 0000000000..2d0b3b9281 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VTPA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VTUA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VTUA.class new file mode 100644 index 0000000000..d532458a2f Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VTUA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/annotations/VVA.class b/src/IKVM.ByteCode.Tests/resources/annotations/VVA.class new file mode 100644 index 0000000000..378a3e73f8 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/annotations/VVA.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidBytecodeOffset.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidBytecodeOffset.clazz new file mode 100644 index 0000000000..bc5286cc2a Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidBytecodeOffset.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidClassVersion.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidClassVersion.clazz new file mode 100644 index 0000000000..1ef8a0b116 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidClassVersion.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCodeLength.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCodeLength.clazz new file mode 100644 index 0000000000..8378e4f4ec Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCodeLength.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolIndex.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolIndex.clazz new file mode 100644 index 0000000000..52360e85ab Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolIndex.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolReference.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolReference.clazz new file mode 100644 index 0000000000..f0a7874f3e Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidConstantPoolReference.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCpInfoTag.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCpInfoTag.clazz new file mode 100644 index 0000000000..650e8ac121 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidCpInfoTag.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidElementValue.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidElementValue.clazz new file mode 100644 index 0000000000..c6cf72e044 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidElementValue.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidInsnTypeAnnotationTargetType.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidInsnTypeAnnotationTargetType.clazz new file mode 100644 index 0000000000..5cbb5ac5c7 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidInsnTypeAnnotationTargetType.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidOpcode.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidOpcode.clazz new file mode 100644 index 0000000000..3c94fceb23 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidOpcode.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidSourceDebugExtension.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidSourceDebugExtension.clazz new file mode 100644 index 0000000000..f7df0e823c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidSourceDebugExtension.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidStackMapFrameType.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidStackMapFrameType.clazz new file mode 100644 index 0000000000..3adc7b76d7 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidStackMapFrameType.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidTypeAnnotationTargetType.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidTypeAnnotationTargetType.clazz new file mode 100644 index 0000000000..8859aa382c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidTypeAnnotationTargetType.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidVerificationTypeInfo.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidVerificationTypeInfo.clazz new file mode 100644 index 0000000000..bea541c093 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidVerificationTypeInfo.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/invalid/InvalidWideOpcode.clazz b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidWideOpcode.clazz new file mode 100644 index 0000000000..59d01081b7 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/invalid/InvalidWideOpcode.clazz differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk11/AllInstructions.class b/src/IKVM.ByteCode.Tests/resources/jdk11/AllInstructions.class new file mode 100644 index 0000000000..69aa44471c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk11/AllInstructions.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures$Nested.class b/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures$Nested.class new file mode 100644 index 0000000000..9d31e7bed0 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures$Nested.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures.class new file mode 100644 index 0000000000..3aa1138bf4 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk11/AllStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$EmptyRecord.class b/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$EmptyRecord.class new file mode 100644 index 0000000000..e2f8cb3ce4 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$EmptyRecord.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$RecordSubType.class b/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$RecordSubType.class new file mode 100644 index 0000000000..61d0a18030 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk14/AllStructures$RecordSubType.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk15/AllStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk15/AllStructures.class new file mode 100644 index 0000000000..851fc92f30 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk15/AllStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/AllInstructions.class b/src/IKVM.ByteCode.Tests/resources/jdk3/AllInstructions.class new file mode 100644 index 0000000000..283d41a0d0 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/AllInstructions.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$1.class b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$1.class new file mode 100644 index 0000000000..c21668ed12 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$1.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$InnerClass.class b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$InnerClass.class new file mode 100644 index 0000000000..930cad3270 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures$InnerClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures.class new file mode 100644 index 0000000000..c5b7ba2c1c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/AllStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/ArtificialStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk3/ArtificialStructures.class new file mode 100644 index 0000000000..0ba4240b6d Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/ArtificialStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk3/LargeMethod.class b/src/IKVM.ByteCode.Tests/resources/jdk3/LargeMethod.class new file mode 100644 index 0000000000..d2547710c4 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk3/LargeMethod.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllInstructions.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllInstructions.class new file mode 100644 index 0000000000..f3d08c37c7 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllInstructions.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$1LocalClass.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$1LocalClass.class new file mode 100644 index 0000000000..b6ac40da4c Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$1LocalClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$EnumClass.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$EnumClass.class new file mode 100644 index 0000000000..23540f193b Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$EnumClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$GenericInnerClass.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$GenericInnerClass.class new file mode 100644 index 0000000000..5291fe7892 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$GenericInnerClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InnerClass.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InnerClass.class new file mode 100644 index 0000000000..baeb6c4ad3 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InnerClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InvisibleAnnotation.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InvisibleAnnotation.class new file mode 100644 index 0000000000..485df516a7 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures$InvisibleAnnotation.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures.class new file mode 100644 index 0000000000..a7de553e83 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk5/AllStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/AllFrames.class b/src/IKVM.ByteCode.Tests/resources/jdk8/AllFrames.class new file mode 100644 index 0000000000..90d79b8c5b Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/AllFrames.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/AllInstructions.class b/src/IKVM.ByteCode.Tests/resources/jdk8/AllInstructions.class new file mode 100644 index 0000000000..ecd224fb08 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/AllInstructions.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$1.class b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$1.class new file mode 100644 index 0000000000..24a5d7e5dc Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$1.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$InnerClass.class b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$InnerClass.class new file mode 100644 index 0000000000..2e05aa5dfa Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures$InnerClass.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures.class b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures.class new file mode 100644 index 0000000000..3069f601b5 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/AllStructures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/Artificial$()$Structures.class b/src/IKVM.ByteCode.Tests/resources/jdk8/Artificial$()$Structures.class new file mode 100644 index 0000000000..1cc843ec6b Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/Artificial$()$Structures.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk8/LargeMethod.class b/src/IKVM.ByteCode.Tests/resources/jdk8/LargeMethod.class new file mode 100644 index 0000000000..bace5c0c89 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk8/LargeMethod.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk9/module-info.class b/src/IKVM.ByteCode.Tests/resources/jdk9/module-info.class new file mode 100644 index 0000000000..e7418e67bd Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk9/module-info.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/A.class b/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/A.class new file mode 100644 index 0000000000..d33ea1d335 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/A.class differ diff --git a/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/internal/AImpl.class b/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/internal/AImpl.class new file mode 100644 index 0000000000..51a374f6c8 Binary files /dev/null and b/src/IKVM.ByteCode.Tests/resources/jdk9/pkg/internal/AImpl.class differ diff --git a/src/IKVM.ByteCode/AccessFlag.cs b/src/IKVM.ByteCode/AccessFlag.cs new file mode 100644 index 0000000000..26697481cd --- /dev/null +++ b/src/IKVM.ByteCode/AccessFlag.cs @@ -0,0 +1,23 @@ +using System; + +namespace IKVM.ByteCode +{ + + [Flags] + internal enum AccessFlag : ushort + { + + ACC_PUBLIC = 0x0001, + ACC_FINAL = 0x0010, + ACC_SUPER = 0x0020, + ACC_INTERFACE = 0x0200, + ACC_ABSTRACT = 0x0400, + ACC_SYNTHETIC = 0x1000, + ACC_ANNOTATION = 0x2000, + ACC_ENUM = 0x4000, + ACC_MODULE = 0x8000, + + } + + +} diff --git a/src/IKVM.ByteCode/Buffers/RawBitConverter.cs b/src/IKVM.ByteCode/Buffers/RawBitConverter.cs new file mode 100644 index 0000000000..05699b8f5b --- /dev/null +++ b/src/IKVM.ByteCode/Buffers/RawBitConverter.cs @@ -0,0 +1,80 @@ +using System.Runtime.CompilerServices; + +#if NETCOREAPP3_1 +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +#endif + +namespace IKVM.ByteCode.Buffers +{ + + static class RawBitConverter + { + +#if NETFRAMEWORK || NETCOREAPP3_1 + + /// + /// Converts the specified 32-bit signed integer to a single-precision floating point number. + /// + /// The number to convert. + /// A single-precision floating point number whose bits are identical to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Int32BitsToSingle(int value) + { +#if NETCOREAPP3_1 + // Workaround for https://github.com/dotnet/runtime/issues/11413 + if (Sse2.IsSupported) + { + Vector128 vec = Vector128.CreateScalarUnsafe(value).AsSingle(); + return vec.ToScalar(); + } +#endif + + return *((float*)&value); + } + + /// + /// Converts the specified 32-bit signed integer to a single-precision floating point number. + /// + /// The number to convert. + /// A single-precision floating point number whose bits are identical to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Int64BitsToDouble(long value) + { +#if NETCOREAPP3_1 + // Workaround for https://github.com/dotnet/runtime/issues/11413 + if (Sse2.X64.IsSupported) + { + Vector128 vec = Vector128.CreateScalarUnsafe(value).AsDouble(); + return vec.ToScalar(); + } +#endif + + return *((double*)&value); + } + +#endif + +#if NETFRAMEWORK || NETCOREAPP3_1 + + /// + /// Converts the specified 32-bit unsigned integer to a single-precision floating point number. + /// + /// The number to convert. + /// A single-precision floating point number whose bits are identical to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float UInt32BitsToSingle(uint value) => Int32BitsToSingle((int)value); + + /// + /// Converts the specified 32-bit unsigned integer to a single-precision floating point number. + /// + /// The number to convert. + /// A single-precision floating point number whose bits are identical to . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double UInt64BitsToDouble(ulong value) => Int64BitsToDouble((long)value); + +#endif + + } + +} diff --git a/src/IKVM.ByteCode/Buffers/SequenceReader.cs b/src/IKVM.ByteCode/Buffers/SequenceReader.cs new file mode 100644 index 0000000000..c93fd648a8 --- /dev/null +++ b/src/IKVM.ByteCode/Buffers/SequenceReader.cs @@ -0,0 +1,505 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + * See the LICENSE file in the project root for more information. */ + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace IKVM.ByteCode.Buffers +{ + +#if NETFRAMEWORK + + public ref partial struct SequenceReader + where T : unmanaged, IEquatable + { + + /// + /// A value indicating whether we're using (as opposed to . + /// + private bool usingSequence; + + /// + /// Backing for the entire sequence when we're not using . + /// + private ReadOnlySequence sequence; + + /// + /// The position at the start of the . + /// + private SequencePosition currentPosition; + + /// + /// The position at the end of the . + /// + private SequencePosition nextPosition; + + /// + /// Backing for the entire sequence when we're not using . + /// + private ReadOnlyMemory memory; + + /// + /// A value indicating whether there is unread data remaining. + /// + private bool moreData; + + /// + /// The total number of elements in the sequence. + /// + private long length; + + /// + /// Initializes a new instance of the struct + /// over the given . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(ReadOnlySequence sequence) + { + this.usingSequence = true; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this.sequence = sequence; + this.memory = default; + this.currentPosition = sequence.Start; + this.length = -1; + + ReadOnlySpan first = sequence.First.Span; + this.nextPosition = sequence.GetPosition(first.Length); + this.CurrentSpan = first; + this.moreData = first.Length > 0; + + if (!this.moreData && !sequence.IsSingleSegment) + { + this.moreData = true; + this.GetNextSpan(); + } + } + + /// + /// Initializes a new instance of the struct + /// over the given . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(ReadOnlyMemory memory) + { + this.usingSequence = false; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this.memory = memory; + this.CurrentSpan = memory.Span; + this.length = memory.Length; + this.moreData = memory.Length > 0; + + this.currentPosition = default; + this.nextPosition = default; + this.sequence = default; + } + + /// + /// Gets a value indicating whether there is no more data in the . + /// + public bool End => !this.moreData; + + /// + /// Gets the underlying for the reader. + /// + public ReadOnlySequence Sequence + { + get + { + if (this.sequence.IsEmpty && !this.memory.IsEmpty) + { + // We're in memory mode (instead of sequence mode). + // Lazily fill in the sequence data. + this.sequence = new ReadOnlySequence(this.memory); + this.currentPosition = this.sequence.Start; + this.nextPosition = this.sequence.End; + } + + return this.sequence; + } + } + + /// + /// Gets the current position in the . + /// + public SequencePosition Position + => this.Sequence.GetPosition(this.CurrentSpanIndex, this.currentPosition); + + /// + /// Gets the current segment in the as a span. + /// + public ReadOnlySpan CurrentSpan { get; private set; } + + /// + /// Gets the index in the . + /// + public int CurrentSpanIndex { get; private set; } + + /// + /// Gets the unread portion of the . + /// + public ReadOnlySpan UnreadSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.CurrentSpan.Slice(this.CurrentSpanIndex); + } + + /// + /// Gets the total number of 's processed by the reader. + /// + public long Consumed { get; private set; } + + /// + /// Gets remaining 's in the reader's . + /// + public long Remaining => this.Length - this.Consumed; + + /// + /// Gets count of in the reader's . + /// + public long Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (this.length < 0) + { + // Cache the length + this.length = this.Sequence.Length; + } + + return this.length; + } + } + + /// + /// Peeks at the next value without advancing the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek(out T value) + { + if (this.moreData) + { + value = this.CurrentSpan[this.CurrentSpanIndex]; + return true; + } + else + { + value = default; + return false; + } + } + + /// + /// Read the next value and advance the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryRead(out T value) + { + if (this.End) + { + value = default; + return false; + } + + value = this.CurrentSpan[this.CurrentSpanIndex]; + this.CurrentSpanIndex++; + this.Consumed++; + + if (this.CurrentSpanIndex >= this.CurrentSpan.Length) + { + if (this.usingSequence) + { + this.GetNextSpan(); + } + else + { + this.moreData = false; + } + } + + return true; + } + + /// + /// Move the reader back the specified number of items. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Rewind(long count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + this.Consumed -= count; + + if (this.CurrentSpanIndex >= count) + { + this.CurrentSpanIndex -= (int)count; + this.moreData = true; + } + else if (this.usingSequence) + { + // Current segment doesn't have enough data, scan backward through segments + this.RetreatToPreviousSpan(this.Consumed); + } + else + { + throw new ArgumentOutOfRangeException("Rewind went past the start of the memory."); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void RetreatToPreviousSpan(long consumed) + { + Debug.Assert(this.usingSequence, "usingSequence"); + this.ResetReader(); + this.Advance(consumed); + } + + private void ResetReader() + { + Debug.Assert(this.usingSequence, "usingSequence"); + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this.currentPosition = this.Sequence.Start; + this.nextPosition = this.currentPosition; + + if (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) + { + this.moreData = true; + + if (memory.Length == 0) + { + this.CurrentSpan = default; + + // No data in the first span, move to one with data + this.GetNextSpan(); + } + else + { + this.CurrentSpan = memory.Span; + } + } + else + { + // No data in any spans and at end of sequence + this.moreData = false; + this.CurrentSpan = default; + } + } + + /// + /// Get the next segment with available data, if any. + /// + private void GetNextSpan() + { + Debug.Assert(this.usingSequence, "usingSequence"); + if (!this.Sequence.IsSingleSegment) + { + SequencePosition previousNextPosition = this.nextPosition; + while (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) + { + this.currentPosition = previousNextPosition; + if (memory.Length > 0) + { + this.CurrentSpan = memory.Span; + this.CurrentSpanIndex = 0; + return; + } + else + { + this.CurrentSpan = default; + this.CurrentSpanIndex = 0; + previousNextPosition = this.nextPosition; + } + } + } + + this.moreData = false; + } + + /// + /// Move the reader ahead the specified number of items. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Advance(long count) + { + const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); + if ((count & TooBigOrNegative) == 0 && this.CurrentSpan.Length - this.CurrentSpanIndex > (int)count) + { + this.CurrentSpanIndex += (int)count; + this.Consumed += count; + } + else if (this.usingSequence) + { + // Can't satisfy from the current span + this.AdvanceToNextSpan(count); + } + else if (this.CurrentSpan.Length - this.CurrentSpanIndex == (int)count) + { + this.CurrentSpanIndex += (int)count; + this.Consumed += count; + this.moreData = false; + } + else + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + } + + /// + /// Unchecked helper to avoid unnecessary checks where you know count is valid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceCurrentSpan(long count) + { + Debug.Assert(count >= 0, "count >= 0"); + + this.Consumed += count; + this.CurrentSpanIndex += (int)count; + if (this.usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) + { + this.GetNextSpan(); + } + } + + /// + /// Only call this helper if you know that you are advancing in the current span + /// with valid count and there is no need to fetch the next one. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceWithinSpan(long count) + { + Debug.Assert(count >= 0, "count >= 0"); + + this.Consumed += count; + this.CurrentSpanIndex += (int)count; + + Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length, "this.CurrentSpanIndex < this.CurrentSpan.Length"); + } + + /// + /// Move the reader ahead the specified number of items + /// if there are enough elements remaining in the sequence. + /// + /// true if there were enough elements to advance; otherwise false. + internal bool TryAdvance(long count) + { + if (this.Remaining < count) + { + return false; + } + + this.Advance(count); + return true; + } + + private void AdvanceToNextSpan(long count) + { + Debug.Assert(this.usingSequence, "usingSequence"); + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + this.Consumed += count; + while (this.moreData) + { + int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; + + if (remaining > count) + { + this.CurrentSpanIndex += (int)count; + count = 0; + break; + } + + // As there may not be any further segments we need to + // push the current index to the end of the span. + this.CurrentSpanIndex += remaining; + count -= remaining; + Debug.Assert(count >= 0, "count >= 0"); + + this.GetNextSpan(); + + if (count == 0) + { + break; + } + } + + if (count != 0) + { + // Not enough data left- adjust for where we actually ended and throw + this.Consumed -= count; + throw new ArgumentOutOfRangeException(nameof(count)); + } + } + + /// + /// Copies data from the current to the given span. + /// + /// Destination to copy to. + /// True if there is enough data to copy to the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryCopyTo(Span destination) + { + ReadOnlySpan firstSpan = this.UnreadSpan; + if (firstSpan.Length >= destination.Length) + { + firstSpan.Slice(0, destination.Length).CopyTo(destination); + return true; + } + + return this.TryCopyMultisegment(destination); + } + + internal bool TryCopyMultisegment(Span destination) + { + if (this.Remaining < destination.Length) + { + return false; + } + + ReadOnlySpan firstSpan = this.UnreadSpan; + Debug.Assert(firstSpan.Length < destination.Length, "firstSpan.Length < destination.Length"); + firstSpan.CopyTo(destination); + int copied = firstSpan.Length; + + SequencePosition next = this.nextPosition; + while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) + { + if (nextSegment.Length > 0) + { + ReadOnlySpan nextSpan = nextSegment.Span; + int toCopy = Math.Min(nextSpan.Length, destination.Length - copied); + nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied)); + copied += toCopy; + if (copied >= destination.Length) + { + break; + } + } + } + + return true; + } + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Buffers/SequenceReaderExtensions.cs b/src/IKVM.ByteCode/Buffers/SequenceReaderExtensions.cs new file mode 100644 index 0000000000..e60681a68f --- /dev/null +++ b/src/IKVM.ByteCode/Buffers/SequenceReaderExtensions.cs @@ -0,0 +1,284 @@ +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace IKVM.ByteCode.Buffers +{ + + static partial class SequenceReaderExtensions + { + + /// + /// Try to read the given type out of the buffer if possible. Warning: this is dangerous to use with arbitrary + /// structs- see remarks for full details. + /// + /// + /// IMPORTANT: The read is a straight copy of bits. If a struct depends on specific state of its members to + /// behave correctly this can lead to exceptions, etc. If reading endian specific integers, use the explicit + /// overloads such as . + /// + /// + /// True if successful. will be default if failed (due to lack of space). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool TryRead(ref this SequenceReader reader, out T value) + where T : unmanaged + { + var span = reader.UnreadSpan; + if (span.Length < sizeof(T)) + return TryReadMultisegment(ref reader, out value); + + value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(span)); + reader.Advance(sizeof(T)); + return true; + } + + static unsafe bool TryReadMultisegment(ref SequenceReader reader, out T value) + where T : unmanaged + { + Debug.Assert(reader.UnreadSpan.Length < sizeof(T), "reader.UnreadSpan.Length < sizeof(T)"); + + // Not enough data in the current segment, try to peek for the data we need. + var buffer = default(T); + var tempSpan = new Span(&buffer, sizeof(T)); + + if (!reader.TryCopyTo(tempSpan)) + { + value = default; + return false; + } + + value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(tempSpan)); + reader.Advance(sizeof(T)); + return true; + } + + /// + /// Reads an from the next position in the sequence. + /// + /// The reader to read from. + /// Receives the value read. + /// true if there was another byte in the sequence; false otherwise. + public static bool TryRead(ref this SequenceReader reader, out sbyte value) + { + if (TryRead(ref reader, out byte byteValue)) + { + value = unchecked((sbyte)byteValue); + return true; + } + + value = default; + return false; + } + +#if NETFRAMEWORK + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out short value) + { + if (!BitConverter.IsLittleEndian) + { + return reader.TryRead(out value); + } + + return TryReadReverseEndianness(ref reader, out value); + } + +#endif + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out ushort value) + { + if (reader.TryReadBigEndian(out short shortValue)) + { + value = unchecked((ushort)shortValue); + return true; + } + + value = default; + return false; + } + + private static bool TryReadReverseEndianness(ref SequenceReader reader, out short value) + { + if (reader.TryRead(out value)) + { + value = BinaryPrimitives.ReverseEndianness(value); + return true; + } + + return false; + } + +#if NETFRAMEWORK + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out int value) + { + return BitConverter.IsLittleEndian ? TryReadReverseEndianness(ref reader, out value) : reader.TryRead(out value); + } + +#endif + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out uint value) + { + if (reader.TryReadBigEndian(out int intValue)) + { + value = unchecked((uint)intValue); + return true; + } + + value = default; + return false; + } + + static bool TryReadReverseEndianness(ref SequenceReader reader, out int value) + { + if (reader.TryRead(out value)) + { + value = BinaryPrimitives.ReverseEndianness(value); + return true; + } + + return false; + } + +#if NETFRAMEWORK + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out long value) + { + return BitConverter.IsLittleEndian ? TryReadReverseEndianness(ref reader, out value) : reader.TryRead(out value); + } + +#endif + + /// + /// Reads an as big endian. + /// + /// False if there wasn't enough data for an . + public static bool TryReadBigEndian(ref this SequenceReader reader, out ulong value) + { + if (reader.TryReadBigEndian(out long longValue)) + { + value = unchecked((ulong)longValue); + return true; + } + + value = default; + return false; + } + + static bool TryReadReverseEndianness(ref SequenceReader reader, out long value) + { + if (reader.TryRead(out value)) + { + value = BinaryPrimitives.ReverseEndianness(value); + return true; + } + + return false; + } + + /// + /// Reads a as big endian. + /// + /// False if there wasn't enough data for a . + public static unsafe bool TryReadBigEndian(ref this SequenceReader reader, out float value) + { + if (reader.TryReadBigEndian(out int intValue)) + { + value = *(float*)&intValue; + return true; + } + + value = default; + return false; + } + + /// + /// Reads a as big endian. + /// + /// False if there wasn't enough data for a . + public static unsafe bool TryReadBigEndian(ref this SequenceReader reader, out double value) + { + if (reader.TryReadBigEndian(out long longValue)) + { + value = *(double*)&longValue; + return true; + } + + value = default; + return false; + } + + /// + /// Try to read data with given . + /// + /// Read count. + /// The read data, if successfully read requested data. + /// true if remaining items in current is enough for . + public static bool TryReadExact(ref this SequenceReader reader, int count, out ReadOnlySequence sequence) + { + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (count > reader.Remaining) + { + sequence = default; + return false; + } + + sequence = reader.Sequence.Slice(reader.Position, count); + if (count != 0) + reader.Advance(count); + + return true; + } + + /// + /// Try to read data with given . + /// + /// Read count. + /// The read data, if successfully read requested data. + /// true if remaining items in current is enough for . + public static bool TryReadExact(ref this SequenceReader reader, long count, out ReadOnlySequence sequence) + { + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (count > reader.Remaining) + { + sequence = default; + return false; + } + + sequence = reader.Sequence.Slice(reader.Position, count); + if (count != 0) + reader.Advance(count); + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/ByteCodeException.cs b/src/IKVM.ByteCode/ByteCodeException.cs new file mode 100644 index 0000000000..5a5ec57ace --- /dev/null +++ b/src/IKVM.ByteCode/ByteCodeException.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Serialization; + +namespace IKVM.ByteCode +{ + + /// + /// Represents an error that occurred with manipulation of Java byte code or class formats. + /// + [Serializable] + internal class ByteCodeException : Exception + { + + /// + /// Initializes a new instance. + /// + internal ByteCodeException() + { + + } + + /// + /// Initializes a new instance. + /// + /// + internal ByteCodeException(string message) : + base(message) + { + + } + + /// + /// Initializes a new instance. + /// + /// + internal ByteCodeException(Exception innerException) : + base(innerException.Message, innerException) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + internal ByteCodeException(string message, Exception innerException) : + base(message, innerException) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + protected ByteCodeException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/ClassFormatVersion.cs b/src/IKVM.ByteCode/ClassFormatVersion.cs new file mode 100644 index 0000000000..33896b9e9d --- /dev/null +++ b/src/IKVM.ByteCode/ClassFormatVersion.cs @@ -0,0 +1,66 @@ +using System; + +namespace IKVM.ByteCode +{ + + /// + /// Describes a Java Class file format version number, consisting of a major and minor pair. + /// + /// + /// + internal record struct ClassFormatVersion(int Major, int Minor) : IComparable + { + + public static implicit operator ClassFormatVersion(int major) + { + return new ClassFormatVersion(major, 0); + } + + public static implicit operator string(ClassFormatVersion version) + { + return version.ToString(); + } + + public static bool operator >(ClassFormatVersion a, ClassFormatVersion b) + { + return a.CompareTo(b) is 1; + } + + public static bool operator <(ClassFormatVersion a, ClassFormatVersion b) + { + return a.CompareTo(b) is -1; + } + + public static bool operator >=(ClassFormatVersion a, ClassFormatVersion b) + { + return a.CompareTo(b) is 0 or 1; + } + + public static bool operator <=(ClassFormatVersion a, ClassFormatVersion b) + { + return a.CompareTo(b) is 0 or -1; + } + + public int CompareTo(ClassFormatVersion other) + { + if (Major < other.Major) + return -1; + else if (Major > other.Major) + return 1; + + if (Minor < other.Minor) + return -1; + else if (Minor > other.Minor) + return 1; + + return 0; + } + + public override string ToString() + { + return $"{Major}.{Minor}"; + } + + } + +} diff --git a/src/IKVM.ByteCode/Compiler/IsExternalInit.cs b/src/IKVM.ByteCode/Compiler/IsExternalInit.cs new file mode 100644 index 0000000000..e251a1ef91 --- /dev/null +++ b/src/IKVM.ByteCode/Compiler/IsExternalInit.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 + +using System.ComponentModel; + +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} + +#endif diff --git a/src/IKVM.ByteCode/IKVM.ByteCode.csproj b/src/IKVM.ByteCode/IKVM.ByteCode.csproj new file mode 100644 index 0000000000..7c6ea6b483 --- /dev/null +++ b/src/IKVM.ByteCode/IKVM.ByteCode.csproj @@ -0,0 +1,32 @@ + + + + net461;netcoreapp3.1;net6.0 + 11 + true + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/IKVM.ByteCode/ModuleExportsFlag.cs b/src/IKVM.ByteCode/ModuleExportsFlag.cs new file mode 100644 index 0000000000..791f2b0085 --- /dev/null +++ b/src/IKVM.ByteCode/ModuleExportsFlag.cs @@ -0,0 +1,15 @@ +using System; + +namespace IKVM.ByteCode +{ + + [Flags] + internal enum ModuleExportsFlag : ushort + { + + Synthetic = 0x1000, + Mandated = 0x8000, + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/ModuleFlag.cs b/src/IKVM.ByteCode/ModuleFlag.cs new file mode 100644 index 0000000000..711ea82db7 --- /dev/null +++ b/src/IKVM.ByteCode/ModuleFlag.cs @@ -0,0 +1,16 @@ +using System; + +namespace IKVM.ByteCode +{ + + [Flags] + internal enum ModuleFlag : ushort + { + + Open = 0x0020, + Synthetic = 0x1000, + Mandated = 0x8000, + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/ModuleOpensFlag.cs b/src/IKVM.ByteCode/ModuleOpensFlag.cs new file mode 100644 index 0000000000..ea6ade7d54 --- /dev/null +++ b/src/IKVM.ByteCode/ModuleOpensFlag.cs @@ -0,0 +1,15 @@ +using System; + +namespace IKVM.ByteCode +{ + + [Flags] + internal enum ModuleOpensFlag : ushort + { + + Synthetic = 0x1000, + Mandated = 0x8000, + + } + +} diff --git a/src/IKVM.ByteCode/ModuleRequiresFlag.cs b/src/IKVM.ByteCode/ModuleRequiresFlag.cs new file mode 100644 index 0000000000..da6dc0457c --- /dev/null +++ b/src/IKVM.ByteCode/ModuleRequiresFlag.cs @@ -0,0 +1,17 @@ +using System; + +namespace IKVM.ByteCode +{ + + [Flags] + internal enum ModuleRequiresFlag : ushort + { + + Transitive = 0x0020, + StaticPhase = 0x0040, + Synthetic = 0x1000, + Mandated = 0x8000, + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/AnnotationDefaultAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/AnnotationDefaultAttributeRecord.cs new file mode 100644 index 0000000000..a714c0a5be --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/AnnotationDefaultAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record AnnotationDefaultAttributeRecord(ElementValueRecord DefaultValue) : AttributeRecord + { + + public static bool TryReadAnnotationDefaultAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (ElementValueRecord.TryRead(ref reader, out var defaultValue) == false) + return false; + + attribute = new AnnotationDefaultAttributeRecord(defaultValue); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/AnnotationRecord.cs b/src/IKVM.ByteCode/Parsing/AnnotationRecord.cs new file mode 100644 index 0000000000..c7da556b50 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/AnnotationRecord.cs @@ -0,0 +1,62 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct AnnotationRecord(ushort TypeIndex, ElementValuePairRecord[] Elements) + { + + public static bool TryReadAnnotation(ref ClassFormatReader reader, out AnnotationRecord annotation) + { + annotation = default; + + if (reader.TryReadU2(out ushort typeIndex) == false) + return false; + if (reader.TryReadU2(out ushort pairCount) == false) + return false; + + var elements = new ElementValuePairRecord[pairCount]; + for (int i = 0; i < pairCount; i++) + { + if (ElementValuePairRecord.TryRead(ref reader, out var element) == false) + return false; + + elements[i] = element; + } + + annotation = new AnnotationRecord(typeIndex, elements); + return true; + } + + public int GetSize() + { + var size = 0; + size += sizeof(ushort); + size += sizeof(ushort); + + foreach (var element in Elements) + size += element.GetSize(); + + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(TypeIndex) == false) + return false; + if (writer.TryWriteU2((ushort)Elements.Length) == false) + return false; + + foreach (var element in Elements) + if (element.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/AppendStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/AppendStackMapFrameRecord.cs new file mode 100644 index 0000000000..255022276a --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/AppendStackMapFrameRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record AppendStackMapFrameRecord(byte Tag, ushort OffsetDelta, VerificationTypeInfoRecord[] Locals) : StackMapFrameRecord(Tag) + { + + public static bool TryReadAppendStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU2(out ushort offsetDelta) == false) + return false; + + var locals = new VerificationTypeInfoRecord[tag - 251]; + for (int i = 0; i < tag - 251; i++) + { + if (VerificationTypeInfoRecord.TryReadVerificationTypeInfo(ref reader, out var local) == false) + return false; + + locals[i] = local; + } + + frame = new AppendStackMapFrameRecord(tag, offsetDelta, locals); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/AttributeInfoRecord.cs b/src/IKVM.ByteCode/Parsing/AttributeInfoRecord.cs new file mode 100644 index 0000000000..a60786bd8b --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/AttributeInfoRecord.cs @@ -0,0 +1,34 @@ +using System.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal record struct AttributeInfoRecord(ushort NameIndex, byte[] Data) + { + + /// + /// Parses an attribute. + /// + /// + /// + public static bool TryReadAttribute(ref ClassFormatReader reader, out AttributeInfoRecord attribute) + { + attribute = default; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU4(out uint length) == false) + return false; + if (reader.TryReadManyU1(length, out var info) == false) + return false; + + var infoBuffer = new byte[info.Length]; + info.CopyTo(infoBuffer); + + attribute = new AttributeInfoRecord(nameIndex, infoBuffer); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/AttributeRecord.cs b/src/IKVM.ByteCode/Parsing/AttributeRecord.cs new file mode 100644 index 0000000000..bc302ef9a8 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/AttributeRecord.cs @@ -0,0 +1,83 @@ +using System.Buffers; + +using IKVM.ByteCode.Reading; + +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record AttributeRecord + { + + /// + /// Attempts to read an attribute data record from the specified . + /// + /// + /// + /// + public static bool TryRead(AttributeInfoReader info, out AttributeRecord attribute) + { + return TryRead(info.Name.Value, new ReadOnlySequence(info.Data), out attribute); + } + + /// + /// Attempts to read an attribute data record from the specified . + /// + /// + /// + /// + /// + public static bool TryRead(string name, ReadOnlySequence data, out AttributeRecord attribute) + { + var reader = new ClassFormatReader(data); + if (TryReadAttribute(name, ref reader, out attribute) == false) + return false; + + return true; + } + + /// + /// Attempts to read a class record starting at the current position. + /// + /// + /// + /// + /// + /// + public static bool TryReadAttribute(string name, ref ClassFormatReader reader, out AttributeRecord attribute) => name switch + { + "ConstantValue" => ConstantValueAttributeRecord.TryReadConstantValueAttribute(ref reader, out attribute), + "Code" => CodeAttributeRecord.TryReadCodeAttribute(ref reader, out attribute), + "StackMapTable" => StackMapTableAttributeRecord.TryRead(ref reader, out attribute), + "Exceptions" => ExceptionsAttributeRecord.TryReadExceptionsAttribute(ref reader, out attribute), + "InnerClasses" => InnerClassesAttributeRecord.TryReadInnerClassesAttribute(ref reader, out attribute), + "EnclosingMethod" => EnclosingMethodAttributeRecord.TryReadEnclosingMethodAttribute(ref reader, out attribute), + "Synthetic" => SyntheticAttributeRecord.TryReadSyntheticAttribute(ref reader, out attribute), + "Signature" => SignatureAttributeRecord.TryReadSignatureAttribute(ref reader, out attribute), + "SourceFile" => SourceFileAttributeRecord.TryReadSourceFileAttribute(ref reader, out attribute), + "SourceDebugExtension" => SourceDebugExtensionAttributeRecord.TryReadSourceDebugExtensionAttribute(ref reader, out attribute), + "LineNumberTable" => LineNumberTableAttributeRecord.TryReadLineNumberTableAttribute(ref reader, out attribute), + "LocalVariableTable" => LocalVariableTableAttributeRecord.TryReadLocalVariableTableAttribute(ref reader, out attribute), + "LocalVariableTypeTable" => LocalVariableTypeTableAttributeRecord.TryReadLocalVariableTypeTableAttribute(ref reader, out attribute), + "Deprecated" => DeprecatedAttributeRecord.TryReadDeprecatedAttribute(ref reader, out attribute), + "RuntimeVisibleAnnotations" => RuntimeVisibleAnnotationsAttributeRecord.TryReadRuntimeVisibleAnnotationsAttribute(ref reader, out attribute), + "RuntimeInvisibleAnnotations" => RuntimeInvisibleAnnotationsAttributeRecord.TryReadRuntimeInvisibleAnnotationsAttribute(ref reader, out attribute), + "RuntimeVisibleParameterAnnotations" => RuntimeVisibleParameterAnnotationsAttributeRecord.TryReadRuntimeVisibleParameterAnnotationsAttribute(ref reader, out attribute), + "RuntimeInvisibleParameterAnnotations" => RuntimeInvisibleParameterAnnotationsAttributeRecord.TryReadRuntimeInvisibleParameterAnnotationsAttribute(ref reader, out attribute), + "RuntimeVisibleTypeAnnotations" => RuntimeVisibleTypeAnnotationsAttributeRecord.TryReadRuntimeVisibleTypeAnnotationsAttribute(ref reader, out attribute), + "RuntimeInvisibleTypeAnnotations" => RuntimeInvisibleTypeAnnotationsAttributeRecord.TryReadRuntimeInvisibleTypeAnnotationsAttribute(ref reader, out attribute), + "AnnotationDefault" => AnnotationDefaultAttributeRecord.TryReadAnnotationDefaultAttribute(ref reader, out attribute), + "BootstrapMethods" => BootstrapMethodsAttributeRecord.TryReadBootstrapMethodsAttribute(ref reader, out attribute), + "MethodParameters" => MethodParametersAttributeRecord.TryReadMethodParametersAttribute(ref reader, out attribute), + "Module" => ModuleAttributeRecord.TryReadModuleAttribute(ref reader, out attribute), + "ModulePackages" => ModulePackagesAttributeRecord.TryReadModulePackagesAttribute(ref reader, out attribute), + "ModuleMainClass" => ModuleMainClassAttributeRecord.TryReadModuleMainClassAttribute(ref reader, out attribute), + "NestHost" => NestHostAttributeRecord.TryReadNestHostAttribute(ref reader, out attribute), + "NestMembers" => NestMembersAttributeRecord.TryReadNestMembersAttribute(ref reader, out attribute), + "Record" => RecordAttributeRecord.TryReadRecordAttribute(ref reader, out attribute), + "PermittedSubclasses" => PermittedSubclassesAttributeRecord.TryReadPermittedSubclassesAttribute(ref reader, out attribute), + _ => UnknownAttributeRecord.TryReadCustomAttribute(ref reader, out attribute), + }; + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs new file mode 100644 index 0000000000..b40529a97b --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct BootstrapMethodsAttributeMethodRecord(ushort MethodRefIndex, ushort[] Arguments) + { + + public static bool TryReadBootstrapMethod(ref ClassFormatReader reader, out BootstrapMethodsAttributeMethodRecord method) + { + method = default; + + if (reader.TryReadU2(out ushort methodRefIndex) == false) + return false; + if (reader.TryReadU2(out ushort argumentCount) == false) + return false; + + var arguments = new ushort[argumentCount]; + for (int i = 0; i < argumentCount; i++) + { + if (reader.TryReadU2(out ushort argumentIndex) == false) + return false; + + arguments[i] = argumentIndex; + } + + method = new BootstrapMethodsAttributeMethodRecord(methodRefIndex, arguments); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeRecord.cs new file mode 100644 index 0000000000..5fa5c4e554 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record BootstrapMethodsAttributeRecord(BootstrapMethodsAttributeMethodRecord[] Methods) : AttributeRecord + { + + public static bool TryReadBootstrapMethodsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var methods = new BootstrapMethodsAttributeMethodRecord[count]; + for (int i = 0; i < count; i++) + { + if (BootstrapMethodsAttributeMethodRecord.TryReadBootstrapMethod(ref reader, out var method) == false) + return false; + + methods[i] = method; + } + + attribute = new BootstrapMethodsAttributeRecord(methods); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ChopStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/ChopStackMapFrameRecord.cs new file mode 100644 index 0000000000..cd7a52c5a6 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ChopStackMapFrameRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ChopStackMapFrameRecord(byte Tag, ushort OffsetDelta) : StackMapFrameRecord(Tag) + { + + public static bool TryReadChopStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU2(out ushort offsetDelta) == false) + return false; + + frame = new ChopStackMapFrameRecord(tag, offsetDelta); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ClassConstantRecord.cs b/src/IKVM.ByteCode/Parsing/ClassConstantRecord.cs new file mode 100644 index 0000000000..67502749e5 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ClassConstantRecord.cs @@ -0,0 +1,26 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ClassConstantRecord(ushort NameIndex) : ConstantRecord + { + + /// + /// Parses a Class constant in the constant pool. + /// + /// + /// + public static bool TryReadClassConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + + constant = new ClassConstantRecord(nameIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ClassFormatReader.cs b/src/IKVM.ByteCode/Parsing/ClassFormatReader.cs new file mode 100644 index 0000000000..443eafddbe --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ClassFormatReader.cs @@ -0,0 +1,105 @@ +using System; +using System.Buffers; + +using IKVM.ByteCode.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + /// + /// Provides common methods to read through memory of a Class file. + /// + internal ref struct ClassFormatReader + { + + SequenceReader reader; + + /// + /// Initializes a new instance. + /// + /// + public ClassFormatReader(ReadOnlyMemory buffer) : + this(new ReadOnlySequence(buffer)) + { + + } + + /// + /// Initializes a new instance. + /// + /// + public ClassFormatReader(ReadOnlySequence sequence) + { + reader = new SequenceReader(sequence); + } + + /// + /// Gets the count of bytes in the reader. + /// + public long Length => reader.Length; + + /// + /// Attempts to read a value defined as a 'u1'. + /// + /// + /// + public bool TryReadU1(out byte u1) + { + return reader.TryRead(out u1); + } + + /// + /// Attempts to read a value defined as a 'u2'. + /// + /// + /// + public bool TryReadU2(out ushort u2) + { + return reader.TryReadBigEndian(out u2); + } + + /// + /// Attempts to read a value defined as a 'u4'. + /// + /// + /// + public bool TryReadU4(out uint u4) + { + return reader.TryReadBigEndian(out u4); + } + + /// + /// Attempts to read the exact given number of bytes. + /// + /// + /// + /// + public bool TryReadManyU1(uint count, out ReadOnlySequence sequence) + { + return reader.TryReadExact(count, out sequence); + } + + /// + /// Attempts to read the exact given number of bytes. + /// + /// + /// + /// + public bool TryReadManyU1(long count, out ReadOnlySequence sequence) + { + return reader.TryReadExact(count, out sequence); + } + + /// + /// Attempts to copy available data at the current position to the destination. + /// + /// + /// + public bool TryCopyTo(Span destination) + { + return reader.TryCopyTo(destination); + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ClassFormatWriter.cs b/src/IKVM.ByteCode/Parsing/ClassFormatWriter.cs new file mode 100644 index 0000000000..eba9ddc1fe --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ClassFormatWriter.cs @@ -0,0 +1,86 @@ +using System; +using System.Buffers.Binary; + +namespace IKVM.ByteCode.Parsing +{ + + /// + /// Provides a forward-only writer of big-endian values. + /// + internal ref struct ClassFormatWriter + { + + Span span; + Span next; + long size = 0; + + /// + /// Initializes a new instance. + /// + /// + public ClassFormatWriter(Span span) + { + this.span = next = span; + } + + /// + /// Gets the span being written to. + /// + public Span Span => span; + + /// + /// Gets the total number of written bytes. + /// + public long Size => size; + + /// + /// Writes a value defined as a 'u1' in the class format specification. + /// + /// + /// + public bool TryWriteU1(byte value) + { + if (next.Length < sizeof(byte)) + return false; + + next[0] = value; + next = next.Slice(sizeof(byte)); + size += sizeof(byte); + return true; + } + + /// + /// Writes a value defined as a 'u2' in the class format specification. + /// + /// + /// + public bool TryWriteU2(ushort value) + { + if (next.Length < sizeof(ushort)) + return false; + + BinaryPrimitives.WriteUInt16BigEndian(next, value); + next = next.Slice(sizeof(ushort)); + size += sizeof(ushort); + return true; + } + + /// + /// Writes a value defined as a 'u4' in the class format specification. + /// + /// + /// + public bool TryWriteU4(uint value) + { + if (next.Length < sizeof(uint)) + return false; + + BinaryPrimitives.WriteUInt32BigEndian(next, value); + next = next.Slice(sizeof(uint)); + size += sizeof(uint); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ClassRecord.cs b/src/IKVM.ByteCode/Parsing/ClassRecord.cs new file mode 100644 index 0000000000..577cf28515 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ClassRecord.cs @@ -0,0 +1,187 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ClassRecord(ushort MinorVersion, ushort MajorVersion, ConstantRecord[] Constants, AccessFlag AccessFlags, ushort ThisClassIndex, ushort SuperClassIndex, InterfaceRecord[] Interfaces, FieldRecord[] Fields, MethodRecord[] Methods, AttributeInfoRecord[] Attributes) + { + + const uint MAGIC = 0xCAFEBABE; + + /// + /// Attempts to read a class record starting at the current position. + /// + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out ClassRecord clazz) + { + clazz = default; + + if (reader.TryReadU4(out uint magic) == false) + return false; + if (magic != MAGIC) + throw new ByteCodeException($"Unexpected magic value '{magic}'."); + + if (reader.TryReadU2(out ushort minorVersion) == false) + return false; + if (reader.TryReadU2(out ushort majorVersion) == false) + return false; + + if (majorVersion > 63) + throw new UnsupportedClassVersionException(new ClassFormatVersion(majorVersion, minorVersion)); + + if (TryReadConstants(ref reader, out var constants) == false) + return false; + + if (reader.TryReadU2(out ushort accessFlags) == false) + return false; + + if (reader.TryReadU2(out ushort thisClass) == false) + return false; + + if (reader.TryReadU2(out ushort superClass) == false) + return false; + + if (TryReadInterfaces(ref reader, out var interfaces) == false) + return false; + + if (TryReadFields(ref reader, out var fields) == false) + return false; + + if (TryReadMethods(ref reader, out var methods) == false) + return false; + + if (TryReadAttributes(ref reader, out var attributes) == false) + return false; + + clazz = new ClassRecord(minorVersion, majorVersion, constants, (AccessFlag)accessFlags, thisClass, superClass, interfaces, fields, methods, attributes); + return true; + } + + /// + /// Attempts to read the set of constants at the current position. + /// + /// + /// + /// + static bool TryReadConstants(ref ClassFormatReader reader, out ConstantRecord[] constants) + { + constants = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + constants = new ConstantRecord[count]; + for (int i = 1; i < count; i++) + { + if (ConstantRecord.TryRead(ref reader, out var constant, out var skip) == false) + return false; + + constants[i] = constant; + i += skip; + } + + return true; + } + + /// + /// Attempts to read the set of interfaces starting from the current position. + /// + /// + /// + /// + static bool TryReadInterfaces(ref ClassFormatReader reader, out InterfaceRecord[] interfaces) + { + interfaces = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + interfaces = new InterfaceRecord[count]; + for (int i = 0; i < count; i++) + { + if (InterfaceRecord.TryRead(ref reader, out InterfaceRecord iface) == false) + return false; + + interfaces[i] = iface; + } + + return true; + } + + /// + /// Attempts to read the set of fields starting from the current position. + /// + /// + /// + /// + static bool TryReadFields(ref ClassFormatReader reader, out FieldRecord[] fields) + { + fields = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + fields = new FieldRecord[count]; + for (int i = 0; i < count; i++) + { + if (FieldRecord.TryRead(ref reader, out FieldRecord field) == false) + return false; + + fields[i] = field; + } + + return true; + } + + /// + /// Attempts to read the set of methods starting from the current position. + /// + /// + /// + /// + static bool TryReadMethods(ref ClassFormatReader reader, out MethodRecord[] methods) + { + methods = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + methods = new MethodRecord[count]; + for (int i = 0; i < count; i++) + { + if (MethodRecord.TryRead(ref reader, out MethodRecord method) == false) + return false; + + methods[i] = method; + } + + return true; + } + + /// + /// Parses an attributes count followed by a sequence of attributes. + /// + /// + /// + internal static bool TryReadAttributes(ref ClassFormatReader reader, out AttributeInfoRecord[] attributes) + { + attributes = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + attributes = new AttributeInfoRecord[count]; + for (int i = 0; i < count; i++) + { + if (AttributeInfoRecord.TryReadAttribute(ref reader, out var attribute) == false) + return false; + + attributes[i] = attribute; + } + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/CodeAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/CodeAttributeRecord.cs new file mode 100644 index 0000000000..e8bd677a31 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/CodeAttributeRecord.cs @@ -0,0 +1,53 @@ +using System; +using System.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record CodeAttributeRecord(ushort MaxStack, ushort MaxLocals, ReadOnlyMemory Code, ExceptionHandlerRecord[] ExceptionTable, AttributeInfoRecord[] Attributes) : AttributeRecord + { + + public static bool TryReadCodeAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort maxStack) == false) + return false; + if (reader.TryReadU2(out ushort maxLocals) == false) + return false; + if (reader.TryReadU4(out uint codeLength) == false) + return false; + if (reader.TryReadManyU1(codeLength, out ReadOnlySequence code) == false) + return false; + + var codeBuffer = new byte[code.Length]; + code.CopyTo(codeBuffer); + + if (reader.TryReadU2(out ushort exceptionTableLength) == false) + return false; + + var exceptionTable = new ExceptionHandlerRecord[(int)exceptionTableLength]; + for (int i = 0; i < exceptionTableLength; i++) + { + if (reader.TryReadU2(out ushort startOffset) == false) + return false; + if (reader.TryReadU2(out ushort endOffset) == false) + return false; + if (reader.TryReadU2(out ushort handlerOffset) == false) + return false; + if (reader.TryReadU2(out ushort catchTypeIndex) == false) + return false; + + exceptionTable[i] = new ExceptionHandlerRecord(startOffset, endOffset, handlerOffset, catchTypeIndex); + } + + if (ClassRecord.TryReadAttributes(ref reader, out var attributes) == false) + return false; + + attribute = new CodeAttributeRecord(maxStack, maxLocals, codeBuffer, exceptionTable, attributes); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ConstantRecord.cs b/src/IKVM.ByteCode/Parsing/ConstantRecord.cs new file mode 100644 index 0000000000..3986e4cb61 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ConstantRecord.cs @@ -0,0 +1,50 @@ +namespace IKVM.ByteCode.Parsing +{ + + /// + /// Base type for constant records. + /// + internal abstract record ConstantRecord + { + + /// + /// Attempts to read the constant at the current position. Returns the the number of index positions to skip. + /// + /// + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU1(out byte tag) == false) + return false; + + return (ConstantTag)tag switch + { + ConstantTag.Utf8 => Utf8ConstantRecord.TryReadUtf8Constant(ref reader, out constant, out skip), + ConstantTag.Integer => IntegerConstantRecord.TryReadIntegerConstant(ref reader, out constant, out skip), + ConstantTag.Float => FloatConstantRecord.TryReadFloatConstant(ref reader, out constant, out skip), + ConstantTag.Long => LongConstantRecord.TryReadLongConstant(ref reader, out constant, out skip), + ConstantTag.Double => DoubleConstantRecord.TryReadDoubleConstant(ref reader, out constant, out skip), + ConstantTag.Class => ClassConstantRecord.TryReadClassConstant(ref reader, out constant, out skip), + ConstantTag.String => StringConstantRecord.TryReadStringConstant(ref reader, out constant, out skip), + ConstantTag.Fieldref => FieldrefConstantRecord.TryReadFieldrefConstant(ref reader, out constant, out skip), + ConstantTag.Methodref => MethodrefConstantRecord.TryReadMethodrefConstant(ref reader, out constant, out skip), + ConstantTag.InterfaceMethodref => InterfaceMethodrefConstantRecord.TryReadInterfaceMethodrefConstant(ref reader, out constant, out skip), + ConstantTag.NameAndType => NameAndTypeConstantRecord.TryReadNameAndTypeConstant(ref reader, out constant, out skip), + ConstantTag.MethodHandle => MethodHandleConstantRecord.TryReadMethodHandleConstant(ref reader, out constant, out skip), + ConstantTag.MethodType => MethodTypeConstantRecord.TryReadMethodTypeConstant(ref reader, out constant, out skip), + ConstantTag.Dynamic => DynamicConstantRecord.TryReadDynamicConstant(ref reader, out constant, out skip), + ConstantTag.InvokeDynamic => InvokeDynamicConstantRecord.TryReadInvokeDynamicConstant(ref reader, out constant, out skip), + ConstantTag.Module => ModuleConstantRecord.TryReadModuleConstant(ref reader, out constant, out skip), + ConstantTag.Package => PackageConstantRecord.TryReadPackageConstant(ref reader, out constant, out skip), + _ => throw new ByteCodeException($"Encountered an unknown constant tag: '0x{tag:X}'."), + }; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ConstantTag.cs b/src/IKVM.ByteCode/Parsing/ConstantTag.cs new file mode 100644 index 0000000000..94a712c1d3 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ConstantTag.cs @@ -0,0 +1,27 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal enum ConstantTag : byte + { + + Utf8 = 1, + Integer = 3, + Float = 4, + Long = 5, + Double = 6, + Class = 7, + String = 8, + Fieldref = 9, + Methodref = 10, + InterfaceMethodref = 11, + NameAndType = 12, + MethodHandle = 15, + MethodType = 16, + Dynamic = 17, + InvokeDynamic = 18, + Module = 19, + Package = 20, + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ConstantValueAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/ConstantValueAttributeRecord.cs new file mode 100644 index 0000000000..1859a13d23 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ConstantValueAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ConstantValueAttributeRecord(ushort ValueIndex) : AttributeRecord + { + + public static bool TryReadConstantValueAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort valueIndex) == false) + return false; + + attribute = new ConstantValueAttributeRecord(valueIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/DeprecatedAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/DeprecatedAttributeRecord.cs new file mode 100644 index 0000000000..eb835d2106 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/DeprecatedAttributeRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record DeprecatedAttributeRecord : AttributeRecord + { + + public static bool TryReadDeprecatedAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = new DeprecatedAttributeRecord(); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/DoubleConstantRecord.cs b/src/IKVM.ByteCode/Parsing/DoubleConstantRecord.cs new file mode 100644 index 0000000000..420e4564eb --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/DoubleConstantRecord.cs @@ -0,0 +1,38 @@ +using System; + +using IKVM.ByteCode.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record DoubleConstantRecord(double Value) : ConstantRecord + { + + /// + /// Parses a Double constant in the constant pool. + /// + /// + /// + public static bool TryReadDoubleConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 1; + + if (reader.TryReadU4(out uint a) == false) + return false; + if (reader.TryReadU4(out uint b) == false) + return false; + +#if NETFRAMEWORK || NETCOREAPP3_1 + var v = RawBitConverter.UInt64BitsToDouble(((ulong)a << 32) | b); +#else + var v = BitConverter.UInt64BitsToDouble(((ulong)a << 32) | b); +#endif + + constant = new DoubleConstantRecord(v); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/DoubleVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/DoubleVariableInfoRecord.cs new file mode 100644 index 0000000000..3363bcad60 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/DoubleVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record DoubleVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new DoubleVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/DynamicConstantRecord.cs b/src/IKVM.ByteCode/Parsing/DynamicConstantRecord.cs new file mode 100644 index 0000000000..c5e003142e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/DynamicConstantRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record DynamicConstantRecord(ushort BootstrapMethodAttributeIndex, ushort NameAndTypeIndex) : ConstantRecord + { + + /// + /// Parses a Dynamic constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadDynamicConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skipIndex) + { + constant = null; + skipIndex = 0; + + if (reader.TryReadU2(out ushort bootstrapMethodAttrIndex) == false) + return false; + if (reader.TryReadU2(out ushort nameAndTypeIndex) == false) + return false; + + constant = new DynamicConstantRecord(bootstrapMethodAttrIndex, nameAndTypeIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueAnnotationValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueAnnotationValueRecord.cs new file mode 100644 index 0000000000..229f263559 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueAnnotationValueRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ElementValueAnnotationValueRecord(AnnotationRecord Annotation) : ElementValueValueRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueValueRecord value) + { + value = null; + + if (AnnotationRecord.TryReadAnnotation(ref reader, out var annotation) == false) + return false; + + value = new ElementValueAnnotationValueRecord(annotation); + return true; + } + + public override int GetSize() + { + var size = 0; + size += Annotation.GetSize(); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (Annotation.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueArrayValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueArrayValueRecord.cs new file mode 100644 index 0000000000..d1c6b664c9 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueArrayValueRecord.cs @@ -0,0 +1,53 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ElementValueArrayValueRecord(ElementValueRecord[] Values) : ElementValueValueRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueValueRecord value) + { + value = null; + + if (reader.TryReadU2(out ushort length) == false) + return false; + + var values = new ElementValueRecord[length]; + for (int i = 0; i < length; i++) + { + if (ElementValueRecord.TryRead(ref reader, out var j) == false) + return false; + + values[i] = j; + } + + value = new ElementValueArrayValueRecord(values); + return true; + } + + public override int GetSize() + { + var size = 0; + size += sizeof(ushort); + + foreach (var value in Values) + size += value.GetSize(); + + return size; + } + + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2((ushort)Values.Length) == false) + return false; + + foreach (var value in Values) + if (value.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueClassValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueClassValueRecord.cs new file mode 100644 index 0000000000..8267ae8a46 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueClassValueRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ElementValueClassValueRecord(ushort ClassIndex) : ElementValueValueRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueValueRecord value) + { + value = null; + + if (reader.TryReadU2(out ushort classInfoIndex) == false) + return false; + + value = new ElementValueClassValueRecord(classInfoIndex); + return true; + } + + public override int GetSize() + { + var size = 0; + size += sizeof(ushort); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(ClassIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/ElementValueConstantValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueConstantValueRecord.cs new file mode 100644 index 0000000000..6d791c6d00 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueConstantValueRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ElementValueConstantValueRecord(ushort Index) : ElementValueValueRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueValueRecord value) + { + value = null; + + if (reader.TryReadU2(out ushort index) == false) + return false; + + value = new ElementValueConstantValueRecord(index); + return true; + } + + public override int GetSize() + { + var size = 0; + size += sizeof(ushort); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(Index) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueEnumConstantValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueEnumConstantValueRecord.cs new file mode 100644 index 0000000000..15b3717f24 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueEnumConstantValueRecord.cs @@ -0,0 +1,45 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ElementValueEnumConstantValueRecord(ushort TypeNameIndex, ushort ConstantNameIndex) : ElementValueValueRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueValueRecord value) + { + value = null; + + if (reader.TryReadU2(out ushort typeNameIndex) == false) + return false; + if (reader.TryReadU2(out ushort constantNameIndex) == false) + return false; + + value = new ElementValueEnumConstantValueRecord(typeNameIndex, constantNameIndex); + return true; + } + + public override int GetSize() + { + var size = 0; + size += sizeof(ushort); + size += sizeof(ushort); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(TypeNameIndex) == false) + return false; + if (writer.TryWriteU2(ConstantNameIndex) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValuePairRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValuePairRecord.cs new file mode 100644 index 0000000000..f94ed0427a --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValuePairRecord.cs @@ -0,0 +1,45 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ElementValuePairRecord(ushort NameIndex, ElementValueRecord Value) + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValuePairRecord record) + { + record = default; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (ElementValueRecord.TryRead(ref reader, out var elementValue) == false) + return false; + + record = new ElementValuePairRecord(nameIndex, elementValue); + return true; + } + + public int GetSize() + { + var size = 0; + size += sizeof(ushort); + size += Value.GetSize(); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(NameIndex) == false) + return false; + if (Value.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueRecord.cs new file mode 100644 index 0000000000..4014a0f920 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueRecord.cs @@ -0,0 +1,64 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ElementValueRecord(ElementValueTag Tag, ElementValueValueRecord Value) + { + + public static bool TryRead(ref ClassFormatReader reader, out ElementValueRecord record) + { + record = default; + + if (reader.TryReadU1(out byte tag) == false) + return false; + if (TryReadValue(ref reader, (ElementValueTag)tag, out var value) == false) + return false; + + record = new ElementValueRecord((ElementValueTag)tag, value); + return true; + } + + static bool TryReadValue(ref ClassFormatReader reader, ElementValueTag tag, out ElementValueValueRecord value) => tag switch + { + ElementValueTag.Byte => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Char => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Double => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Float => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Integer => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Long => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Short => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Boolean => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.String => ElementValueConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Enum => ElementValueEnumConstantValueRecord.TryRead(ref reader, out value), + ElementValueTag.Class => ElementValueClassValueRecord.TryRead(ref reader, out value), + ElementValueTag.Annotation => ElementValueAnnotationValueRecord.TryRead(ref reader, out value), + ElementValueTag.Array => ElementValueArrayValueRecord.TryRead(ref reader, out value), + _ => throw new ByteCodeException($"Invalid element value tag: '{(char)tag}'."), + }; + + public int GetSize() + { + var size = 0; + size += sizeof(byte); + size += Value.GetSize(); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1((byte)Tag) == false) + return false; + + if (Value.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueTag.cs b/src/IKVM.ByteCode/Parsing/ElementValueTag.cs new file mode 100644 index 0000000000..76e388b5e6 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueTag.cs @@ -0,0 +1,23 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal enum ElementValueTag : byte + { + + Byte = (byte)'B', + Char = (byte)'C', + Double = (byte)'D', + Float = (byte)'F', + Integer = (byte)'I', + Long = (byte)'J', + Short = (byte)'S', + Boolean = (byte)'Z', + String = (byte)'s', + Enum = (byte)'e', + Class = (byte)'c', + Annotation = (byte)'@', + Array = (byte)'[', + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ElementValueValueRecord.cs b/src/IKVM.ByteCode/Parsing/ElementValueValueRecord.cs new file mode 100644 index 0000000000..e1d51b57c1 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ElementValueValueRecord.cs @@ -0,0 +1,22 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record ElementValueValueRecord + { + + /// + /// Gets the size of the record if written. + /// + /// + public abstract int GetSize(); + + /// + /// Attempts to write the record to the . + /// + /// + /// + public abstract bool TryWrite(ref ClassFormatWriter writer); + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/EnclosingMethodAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/EnclosingMethodAttributeRecord.cs new file mode 100644 index 0000000000..0d19202ea7 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/EnclosingMethodAttributeRecord.cs @@ -0,0 +1,22 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record EnclosingMethodAttributeRecord(ushort ClassIndex, ushort MethodIndex) : AttributeRecord + { + + public static bool TryReadEnclosingMethodAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + if (reader.TryReadU2(out ushort methodIndex) == false) + return false; + + attribute = new EnclosingMethodAttributeRecord(classIndex, methodIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ExceptionHandlerRecord.cs b/src/IKVM.ByteCode/Parsing/ExceptionHandlerRecord.cs new file mode 100644 index 0000000000..e17b4ef97f --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ExceptionHandlerRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ExceptionHandlerRecord(ushort StartOffset, ushort EndOffset, ushort HandlerOffset, ushort CatchTypeIndex); + +} diff --git a/src/IKVM.ByteCode/Parsing/ExceptionsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/ExceptionsAttributeRecord.cs new file mode 100644 index 0000000000..e000b4f5ec --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ExceptionsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ExceptionsAttributeRecord(ushort[] ExceptionsIndexes) : AttributeRecord + { + + public static bool TryReadExceptionsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var entries = new ushort[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort index) == false) + return false; + + entries[i] = index; + } + + attribute = new ExceptionsAttributeRecord(entries); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/FieldRecord.cs b/src/IKVM.ByteCode/Parsing/FieldRecord.cs new file mode 100644 index 0000000000..5471982603 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/FieldRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct FieldRecord(AccessFlag AccessFlags, ushort NameIndex, ushort DescriptorIndex, AttributeInfoRecord[] Attributes) + { + + /// + /// Parses a field. + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out FieldRecord field) + { + field = default; + + if (reader.TryReadU2(out ushort accessFlags) == false) + return false; + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + if (ClassRecord.TryReadAttributes(ref reader, out var attributes) == false) + return false; + + field = new FieldRecord((AccessFlag)accessFlags, nameIndex, descriptorIndex, attributes); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/FieldrefConstantRecord.cs b/src/IKVM.ByteCode/Parsing/FieldrefConstantRecord.cs new file mode 100644 index 0000000000..da7a9c03b6 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/FieldrefConstantRecord.cs @@ -0,0 +1,28 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record FieldrefConstantRecord(ushort ClassIndex, ushort NameAndTypeIndex) : RefConstantRecord(ClassIndex, NameAndTypeIndex) + { + + /// + /// Parses a Fieldref constant in the constant pool. + /// + /// + /// + public static bool TryReadFieldrefConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + if (reader.TryReadU2(out ushort nameAndTypeIndex) == false) + return false; + + constant = new FieldrefConstantRecord(classIndex, nameAndTypeIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/FloatConstantRecord.cs b/src/IKVM.ByteCode/Parsing/FloatConstantRecord.cs new file mode 100644 index 0000000000..7078cff4ef --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/FloatConstantRecord.cs @@ -0,0 +1,36 @@ +using System; + +using IKVM.ByteCode.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record FloatConstantRecord(float Value) : ConstantRecord + { + + /// + /// Parses a Float constant in the constant pool. + /// + /// + /// + public static bool TryReadFloatConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU4(out uint value) == false) + return false; + +#if NETFRAMEWORK || NETCOREAPP3_1 + var v = RawBitConverter.UInt32BitsToSingle(value); +#else + var v = BitConverter.UInt32BitsToSingle(value); +#endif + + constant = new FloatConstantRecord(v); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/FloatVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/FloatVariableInfoRecord.cs new file mode 100644 index 0000000000..538ab9bf7e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/FloatVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record FloatVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new FloatVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/FullStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/FullStackMapFrameRecord.cs new file mode 100644 index 0000000000..0a933c5247 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/FullStackMapFrameRecord.cs @@ -0,0 +1,36 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record FullStackMapFrameRecord(byte Tag, ushort OffsetDelta, VerificationTypeInfoRecord[] Locals, VerificationTypeInfoRecord[] Stack) : StackMapFrameRecord(Tag) + { + + public static bool TryReadFullStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU2(out ushort offsetDelta) == false) + return false; + + if (reader.TryReadU2(out ushort localsCount) == false) + return false; + + var locals = new VerificationTypeInfoRecord[localsCount]; + for (int i = 0; i < localsCount; i++) + if (VerificationTypeInfoRecord.TryReadVerificationTypeInfo(ref reader, out locals[i]) == false) + return false; + + if (reader.TryReadU2(out ushort stackCount) == false) + return false; + + var stack = new VerificationTypeInfoRecord[stackCount]; + for (int i = 0; i < stackCount; i++) + if (VerificationTypeInfoRecord.TryReadVerificationTypeInfo(ref reader, out stack[i]) == false) + return false; + + frame = new FullStackMapFrameRecord(tag, offsetDelta, locals, stack); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/InnerClassesAttributeItemRecord.cs b/src/IKVM.ByteCode/Parsing/InnerClassesAttributeItemRecord.cs new file mode 100644 index 0000000000..03d245a23e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/InnerClassesAttributeItemRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct InnerClassesAttributeItemRecord(ushort InnerClassIndex, ushort OuterClassIndex, ushort InnerNameIndex, AccessFlag InnerClassAccessFlags); + +} diff --git a/src/IKVM.ByteCode/Parsing/InnerClassesAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/InnerClassesAttributeRecord.cs new file mode 100644 index 0000000000..eec2f9ee6c --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/InnerClassesAttributeRecord.cs @@ -0,0 +1,35 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record InnerClassesAttributeRecord(InnerClassesAttributeItemRecord[] Items) : AttributeRecord + { + + public static bool TryReadInnerClassesAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var items = new InnerClassesAttributeItemRecord[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort innerClassInfoIndex) == false) + return false; + if (reader.TryReadU2(out ushort outerClassInfoIndex) == false) + return false; + if (reader.TryReadU2(out ushort innerNameIndex) == false) + return false; + if (reader.TryReadU2(out ushort innerClassAccessFlags) == false) + return false; + + items[i] = new InnerClassesAttributeItemRecord(innerClassInfoIndex, outerClassInfoIndex, innerNameIndex, (AccessFlag)innerClassAccessFlags); + } + + attribute = new InnerClassesAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/IntegerConstantRecord.cs b/src/IKVM.ByteCode/Parsing/IntegerConstantRecord.cs new file mode 100644 index 0000000000..1f07772f39 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/IntegerConstantRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record IntegerConstantRecord(int Value) : ConstantRecord + { + + /// + /// Parses a Integer constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadIntegerConstant(ref ClassFormatReader reader, out ConstantRecord record, out int skip) + { + record = null; + skip = 0; + + if (reader.TryReadU4(out uint value) == false) + return false; + + var v = unchecked((int)value); + + record = new IntegerConstantRecord(v); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/IntegerVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/IntegerVariableInfoRecord.cs new file mode 100644 index 0000000000..978c79e69d --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/IntegerVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record IntegerVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new IntegerVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/InterfaceMethodrefConstantRecord.cs b/src/IKVM.ByteCode/Parsing/InterfaceMethodrefConstantRecord.cs new file mode 100644 index 0000000000..8a4f8df66a --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/InterfaceMethodrefConstantRecord.cs @@ -0,0 +1,28 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record InterfaceMethodrefConstantRecord(ushort ClassIndex, ushort NameAndTypeIndex) : RefConstantRecord(ClassIndex, NameAndTypeIndex) + { + + /// + /// Parses a InterfaceMethodref constant in the constant pool. + /// + /// + /// + public static bool TryReadInterfaceMethodrefConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + if (reader.TryReadU2(out ushort nameAndTypeIndex) == false) + return false; + + constant = new InterfaceMethodrefConstantRecord(classIndex, nameAndTypeIndex); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/InterfaceRecord.cs b/src/IKVM.ByteCode/Parsing/InterfaceRecord.cs new file mode 100644 index 0000000000..6044da7dd1 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/InterfaceRecord.cs @@ -0,0 +1,25 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct InterfaceRecord(ushort ClassIndex) + { + + /// + /// Parses an interface. + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out InterfaceRecord iface) + { + iface = default; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + + iface = new InterfaceRecord(classIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/InvokeDynamicConstantRecord.cs b/src/IKVM.ByteCode/Parsing/InvokeDynamicConstantRecord.cs new file mode 100644 index 0000000000..fe82591c34 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/InvokeDynamicConstantRecord.cs @@ -0,0 +1,28 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record InvokeDynamicConstantRecord(ushort BootstrapMethodAttributeIndex, ushort NameAndTypeIndex) : ConstantRecord + { + + /// + /// Parses a InvokeDynamic constant in the constant pool. + /// + /// + /// + public static bool TryReadInvokeDynamicConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort bootstrapMethodAttrIndex) == false) + return false; + if (reader.TryReadU2(out ushort nameAndTypeIndex) == false) + return false; + + constant = new InvokeDynamicConstantRecord(bootstrapMethodAttrIndex, nameAndTypeIndex); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeItemRecord.cs b/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeItemRecord.cs new file mode 100644 index 0000000000..cba1871c9f --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeItemRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct LineNumberTableAttributeItemRecord(ushort CodeOffset, ushort LineNumber); + +} diff --git a/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeRecord.cs new file mode 100644 index 0000000000..b4ef48d0c0 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LineNumberTableAttributeRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record LineNumberTableAttributeRecord(LineNumberTableAttributeItemRecord[] Items) : AttributeRecord + { + + public static bool TryReadLineNumberTableAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort itemCount) == false) + return false; + + var items = new LineNumberTableAttributeItemRecord[itemCount]; + for (int i = 0; i < itemCount; i++) + { + if (reader.TryReadU2(out ushort codeOffset) == false) + return false; + if (reader.TryReadU2(out ushort lineNumber) == false) + return false; + + items[i] = new LineNumberTableAttributeItemRecord(codeOffset, lineNumber); + } + + attribute = new LineNumberTableAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeItemRecord.cs b/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeItemRecord.cs new file mode 100644 index 0000000000..38ffc6a6ee --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeItemRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct LocalVariableTableAttributeItemRecord(ushort CodeOffset, ushort CodeLength, ushort NameIndex, ushort DescriptorIndex, ushort Index); + +} diff --git a/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeRecord.cs new file mode 100644 index 0000000000..3d0ab69e7e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LocalVariableTableAttributeRecord.cs @@ -0,0 +1,37 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record LocalVariableTableAttributeRecord(LocalVariableTableAttributeItemRecord[] Items) : AttributeRecord + { + + public static bool TryReadLocalVariableTableAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort length) == false) + return false; + + var items = new LocalVariableTableAttributeItemRecord[length]; + for (int i = 0; i < length; i++) + { + if (reader.TryReadU2(out ushort codeOffset) == false) + return false; + if (reader.TryReadU2(out ushort codeLength) == false) + return false; + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + if (reader.TryReadU2(out ushort index) == false) + return false; + + items[i] = new LocalVariableTableAttributeItemRecord(codeOffset, codeLength, nameIndex, descriptorIndex, index); + } + + attribute = new LocalVariableTableAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeItemRecord.cs b/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeItemRecord.cs new file mode 100644 index 0000000000..7acdf2c994 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeItemRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct LocalVariableTypeTableAttributeItemRecord(ushort CodeOffset, ushort CodeLength, ushort NameIndex, ushort SignatureIndex, ushort Index); + +} diff --git a/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeRecord.cs new file mode 100644 index 0000000000..c1c8108f7e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LocalVariableTypeTableAttributeRecord.cs @@ -0,0 +1,37 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record LocalVariableTypeTableAttributeRecord(LocalVariableTypeTableAttributeItemRecord[] Items) : AttributeRecord + { + + public static bool TryReadLocalVariableTypeTableAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort length) == false) + return false; + + var items = new LocalVariableTypeTableAttributeItemRecord[length]; + for (int i = 0; i < length; i++) + { + if (reader.TryReadU2(out ushort codeOffset) == false) + return false; + if (reader.TryReadU2(out ushort codeLength) == false) + return false; + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort signatureIndex) == false) + return false; + if (reader.TryReadU2(out ushort index) == false) + return false; + + items[i] = new LocalVariableTypeTableAttributeItemRecord(codeOffset, codeLength, nameIndex, signatureIndex, index); + } + + attribute = new LocalVariableTypeTableAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/LongConstantRecord.cs b/src/IKVM.ByteCode/Parsing/LongConstantRecord.cs new file mode 100644 index 0000000000..f20548c10b --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LongConstantRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record LongConstantRecord(long Value) : ConstantRecord + { + + /// + /// Parses a Long constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadLongConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 1; + + if (reader.TryReadU4(out uint a) == false) + return false; + if (reader.TryReadU4(out uint b) == false) + return false; + + constant = new LongConstantRecord((long)(((ulong)a << 32) | b)); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/LongVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/LongVariableInfoRecord.cs new file mode 100644 index 0000000000..a37b596569 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/LongVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record LongVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new LongVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodHandleConstantRecord.cs b/src/IKVM.ByteCode/Parsing/MethodHandleConstantRecord.cs new file mode 100644 index 0000000000..43e73a6ec3 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodHandleConstantRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record MethodHandleConstantRecord(ReferenceKind Kind, ushort Index) : ConstantRecord + { + + /// + /// Parses a MethodHandle constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadMethodHandleConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU1(out byte kind) == false) + return false; + if (reader.TryReadU2(out ushort index) == false) + return false; + + constant = new MethodHandleConstantRecord((ReferenceKind)kind, index); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodParametersAttributeParameterRecord.cs b/src/IKVM.ByteCode/Parsing/MethodParametersAttributeParameterRecord.cs new file mode 100644 index 0000000000..508b687cdd --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodParametersAttributeParameterRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct MethodParametersAttributeParameterRecord(ushort NameIndex, AccessFlag AccessFlags); + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodParametersAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/MethodParametersAttributeRecord.cs new file mode 100644 index 0000000000..15e006ddef --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodParametersAttributeRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record MethodParametersAttributeRecord(MethodParametersAttributeParameterRecord[] Parameters) : AttributeRecord + { + + public static bool TryReadMethodParametersAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU1(out byte count) == false) + return false; + + var arguments = new MethodParametersAttributeParameterRecord[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort accessFlags) == false) + return false; + + arguments[i] = new MethodParametersAttributeParameterRecord(nameIndex, (AccessFlag)accessFlags); + } + + attribute = new MethodParametersAttributeRecord(arguments); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodRecord.cs b/src/IKVM.ByteCode/Parsing/MethodRecord.cs new file mode 100644 index 0000000000..96bdb9d3b5 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct MethodRecord(AccessFlag AccessFlags, ushort NameIndex, ushort DescriptorIndex, AttributeInfoRecord[] Attributes) + { + + /// + /// Parses a method. + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out MethodRecord method) + { + method = default; + + if (reader.TryReadU2(out ushort accessFlags) == false) + return false; + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + if (ClassRecord.TryReadAttributes(ref reader, out var attributes) == false) + return false; + + method = new MethodRecord((AccessFlag)accessFlags, nameIndex, descriptorIndex, attributes); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodTypeConstantRecord.cs b/src/IKVM.ByteCode/Parsing/MethodTypeConstantRecord.cs new file mode 100644 index 0000000000..c8f2e38cc3 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodTypeConstantRecord.cs @@ -0,0 +1,27 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record MethodTypeConstantRecord(ushort DescriptorIndex) : ConstantRecord + { + + /// + /// Parses a MethodType constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadMethodTypeConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + + constant = new MethodTypeConstantRecord(descriptorIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/MethodrefConstantRecord.cs b/src/IKVM.ByteCode/Parsing/MethodrefConstantRecord.cs new file mode 100644 index 0000000000..41d1e08d5c --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/MethodrefConstantRecord.cs @@ -0,0 +1,28 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record MethodrefConstantRecord(ushort ClassIndex, ushort NameAndTypeIndex) : RefConstantRecord(ClassIndex, NameAndTypeIndex) + { + + /// + /// Parses a Methodref constant in the constant pool. + /// + /// + /// + public static bool TryReadMethodrefConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + if (reader.TryReadU2(out ushort nameAndTypeIndex) == false) + return false; + + constant = new MethodrefConstantRecord(classIndex, nameAndTypeIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleAttributeExportsRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleAttributeExportsRecord.cs new file mode 100644 index 0000000000..33f3803fd0 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleAttributeExportsRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ModuleAttributeExportsRecord(ushort Index, ModuleExportsFlag Flags, ushort[] Modules); + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleAttributeOpensRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleAttributeOpensRecord.cs new file mode 100644 index 0000000000..0bafd0d9cf --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleAttributeOpensRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ModuleAttributeOpensRecord(ushort Index, ModuleOpensFlag Flags, ushort[] Modules); + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleAttributeProvidesRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleAttributeProvidesRecord.cs new file mode 100644 index 0000000000..b7bd3adb70 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleAttributeProvidesRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ModuleAttributeProvidesRecord(ushort Index, ushort[] Modules); + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleAttributeRecord.cs new file mode 100644 index 0000000000..30689d1b84 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleAttributeRecord.cs @@ -0,0 +1,128 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ModuleAttributeRecord(ushort NameIndex, ModuleFlag Flags, ushort VersionIndex, ModuleAttributeRequiresRecord[] Requires, ModuleAttributeExportsRecord[] Exports, ModuleAttributeOpensRecord[] Opens, ushort[] Uses, ModuleAttributeProvidesRecord[] Provides) : AttributeRecord + { + + public static bool TryReadModuleAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort moduleNameIndex) == false) + return false; + if (reader.TryReadU2(out ushort moduleFlags) == false) + return false; + if (reader.TryReadU2(out ushort moduleVersionIndex) == false) + return false; + + if (reader.TryReadU2(out ushort requiresCount) == false) + return false; + + var requires = new ModuleAttributeRequiresRecord[requiresCount]; + for (int i = 0; i < requiresCount; i++) + { + if (reader.TryReadU2(out ushort requiresIndex) == false) + return false; + if (reader.TryReadU2(out ushort requiresFlags) == false) + return false; + if (reader.TryReadU2(out ushort requiresVersionIndex) == false) + return false; + + requires[i] = new ModuleAttributeRequiresRecord(requiresIndex, (ModuleRequiresFlag)requiresFlags, requiresVersionIndex); + } + + if (reader.TryReadU2(out ushort exportsCount) == false) + return false; + + var exports = new ModuleAttributeExportsRecord[exportsCount]; + for (int i = 0; i < exportsCount; i++) + { + if (reader.TryReadU2(out ushort exportsIndex) == false) + return false; + if (reader.TryReadU2(out ushort exportsFlags) == false) + return false; + + if (reader.TryReadU2(out ushort exportsModuleCount) == false) + return false; + + var exportsModules = new ushort[exportsModuleCount]; + for (int j = 0; j < exportsModuleCount; j++) + { + if (reader.TryReadU2(out ushort exportsToModuleIndex) == false) + return false; + + exportsModules[j] = exportsToModuleIndex; + } + + exports[i] = new ModuleAttributeExportsRecord(exportsIndex, (ModuleExportsFlag)exportsFlags, exportsModules); + } + + if (reader.TryReadU2(out ushort opensCount) == false) + return false; + + var opens = new ModuleAttributeOpensRecord[opensCount]; + for (int i = 0; i < opensCount; i++) + { + if (reader.TryReadU2(out ushort opensIndex) == false) + return false; + if (reader.TryReadU2(out ushort opensFlags) == false) + return false; + + if (reader.TryReadU2(out ushort opensModulesCount) == false) + return false; + + var opensModules = new ushort[opensModulesCount]; + for (int j = 0; j < opensModulesCount; j++) + { + if (reader.TryReadU2(out ushort opensModuleIndex) == false) + return false; + + opensModules[j] = opensModuleIndex; + } + + opens[i] = new ModuleAttributeOpensRecord(opensIndex, (ModuleOpensFlag)opensFlags, opensModules); + } + + if (reader.TryReadU2(out ushort usesCount) == false) + return false; + + var uses = new ushort[usesCount]; + for (int i = 0; i < usesCount; i++) + { + if (reader.TryReadU2(out ushort usesIndex) == false) + return false; + + uses[i] = usesIndex; + } + + if (reader.TryReadU2(out ushort providesCount) == false) + return false; + + var provides = new ModuleAttributeProvidesRecord[providesCount]; + for (int i = 0; i < providesCount; i++) + { + if (reader.TryReadU2(out ushort providesIndex) == false) + return false; + + if (reader.TryReadU2(out ushort providesModulesCount) == false) + return false; + + var providesModules = new ushort[providesModulesCount]; + for (int j = 0; j < providesModulesCount; j++) + { + if (reader.TryReadU2(out ushort providesModuleIndex) == false) + return false; + + providesModules[j] = providesModuleIndex; + } + + provides[i] = new ModuleAttributeProvidesRecord(providesIndex, providesModules); + } + + attribute = new ModuleAttributeRecord(moduleNameIndex, (ModuleFlag)moduleFlags, moduleVersionIndex, requires, exports, opens, uses, provides); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleAttributeRequiresRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleAttributeRequiresRecord.cs new file mode 100644 index 0000000000..5aa439ad2b --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleAttributeRequiresRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ModuleAttributeRequiresRecord(ushort Index, ModuleRequiresFlag Flag, ushort VersionIndex); + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/ModuleConstantRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleConstantRecord.cs new file mode 100644 index 0000000000..2a1651c5c3 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleConstantRecord.cs @@ -0,0 +1,27 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ModuleConstantRecord(ushort NameIndex) : ConstantRecord + { + + /// + /// Parses a Module constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadModuleConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + + constant = new ModuleConstantRecord(nameIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ModuleMainClassAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/ModuleMainClassAttributeRecord.cs new file mode 100644 index 0000000000..068ab4f8e9 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModuleMainClassAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ModuleMainClassAttributeRecord(ushort MainClassIndex) : AttributeRecord + { + + public static bool TryReadModuleMainClassAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort mainClassIndex) == false) + return false; + + attribute = new ModuleMainClassAttributeRecord(mainClassIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ModulePackagesAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/ModulePackagesAttributeRecord.cs new file mode 100644 index 0000000000..14ccf63703 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ModulePackagesAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ModulePackagesAttributeRecord(ushort[] Packages) : AttributeRecord + { + + public static bool TryReadModulePackagesAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var packages = new ushort[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort packageIndex) == false) + return false; + + packages[i] = packageIndex; + } + + attribute = new ModulePackagesAttributeRecord(packages); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/NameAndTypeConstantRecord.cs b/src/IKVM.ByteCode/Parsing/NameAndTypeConstantRecord.cs new file mode 100644 index 0000000000..4a591055c1 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/NameAndTypeConstantRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record NameAndTypeConstantRecord(ushort NameIndex, ushort DescriptorIndex) : ConstantRecord + { + + /// + /// Parses a NameAndType constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadNameAndTypeConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + + constant = new NameAndTypeConstantRecord(nameIndex, descriptorIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/NestHostAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/NestHostAttributeRecord.cs new file mode 100644 index 0000000000..ff62be73c7 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/NestHostAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record NestHostAttributeRecord(ushort HostClassIndex) : AttributeRecord + { + + public static bool TryReadNestHostAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort hostClassIndex) == false) + return false; + + attribute = new NestHostAttributeRecord(hostClassIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/NestMembersAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/NestMembersAttributeRecord.cs new file mode 100644 index 0000000000..b004fa52ad --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/NestMembersAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record NestMembersAttributeRecord(ushort[] ClassIndexes) : AttributeRecord + { + + public static bool TryReadNestMembersAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var classes = new ushort[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + + classes[i] = classIndex; + } + + attribute = new NestMembersAttributeRecord(classes); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/NullVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/NullVariableInfoRecord.cs new file mode 100644 index 0000000000..00c3b00c03 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/NullVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record NullVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new NullVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ObjectVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/ObjectVariableInfoRecord.cs new file mode 100644 index 0000000000..e8d5667ea7 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ObjectVariableInfoRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record ObjectVariableInfoRecord(ushort ClassIndex) : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = null; + + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + + record = new ObjectVariableInfoRecord(classIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/PackageConstantRecord.cs b/src/IKVM.ByteCode/Parsing/PackageConstantRecord.cs new file mode 100644 index 0000000000..e98d5f2d75 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/PackageConstantRecord.cs @@ -0,0 +1,27 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record PackageConstantRecord(ushort NameIndex) : ConstantRecord + { + + /// + /// Parses a Package constant in the constant pool. + /// + /// + /// + /// + public static bool TryReadPackageConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + + constant = new PackageConstantRecord(nameIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/ParameterAnnotationRecord.cs b/src/IKVM.ByteCode/Parsing/ParameterAnnotationRecord.cs new file mode 100644 index 0000000000..6157f2bcde --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/ParameterAnnotationRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct ParameterAnnotationRecord(AnnotationRecord[] Annotations) + { + + public static bool TryReadParameterAnnotation(ref ClassFormatReader reader, out ParameterAnnotationRecord record) + { + record = default; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var annotations = new AnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (AnnotationRecord.TryReadAnnotation(ref reader, out var annotation) == false) + return false; + + annotations[i] = annotation; + } + + record = new ParameterAnnotationRecord(annotations); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/PermittedSubclassesAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/PermittedSubclassesAttributeRecord.cs new file mode 100644 index 0000000000..b1bd98046d --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/PermittedSubclassesAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record PermittedSubclassesAttributeRecord(ushort[] ClassIndexes) : AttributeRecord + { + + public static bool TryReadPermittedSubclassesAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var classes = new ushort[count]; + for (int i = 0; i < count; i++) + { + if (reader.TryReadU2(out ushort classIndex) == false) + return false; + + classes[i] = classIndex; + } + + attribute = new PermittedSubclassesAttributeRecord(classes); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RecordAttributeComponentRecord.cs b/src/IKVM.ByteCode/Parsing/RecordAttributeComponentRecord.cs new file mode 100644 index 0000000000..1fb3ff2162 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RecordAttributeComponentRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct RecordAttributeComponentRecord(ushort NameIndex, ushort DescriptorIndex, AttributeInfoRecord[] Attributes); + +} diff --git a/src/IKVM.ByteCode/Parsing/RecordAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RecordAttributeRecord.cs new file mode 100644 index 0000000000..192bc8ae94 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RecordAttributeRecord.cs @@ -0,0 +1,33 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RecordAttributeRecord(RecordAttributeComponentRecord[] Components) : AttributeRecord + { + + public static bool TryReadRecordAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort componentsCount) == false) + return false; + + var components = new RecordAttributeComponentRecord[componentsCount]; + for (int i = 0; i < componentsCount; i++) + { + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + if (reader.TryReadU2(out ushort descriptorIndex) == false) + return false; + if (ClassRecord.TryReadAttributes(ref reader, out var attributes) == false) + return false; + + components[i] = new RecordAttributeComponentRecord(nameIndex, descriptorIndex, attributes); + } + + attribute = new RecordAttributeRecord(components); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RefConstantRecord.cs b/src/IKVM.ByteCode/Parsing/RefConstantRecord.cs new file mode 100644 index 0000000000..1df9453358 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RefConstantRecord.cs @@ -0,0 +1,12 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record RefConstantRecord(ushort ClassIndex, ushort NameAndTypeIndex) : + ConstantRecord + { + + + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RuntimeInvisibleAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..7354beb0f1 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleAnnotationsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeInvisibleAnnotationsAttributeRecord(AnnotationRecord[] Annotations) : AttributeRecord + { + + public static bool TryReadRuntimeInvisibleAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var items = new AnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (AnnotationRecord.TryReadAnnotation(ref reader, out var annotation) == false) + return false; + + items[i] = annotation; + } + + attribute = new RuntimeInvisibleAnnotationsAttributeRecord(items); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/RuntimeInvisibleParameterAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleParameterAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..77cac86d7e --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleParameterAnnotationsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeInvisibleParameterAnnotationsAttributeRecord(ParameterAnnotationRecord[] Parameters) : AttributeRecord + { + + public static bool TryReadRuntimeInvisibleParameterAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU1(out byte count) == false) + return false; + + var parameters = new ParameterAnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (ParameterAnnotationRecord.TryReadParameterAnnotation(ref reader, out var parameter) == false) + return false; + + parameters[i] = parameter; + } + + attribute = new RuntimeInvisibleParameterAnnotationsAttributeRecord(parameters); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RuntimeInvisibleTypeAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleTypeAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..81cc47298f --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeInvisibleTypeAnnotationsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeInvisibleTypeAnnotationsAttributeRecord(TypeAnnotationRecord[] Annotations) : AttributeRecord + { + + public static bool TryReadRuntimeInvisibleTypeAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var annotations = new TypeAnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (TypeAnnotationRecord.TryReadTypeAnnotation(ref reader, out var annotation) == false) + return false; + + annotations[i] = annotation; + } + + attribute = new RuntimeInvisibleTypeAnnotationsAttributeRecord(annotations); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RuntimeVisibleAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeVisibleAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..7fb6dd7793 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeVisibleAnnotationsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeVisibleAnnotationsAttributeRecord(AnnotationRecord[] Annotations) : AttributeRecord + { + + public static bool TryReadRuntimeVisibleAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var items = new AnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (AnnotationRecord.TryReadAnnotation(ref reader, out var annotation) == false) + return false; + + items[i] = annotation; + } + + attribute = new RuntimeVisibleAnnotationsAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RuntimeVisibleParameterAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeVisibleParameterAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..5990ba25da --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeVisibleParameterAnnotationsAttributeRecord.cs @@ -0,0 +1,29 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeVisibleParameterAnnotationsAttributeRecord(ParameterAnnotationRecord[] Parameters) : AttributeRecord + { + + public static bool TryReadRuntimeVisibleParameterAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU1(out byte count) == false) + return false; + + var items = new ParameterAnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (ParameterAnnotationRecord.TryReadParameterAnnotation(ref reader, out var parameter) == false) + return false; + + items[i] = parameter; + } + + attribute = new RuntimeVisibleParameterAnnotationsAttributeRecord(items); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/RuntimeVisibleTypeAnnotationsAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/RuntimeVisibleTypeAnnotationsAttributeRecord.cs new file mode 100644 index 0000000000..fde2094726 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/RuntimeVisibleTypeAnnotationsAttributeRecord.cs @@ -0,0 +1,61 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record RuntimeVisibleTypeAnnotationsAttributeRecord(TypeAnnotationRecord[] Annotations) : AttributeRecord + { + + public static bool TryReadRuntimeVisibleTypeAnnotationsAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var annotations = new TypeAnnotationRecord[count]; + for (int i = 0; i < count; i++) + { + if (TypeAnnotationRecord.TryReadTypeAnnotation(ref reader, out var annotation) == false) + return false; + + annotations[i] = annotation; + } + + attribute = new RuntimeVisibleTypeAnnotationsAttributeRecord(annotations); + return true; + } + + /// + /// Gets the number of bytes required to write the record. + /// + /// + public int GetSize() + { + var size = 0; + size += sizeof(ushort); + + foreach (var element in Annotations) + size += element.GetSize(); + + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2((ushort)Annotations.Length) == false) + return false; + + foreach (var record in Annotations) + if (record.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/SameExtendedStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/SameExtendedStackMapFrameRecord.cs new file mode 100644 index 0000000000..00e8a0f50b --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SameExtendedStackMapFrameRecord.cs @@ -0,0 +1,21 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record SameExtendedStackMapFrameRecord(byte Tag, ushort OffsetDelta) : StackMapFrameRecord(Tag) + { + + public static bool TryReadSameExtendedStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU2(out ushort offsetDelta) == false) + return false; + + frame = new SameExtendedStackMapFrameRecord(tag, offsetDelta); + return true; + } + + } + + +} diff --git a/src/IKVM.ByteCode/Parsing/SameLocalsOneExtendedStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/SameLocalsOneExtendedStackMapFrameRecord.cs new file mode 100644 index 0000000000..60009a85b9 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SameLocalsOneExtendedStackMapFrameRecord.cs @@ -0,0 +1,22 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record SameLocalsOneExtendedStackMapFrameRecord(byte FrameType, ushort OffsetDelta, VerificationTypeInfoRecord Stack) : StackMapFrameRecord(FrameType) + { + + public static bool TryReadSameLocalsOneStackItemExtendedStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU2(out ushort offsetDelta) == false) + return false; + if (VerificationTypeInfoRecord.TryReadVerificationTypeInfo(ref reader, out var verificationTypeInfo) == false) + return false; + + frame = new SameLocalsOneExtendedStackMapFrameRecord(tag, offsetDelta, verificationTypeInfo); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/SameLocalsOneStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/SameLocalsOneStackMapFrameRecord.cs new file mode 100644 index 0000000000..7ee6b0a890 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SameLocalsOneStackMapFrameRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record SameLocalsOneStackMapFrameRecord(byte FrameType, VerificationTypeInfoRecord Stack) : StackMapFrameRecord(FrameType) + { + + public static bool TryReadSameLocalsOneStackItemStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = null; + + if (VerificationTypeInfoRecord.TryReadVerificationTypeInfo(ref reader, out var verificationTypeInfo) == false) + return false; + + frame = new SameLocalsOneStackMapFrameRecord(tag, verificationTypeInfo); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/SameStackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/SameStackMapFrameRecord.cs new file mode 100644 index 0000000000..f5e61a2ec7 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SameStackMapFrameRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record SameStackMapFrameRecord(byte Tag) : StackMapFrameRecord(Tag) + { + + public static bool TryReadSameStackMapFrame(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + frame = new SameStackMapFrameRecord(tag); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/SignatureAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/SignatureAttributeRecord.cs new file mode 100644 index 0000000000..5416870050 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SignatureAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record SignatureAttributeRecord(ushort SignatureIndex) : AttributeRecord + { + + public static bool TryReadSignatureAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort signatureIndex) == false) + return false; + + attribute = new SignatureAttributeRecord(signatureIndex); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/SourceDebugExtensionAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/SourceDebugExtensionAttributeRecord.cs new file mode 100644 index 0000000000..34ffa8e832 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SourceDebugExtensionAttributeRecord.cs @@ -0,0 +1,25 @@ +using System.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal record SourceDebugExtensionAttributeRecord(byte[] Data) : AttributeRecord + { + + public static bool TryReadSourceDebugExtensionAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadManyU1(reader.Length, out ReadOnlySequence data) == false) + return false; + + var dataBuffer = new byte[data.Length]; + data.CopyTo(dataBuffer); + + attribute = new SourceDebugExtensionAttributeRecord(dataBuffer); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/SourceFileAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/SourceFileAttributeRecord.cs new file mode 100644 index 0000000000..22d3357814 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SourceFileAttributeRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record SourceFileAttributeRecord(ushort SourceFileIndex) : AttributeRecord + { + + public static bool TryReadSourceFileAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort sourceFileIndex) == false) + return false; + + attribute = new SourceFileAttributeRecord(sourceFileIndex); + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/StackMapFrameRecord.cs b/src/IKVM.ByteCode/Parsing/StackMapFrameRecord.cs new file mode 100644 index 0000000000..dbc5cb6613 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/StackMapFrameRecord.cs @@ -0,0 +1,42 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record StackMapFrameRecord(byte FrameType) + { + + public static bool TryRead(ref ClassFormatReader reader, out StackMapFrameRecord frame) + { + frame = null; + + if (reader.TryReadU1(out byte tag) == false) + return false; + + if (TryRead(ref reader, tag, out frame) == false) + return false; + + return true; + } + + public static bool TryRead(ref ClassFormatReader reader, byte tag, out StackMapFrameRecord frame) + { + if (tag >= 0 && tag <= 63) + return SameStackMapFrameRecord.TryReadSameStackMapFrame(ref reader, tag, out frame); + else if (tag >= 64 && tag <= 127) + return SameLocalsOneStackMapFrameRecord.TryReadSameLocalsOneStackItemStackMapFrame(ref reader, tag, out frame); + else if (tag == 247) + return SameLocalsOneExtendedStackMapFrameRecord.TryReadSameLocalsOneStackItemExtendedStackMapFrame(ref reader, tag, out frame); + else if (tag >= 248 && tag <= 250) + return ChopStackMapFrameRecord.TryReadChopStackMapFrame(ref reader, tag, out frame); + else if (tag == 251) + return SameExtendedStackMapFrameRecord.TryReadSameExtendedStackMapFrame(ref reader, tag, out frame); + else if (tag >= 252 && tag <= 254) + return AppendStackMapFrameRecord.TryReadAppendStackMapFrame(ref reader, tag, out frame); + else if (tag == 255) + return FullStackMapFrameRecord.TryReadFullStackMapFrame(ref reader, tag, out frame); + else + throw new ByteCodeException($"Invalid stack map frame tag value: '{tag}'."); + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/StackMapTableAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/StackMapTableAttributeRecord.cs new file mode 100644 index 0000000000..925e394091 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/StackMapTableAttributeRecord.cs @@ -0,0 +1,25 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record StackMapTableAttributeRecord(StackMapFrameRecord[] Frames) : AttributeRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = null; + + if (reader.TryReadU2(out ushort count) == false) + return false; + + var frames = new StackMapFrameRecord[count]; + for (int i = 0; i < count; i++) + if (StackMapFrameRecord.TryRead(ref reader, out frames[i]) == false) + return false; + + attribute = new StackMapTableAttributeRecord(frames); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/StringConstantRecord.cs b/src/IKVM.ByteCode/Parsing/StringConstantRecord.cs new file mode 100644 index 0000000000..19330f9330 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/StringConstantRecord.cs @@ -0,0 +1,26 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record StringConstantRecord(ushort ValueIndex) : ConstantRecord + { + + /// + /// Parses a Class constant in the constant pool. + /// + /// + /// + public static bool TryReadStringConstant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort nameIndex) == false) + return false; + + constant = new StringConstantRecord(nameIndex); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/SyntheticAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/SyntheticAttributeRecord.cs new file mode 100644 index 0000000000..cc1846404d --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/SyntheticAttributeRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record SyntheticAttributeRecord : AttributeRecord + { + + public static bool TryReadSyntheticAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + attribute = new SyntheticAttributeRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TopVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/TopVariableInfoRecord.cs new file mode 100644 index 0000000000..bcbecce5c2 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TopVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TopVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new TopVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TypeAndNameConstantRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAndNameConstantRecord.cs new file mode 100644 index 0000000000..c797079829 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAndNameConstantRecord.cs @@ -0,0 +1,6 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAndNameConstantRecord(ushort NameIndex, ushort DescriptorIndex) : ConstantRecord; + +} diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationCatchTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationCatchTargetRecord.cs new file mode 100644 index 0000000000..8b0210b532 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationCatchTargetRecord.cs @@ -0,0 +1,44 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationCatchTargetRecord(ushort ExceptionTableIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort exceptionTableIndex) == false) + return false; + + targetInfo = new TypeAnnotationCatchTargetRecord(exceptionTableIndex); + return true; + } + + /// + /// Gets the size of the record if written. + /// + /// + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(ExceptionTableIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationEmptyTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationEmptyTargetRecord.cs new file mode 100644 index 0000000000..99fe7fcff6 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationEmptyTargetRecord.cs @@ -0,0 +1,30 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationEmptyTargetRecord : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = new TypeAnnotationEmptyTargetRecord(); + return true; + } + + public override int GetSize() + { + return 0; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationFormalParameterTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationFormalParameterTargetRecord.cs new file mode 100644 index 0000000000..90c9e095e6 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationFormalParameterTargetRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationFormalParameterTargetRecord(byte ParameterIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU1(out byte parameterIndex) == false) + return false; + + targetInfo = new TypeAnnotationFormalParameterTargetRecord(parameterIndex); + return true; + } + + public override int GetSize() + { + var length = 0; + length += sizeof(byte); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1(ParameterIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetItemRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetItemRecord.cs new file mode 100644 index 0000000000..e76d10bafe --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetItemRecord.cs @@ -0,0 +1,56 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct TypeAnnotationLocalVarTargetItemRecord(ushort Offset, ushort Length, ushort Index) + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationLocalVarTargetItemRecord record) + { + record = default; + + if (reader.TryReadU2(out ushort offset) == false) + return false; + + if (reader.TryReadU2(out ushort length) == false) + return false; + + if (reader.TryReadU2(out ushort index) == false) + return false; + + record = new TypeAnnotationLocalVarTargetItemRecord(offset, length, index); + return true; + } + + /// + /// Gets the size of the record if written. + /// + /// + public int GetSize() + { + var length = 0; + length += sizeof(ushort); + length += sizeof(ushort); + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(Offset) == false) + return false; + if (writer.TryWriteU2(Length) == false) + return false; + if (writer.TryWriteU2(Index) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetRecord.cs new file mode 100644 index 0000000000..ef82fc5c03 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationLocalVarTargetRecord.cs @@ -0,0 +1,57 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationLocalVarTargetRecord(TypeAnnotationLocalVarTargetItemRecord[] Items) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort length) == false) + return false; + + var items = new TypeAnnotationLocalVarTargetItemRecord[length]; + for (int i = 0; i < length; i++) + if (TypeAnnotationLocalVarTargetItemRecord.TryRead(ref reader, out items[i]) == false) + return false; + + targetInfo = new TypeAnnotationLocalVarTargetRecord(items); + return true; + } + + /// + /// Gets the size of the record if written. + /// + /// + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + + foreach (var item in Items) + length += item.GetSize(); + + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2((ushort)Items.Length) == false) + return false; + + foreach (var item in Items) + if (item.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationOffsetTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationOffsetTargetRecord.cs new file mode 100644 index 0000000000..4babfae72c --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationOffsetTargetRecord.cs @@ -0,0 +1,44 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationOffsetTargetRecord(ushort Offset) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort offset) == false) + return false; + + targetInfo = new TypeAnnotationCatchTargetRecord(offset); + return true; + } + + /// + /// Gets the size of the record if written. + /// + /// + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(Offset) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterBoundTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterBoundTargetRecord.cs new file mode 100644 index 0000000000..c11930bfb8 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterBoundTargetRecord.cs @@ -0,0 +1,45 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationParameterBoundTargetRecord(byte ParameterIndex, byte BoundIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU1(out byte parameterIndex) == false) + return false; + if (reader.TryReadU1(out byte boundIndex) == false) + return false; + + targetInfo = new TypeAnnotationParameterBoundTargetRecord(parameterIndex, boundIndex); + return true; + } + + public override int GetSize() + { + var length = 0; + length += sizeof(byte); + length += sizeof(byte); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1(ParameterIndex) == false) + return false; + if (writer.TryWriteU1(BoundIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterTargetRecord.cs new file mode 100644 index 0000000000..37e31c886d --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationParameterTargetRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationParameterTargetRecord(byte ParameterIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU1(out byte parameterIndex) == false) + return false; + + targetInfo = new TypeAnnotationParameterTargetRecord( parameterIndex); + return true; + } + + public override int GetSize() + { + var length = 0; + length += sizeof(byte); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1(ParameterIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationRecord.cs new file mode 100644 index 0000000000..01fa9b68e3 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationRecord.cs @@ -0,0 +1,104 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct TypeAnnotationRecord(TypeAnnotationTargetType TargetType, TypeAnnotationTargetRecord Target, TypePathRecord TargetPath, ushort TypeIndex, params ElementValuePairRecord[] Elements) + { + + public static bool TryReadTypeAnnotation(ref ClassFormatReader reader, out TypeAnnotationRecord annotation) + { + annotation = default; + + if (reader.TryReadU1(out byte targetType) == false) + return false; + if (TryReadTarget(ref reader, (TypeAnnotationTargetType)targetType, out var target) == false) + return false; + if (TypePathRecord.TryRead(ref reader, out var targetPath) == false) + return false; + if (reader.TryReadU2(out ushort typeIndex) == false) + return false; + if (reader.TryReadU2(out ushort pairCount) == false) + return false; + + var elements = new ElementValuePairRecord[pairCount]; + for (int i = 0; i < pairCount; i++) + if (ElementValuePairRecord.TryRead(ref reader, out elements[i]) == false) + return false; + + annotation = new TypeAnnotationRecord((TypeAnnotationTargetType)targetType, target, targetPath, typeIndex, elements); + return true; + } + + public static bool TryReadTarget(ref ClassFormatReader reader, TypeAnnotationTargetType targetType, out TypeAnnotationTargetRecord targetInfo) => targetType switch + { + TypeAnnotationTargetType.ClassTypeParameter => TypeAnnotationParameterTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodTypeParameter => TypeAnnotationParameterTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ClassExtends => TypeAnnotationSuperTypeTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ClassTypeParameterBound => TypeAnnotationParameterBoundTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodTypeParameterBound => TypeAnnotationParameterBoundTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.Field => TypeAnnotationEmptyTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodReturn => TypeAnnotationEmptyTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodReceiver => TypeAnnotationEmptyTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodFormalParameter => TypeAnnotationFormalParameterTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.Throws => TypeAnnotationThrowsTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.LocalVariable => TypeAnnotationLocalVarTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ResourceVariable => TypeAnnotationLocalVarTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ExceptionParameter => TypeAnnotationCatchTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.InstanceOf => TypeAnnotationOffsetTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.New => TypeAnnotationOffsetTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ConstructorReference => TypeAnnotationOffsetTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodReference => TypeAnnotationOffsetTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.Cast => TypeAnnotationTypeArgumentTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ConstructorInvocationTypeArgument => TypeAnnotationTypeArgumentTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodInvocationTypeArgument => TypeAnnotationTypeArgumentTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.ConstructorReferenceTypeArgument => TypeAnnotationTypeArgumentTargetRecord.TryRead(ref reader, out targetInfo), + TypeAnnotationTargetType.MethodReferenceTypeArgument => TypeAnnotationTypeArgumentTargetRecord.TryRead(ref reader, out targetInfo), + _ => throw new ByteCodeException($"Invalid type annotation target type: '0x{targetType:X}'."), + }; + + /// + /// Gets the number of bytes required to write the record. + /// + /// + public int GetSize() + { + var size = 0; + size += sizeof(byte); + size += Target.GetSize(); + size += TargetPath.GetSize(); + size += sizeof(ushort); + size += sizeof(ushort); + + for (int i = 0; i < Elements.Length; i++) + size += Elements[i].GetSize(); + + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1((byte)TargetType) == false) + return false; + if (Target.TryWrite(ref writer) == false) + return false; + if (TargetPath.TryWrite(ref writer) == false) + return false; + if (writer.TryWriteU2(TypeIndex) == false) + return false; + if (writer.TryWriteU2((ushort)Elements.Length) == false) + return false; + + foreach (var record in Elements) + if (record.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationSuperTypeTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationSuperTypeTargetRecord.cs new file mode 100644 index 0000000000..bfe06378a0 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationSuperTypeTargetRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationSuperTypeTargetRecord(ushort SuperTypeIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort superTypeIndex) == false) + return false; + + targetInfo = new TypeAnnotationSuperTypeTargetRecord(superTypeIndex); + return true; + } + + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(SuperTypeIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetRecord.cs new file mode 100644 index 0000000000..71df0c3a77 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetRecord.cs @@ -0,0 +1,22 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record TypeAnnotationTargetRecord + { + + /// + /// Returns the size of the record when written. + /// + /// + public abstract int GetSize(); + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public abstract bool TryWrite(ref ClassFormatWriter writer); + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetType.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetType.cs new file mode 100644 index 0000000000..4c9fbba312 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationTargetType.cs @@ -0,0 +1,32 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal enum TypeAnnotationTargetType : byte + { + + ClassTypeParameter = 0x00, + MethodTypeParameter = 0x01, + ClassExtends = 0x10, + ClassTypeParameterBound = 0x11, + MethodTypeParameterBound = 0x12, + Field = 0x13, + MethodReturn = 0x14, + MethodReceiver = 0x15, + MethodFormalParameter = 0x16, + Throws = 0x17, + LocalVariable = 0x40, + ResourceVariable = 0x41, + ExceptionParameter = 0x42, + InstanceOf = 0x43, + New = 0x44, + ConstructorReference = 0x45, + MethodReference = 0x46, + Cast = 0x47, + ConstructorInvocationTypeArgument = 0x48, + MethodInvocationTypeArgument = 0x49, + ConstructorReferenceTypeArgument = 0x4A, + MethodReferenceTypeArgument = 0x4B, + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationThrowsTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationThrowsTargetRecord.cs new file mode 100644 index 0000000000..cd5aa44d8f --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationThrowsTargetRecord.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationThrowsTargetRecord(ushort ThrowsTypeIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort throwsTypeIndex) == false) + return false; + + targetInfo = new TypeAnnotationThrowsTargetRecord(throwsTypeIndex); + return true; + } + + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(ThrowsTypeIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypeAnnotationTypeArgumentTargetRecord.cs b/src/IKVM.ByteCode/Parsing/TypeAnnotationTypeArgumentTargetRecord.cs new file mode 100644 index 0000000000..69eb461987 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypeAnnotationTypeArgumentTargetRecord.cs @@ -0,0 +1,49 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record TypeAnnotationTypeArgumentTargetRecord(ushort Offset, ushort TypeArgumentIndex) : TypeAnnotationTargetRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out TypeAnnotationTargetRecord targetInfo) + { + targetInfo = null; + + if (reader.TryReadU2(out ushort offset) == false) + return false; + if (reader.TryReadU2(out ushort typeArgumentIndex) == false) + return false; + + targetInfo = new TypeAnnotationTypeArgumentTargetRecord(offset, typeArgumentIndex); + return true; + } + + /// + /// Gets the size of the record if written. + /// + /// + public override int GetSize() + { + var length = 0; + length += sizeof(ushort); + length += sizeof(ushort); + return length; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public override bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU2(Offset) == false) + return false; + if (writer.TryWriteU2(TypeArgumentIndex) == false) + return false; + + return true; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Parsing/TypePathItemRecord.cs b/src/IKVM.ByteCode/Parsing/TypePathItemRecord.cs new file mode 100644 index 0000000000..f67f1baebe --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypePathItemRecord.cs @@ -0,0 +1,45 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct TypePathItemRecord(TypePathKind Kind, byte ArgumentIndex) + { + + public static bool TryRead(ref ClassFormatReader reader, out TypePathItemRecord record) + { + record = default; + + if (reader.TryReadU1(out byte kind) == false) + return false; + if (reader.TryReadU1(out byte argumentIndex) == false) + return false; + + record = new TypePathItemRecord((TypePathKind)kind, argumentIndex); + return true; + } + + public int GetSize() + { + var size = 0; + size += sizeof(byte); + size += sizeof(byte); + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1((byte)Kind) == false) + return false; + if (writer.TryWriteU1(ArgumentIndex) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/TypePathRecord.cs b/src/IKVM.ByteCode/Parsing/TypePathRecord.cs new file mode 100644 index 0000000000..b351bc464a --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/TypePathRecord.cs @@ -0,0 +1,57 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal record struct TypePathRecord(params TypePathItemRecord[] Path) + { + + public static bool TryRead(ref ClassFormatReader reader, out TypePathRecord typePath) + { + typePath = default; + + if (reader.TryReadU1(out byte length) == false) + return false; + + var path = new TypePathItemRecord[length]; + for (int i = 0; i < length; i++) + { + if (TypePathItemRecord.TryRead(ref reader, out var item) == false) + return false; + + path[i] = item; + } + + typePath = new TypePathRecord(path); + return true; + } + + public int GetSize() + { + var size = 0; + size += sizeof(byte); + + foreach (var item in Path) + size += item.GetSize(); + + return size; + } + + /// + /// Attempts to write the record to the given . + /// + /// + /// + public bool TryWrite(ref ClassFormatWriter writer) + { + if (writer.TryWriteU1((byte)Path.Length) == false) + return false; + + foreach (var item in Path) + if (item.TryWrite(ref writer) == false) + return false; + + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/UninitializedThisVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/UninitializedThisVariableInfoRecord.cs new file mode 100644 index 0000000000..6b52843f36 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/UninitializedThisVariableInfoRecord.cs @@ -0,0 +1,15 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record UninitializedThisVariableInfoRecord : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = new UninitializedThisVariableInfoRecord(); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/UninitializedVariableInfoRecord.cs b/src/IKVM.ByteCode/Parsing/UninitializedVariableInfoRecord.cs new file mode 100644 index 0000000000..8a16e8df7c --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/UninitializedVariableInfoRecord.cs @@ -0,0 +1,20 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record UninitializedVariableInfoRecord(ushort Offset) : VerificationTypeInfoRecord + { + + public static bool TryRead(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = null; + + if (reader.TryReadU2(out ushort offset) == false) + return false; + + record = new UninitializedVariableInfoRecord(offset); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/UnknownAttributeRecord.cs b/src/IKVM.ByteCode/Parsing/UnknownAttributeRecord.cs new file mode 100644 index 0000000000..50d2949772 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/UnknownAttributeRecord.cs @@ -0,0 +1,18 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal sealed record UnknownAttributeRecord(byte[] Data) : AttributeRecord + { + + public static bool TryReadCustomAttribute(ref ClassFormatReader reader, out AttributeRecord attribute) + { + var data = new byte[reader.Length]; + reader.TryCopyTo(data); + + attribute = new UnknownAttributeRecord(data); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/Utf8ConstantRecord.cs b/src/IKVM.ByteCode/Parsing/Utf8ConstantRecord.cs new file mode 100644 index 0000000000..548db6b53f --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/Utf8ConstantRecord.cs @@ -0,0 +1,33 @@ +using System.Buffers; + +namespace IKVM.ByteCode.Parsing +{ + + internal record class Utf8ConstantRecord(byte[] Value) : ConstantRecord + { + + /// + /// Parses a UTF8 constant in the constant pool. + /// + /// + /// + public static bool TryReadUtf8Constant(ref ClassFormatReader reader, out ConstantRecord constant, out int skip) + { + constant = null; + skip = 0; + + if (reader.TryReadU2(out ushort length) == false) + return false; + if (reader.TryReadManyU1(length, out ReadOnlySequence value) == false) + return false; + + var valueBuffer = new byte[value.Length]; + value.CopyTo(valueBuffer); + + constant = new Utf8ConstantRecord(valueBuffer); + return true; + } + + } + +} diff --git a/src/IKVM.ByteCode/Parsing/VerificationTypeInfoRecord.cs b/src/IKVM.ByteCode/Parsing/VerificationTypeInfoRecord.cs new file mode 100644 index 0000000000..51a5e5c7c0 --- /dev/null +++ b/src/IKVM.ByteCode/Parsing/VerificationTypeInfoRecord.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode.Parsing +{ + + internal abstract record VerificationTypeInfoRecord + { + + public static bool TryReadVerificationTypeInfo(ref ClassFormatReader reader, out VerificationTypeInfoRecord record) + { + record = null; + + if (reader.TryReadU1(out byte tag) == false) + return false; + + return tag switch + { + 0 => TopVariableInfoRecord.TryRead(ref reader, out record), + 1 => IntegerVariableInfoRecord.TryRead(ref reader, out record), + 2 => FloatVariableInfoRecord.TryRead(ref reader, out record), + 3 => DoubleVariableInfoRecord.TryRead(ref reader, out record), + 4 => LongVariableInfoRecord.TryRead(ref reader, out record), + 5 => NullVariableInfoRecord.TryRead(ref reader, out record), + 6 => UninitializedThisVariableInfoRecord.TryRead(ref reader, out record), + 7 => ObjectVariableInfoRecord.TryRead(ref reader, out record), + 8 => UninitializedVariableInfoRecord.TryRead(ref reader, out record), + _ => throw new ByteCodeException($"Invalid verification info tag: '{tag}'."), + }; + } + + } + +} diff --git a/src/IKVM.ByteCode/README.md b/src/IKVM.ByteCode/README.md new file mode 100644 index 0000000000..22312bf091 --- /dev/null +++ b/src/IKVM.ByteCode/README.md @@ -0,0 +1,20 @@ +# IKVM.ByteCode + +Provides a Java class file parser, reader and writer implementation used by the IKVM project. + +The project is organized into three related sections. + +## IKVM.ByteCode.Parsing + +Contains Record structures and classes that represent the raw values that might appear in a Java class file. These +types are not blittable, but do contain a TryRead method and TryWrite method, targeting a ClassFormatReader and +ClassFormatWriter respectively. + +## IVKM.ByteCode.Reading + +Contains Reader classes that wrap Record structures. The main top-level class is ClassReader. ClassReader operates on +a ClassRecord. ClassReader exposes collections and properties which allow navigation through a parsed class file. An +attempt is made to only initialize data from the underlying records on demand. + +The Readers allow you to navigate through the constant pool, resolve constant values, and navigate through the +interfaces, fields, methods and attributes of the class. diff --git a/src/IKVM.ByteCode/Reading/AnnotationDefaultAttributeReader.cs b/src/IKVM.ByteCode/Reading/AnnotationDefaultAttributeReader.cs new file mode 100644 index 0000000000..fe036cbef3 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AnnotationDefaultAttributeReader.cs @@ -0,0 +1,29 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class AnnotationDefaultAttributeReader : AttributeReader + { + + ElementValueReader defaultValue; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal AnnotationDefaultAttributeReader(ClassReader declaringClass, AttributeInfoReader info, AnnotationDefaultAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + public ElementValueReader DefaultValue => LazyGet(ref defaultValue, () => ElementValueReader.Resolve(DeclaringClass, Record.DefaultValue)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/AnnotationReader.cs b/src/IKVM.ByteCode/Reading/AnnotationReader.cs new file mode 100644 index 0000000000..5a9b50c55a --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AnnotationReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class AnnotationReader : ReaderBase + { + + Utf8ConstantReader type; + ElementValueKeyReaderCollection elements; + + /// + /// Initializes a new instance. + /// + /// + /// + public AnnotationReader(ClassReader declaringClass, AnnotationRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the type of the annotation. + /// + public Utf8ConstantReader Type => LazyGet(ref type, () => DeclaringClass.Constants.Get(Record.TypeIndex)); + + /// + /// Gets the element values of the annotation. + /// + public ElementValueKeyReaderCollection Elements => LazyGet(ref elements, () => new ElementValueKeyReaderCollection(DeclaringClass, Record.Elements)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/AnnotationReaderCollection.cs b/src/IKVM.ByteCode/Reading/AnnotationReaderCollection.cs new file mode 100644 index 0000000000..b132856a6e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AnnotationReaderCollection.cs @@ -0,0 +1,36 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of annotation data. + /// + internal sealed class AnnotationReaderCollection : LazyReaderList + { + + /// + /// Initializes a new instance. + /// + /// + /// + public AnnotationReaderCollection(ClassReader declaringClass, AnnotationRecord[] records) : + base(declaringClass, records) + { + + } + + /// + /// Creates a new reader. + /// + /// + /// + /// + protected override AnnotationReader CreateReader(int index, AnnotationRecord record) + { + return new AnnotationReader(DeclaringClass, record); + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/AttributeInfoReader.cs b/src/IKVM.ByteCode/Reading/AttributeInfoReader.cs new file mode 100644 index 0000000000..dc6403d415 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AttributeInfoReader.cs @@ -0,0 +1,36 @@ +using System; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class AttributeInfoReader : ReaderBase + { + + Utf8ConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + /// + internal AttributeInfoReader(ClassReader declaringClass, AttributeInfoRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the name of the attribute. + /// + public Utf8ConstantReader Name => name ??= DeclaringClass.Constants.Get(Record.NameIndex); + + /// + /// Gets the data of the attribute. + /// + public ReadOnlyMemory Data => Record.Data; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/AttributeReader.cs b/src/IKVM.ByteCode/Reading/AttributeReader.cs new file mode 100644 index 0000000000..a87b5ecaaf --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AttributeReader.cs @@ -0,0 +1,64 @@ +using System; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal abstract class AttributeReader : ReaderBase + { + + readonly AttributeInfoReader info; + readonly AttributeRecord record; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal AttributeReader(ClassReader declaringClass, AttributeInfoReader info, AttributeRecord record) : + base(declaringClass) + { + this.info = info ?? throw new ArgumentNullException(nameof(info)); + this.record = record ?? throw new ArgumentNullException(nameof(record)); + } + + /// + /// Gets the information about the attribute. + /// + public AttributeInfoReader Info => info; + + /// + /// Gets the underlying record of the attribute. + /// + public AttributeRecord Record => record; + + } + + internal abstract class AttributeReader : AttributeReader + where TRecord : AttributeRecord + { + + readonly TRecord record; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal AttributeReader(ClassReader declaringClass, AttributeInfoReader info, TRecord record) : + base(declaringClass, info, record) + { + this.record = record ?? throw new ArgumentNullException(nameof(record)); + } + + /// + /// Gets the underlying data record of the attribute. + /// + public new TRecord Record => record; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/AttributeReaderCollection.cs b/src/IKVM.ByteCode/Reading/AttributeReaderCollection.cs new file mode 100644 index 0000000000..f5356e78f9 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/AttributeReaderCollection.cs @@ -0,0 +1,196 @@ +using System.Collections.Generic; +using System.Linq; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of attribute data. + /// + internal sealed class AttributeReaderCollection : LazyReaderList + { + + ConstantValueAttributeReader constantValueAttribute; + CodeAttributeReader codeAttribute; + StackMapTableAttributeReader stackMapTableAttribute; + ExceptionsAttributeReader exceptionsAttribute; + InnerClassesAttributeReader innerClassesAttribute; + EnclosingMethodAttributeReader enclosingMethodAttribute; + SyntheticAttributeReader syntheticAttribute; + SignatureAttributeReader signatureAttribute; + SourceFileAttributeReader sourceFileAttribute; + SourceDebugExtensionAttributeReader sourceDebugExtensionAttribute; + LineNumberTableAttributeReader lineNumberTableAttribute; + LocalVariableTableAttributeReader localVariableTableAttribute; + LocalVariableTypeTableAttributeReader localVariableTypeTableAttribute; + DeprecatedAttributeReader deprecatedAttribute; + RuntimeVisibleAnnotationsAttributeReader runtimeVisibleAnnotationsAttribute; + RuntimeInvisibleAnnotationsAttributeReader runtimeInvisibleAnnotationsAttribute; + RuntimeVisibleParameterAnnotationsAttributeReader runtimeVisibleParameterAnnotationsAttribute; + RuntimeInvisibleParameterAnnotationsAttributeReader runtimeInvisibleParameterAnnotationsAttribute; + RuntimeVisibleTypeAnnotationsAttributeReader runtimeVisibleTypeAnnotationsAttribute; + RuntimeInvisibleTypeAnnotationsAttributeReader runtimeInvisibleTypeAnnotationsAttribute; + AnnotationDefaultAttributeReader annotationDefaultAttribute; + BootstrapMethodsAttributeReader bootstrapMethodsAttribute; + MethodParametersAttributeReader methodParametersAttribute; + ModuleAttributeReader moduleAttribute; + ModulePackagesAttributeReader modulePackagesAttribute; + ModuleMainClassAttributeReader moduleMainClassAttribute; + NestHostAttributeReader nestHostAttribute; + NestMembersAttributeReader nestMembersAttribute; + RecordAttributeReader recordAttribute; + PermittedSubclassesAttributeReader permittedSubclassesAttribute; + + /// + /// Initializes a new instance. + /// + /// + /// + public AttributeReaderCollection(ClassReader declaringClass, AttributeInfoRecord[] records) : + base(declaringClass, records) + { + + } + + /// + /// Creates a new reader. + /// + /// + /// + /// + /// + protected override AttributeReader CreateReader(int index, AttributeInfoRecord record) + { + var info = new AttributeInfoReader(DeclaringClass, record); + if (AttributeRecord.TryRead(info, out var attribute) == false) + throw new ByteCodeException("Unable to read attribute data."); + + return attribute switch + { + ConstantValueAttributeRecord d => new ConstantValueAttributeReader(DeclaringClass, info, d), + CodeAttributeRecord d => new CodeAttributeReader(DeclaringClass, info, d), + StackMapTableAttributeRecord d => new StackMapTableAttributeReader(DeclaringClass, info, d), + ExceptionsAttributeRecord d => new ExceptionsAttributeReader(DeclaringClass, info, d), + InnerClassesAttributeRecord d => new InnerClassesAttributeReader(DeclaringClass, info, d), + EnclosingMethodAttributeRecord d => new EnclosingMethodAttributeReader(DeclaringClass, info, d), + SyntheticAttributeRecord d => new SyntheticAttributeReader(DeclaringClass, info, d), + SignatureAttributeRecord d => new SignatureAttributeReader(DeclaringClass, info, d), + SourceFileAttributeRecord d => new SourceFileAttributeReader(DeclaringClass, info, d), + SourceDebugExtensionAttributeRecord d => new SourceDebugExtensionAttributeReader(DeclaringClass, info, d), + LineNumberTableAttributeRecord d => new LineNumberTableAttributeReader(DeclaringClass, info, d), + LocalVariableTableAttributeRecord d => new LocalVariableTableAttributeReader(DeclaringClass, info, d), + LocalVariableTypeTableAttributeRecord d => new LocalVariableTypeTableAttributeReader(DeclaringClass, info, d), + DeprecatedAttributeRecord d => new DeprecatedAttributeReader(DeclaringClass, info, d), + RuntimeVisibleAnnotationsAttributeRecord d => new RuntimeVisibleAnnotationsAttributeReader(DeclaringClass, info, d), + RuntimeInvisibleAnnotationsAttributeRecord d => new RuntimeInvisibleAnnotationsAttributeReader(DeclaringClass, info, d), + RuntimeVisibleParameterAnnotationsAttributeRecord d => new RuntimeVisibleParameterAnnotationsAttributeReader(DeclaringClass, info, d), + RuntimeInvisibleParameterAnnotationsAttributeRecord d => new RuntimeInvisibleParameterAnnotationsAttributeReader(DeclaringClass, info, d), + RuntimeVisibleTypeAnnotationsAttributeRecord d => new RuntimeVisibleTypeAnnotationsAttributeReader(DeclaringClass, info, d), + RuntimeInvisibleTypeAnnotationsAttributeRecord d => new RuntimeInvisibleTypeAnnotationsAttributeReader(DeclaringClass, info, d), + AnnotationDefaultAttributeRecord d => new AnnotationDefaultAttributeReader(DeclaringClass, info, d), + BootstrapMethodsAttributeRecord d => new BootstrapMethodsAttributeReader(DeclaringClass, info, d), + MethodParametersAttributeRecord d => new MethodParametersAttributeReader(DeclaringClass, info, d), + ModuleAttributeRecord d => new ModuleAttributeReader(DeclaringClass, info, d), + ModulePackagesAttributeRecord d => new ModulePackagesAttributeReader(DeclaringClass, info, d), + ModuleMainClassAttributeRecord d => new ModuleMainClassAttributeReader(DeclaringClass, info, d), + NestHostAttributeRecord d => new NestHostAttributeReader(DeclaringClass, info, d), + NestMembersAttributeRecord d => new NestMembersAttributeReader(DeclaringClass, info, d), + RecordAttributeRecord d => new RecordAttributeReader(DeclaringClass, info, d), + PermittedSubclassesAttributeRecord d => new PermittedSubclassesAttributeReader(DeclaringClass, info, d), + UnknownAttributeRecord d => new UnknownAttributeReader(DeclaringClass, info, d), + _ => throw new ByteCodeException("Cannot resolve attribute data."), + }; + } + + /// + /// Gets the attribute with the specified name. + /// + /// + /// + public AttributeReader this[string name] => this.FirstOrDefault(i => i.Info.Name.Value == name) ?? throw new KeyNotFoundException(); + + /// + /// Returns true if an attribute with the specified name exists. + /// + /// + /// + public bool Contains(string name) => this.Any(i => i.Info.Name.Value == name); + + /// + /// Attempts to get the attribute with the specified name. + /// + /// + /// + public bool TryGet(out TAttribute value) + { + value = this.OfType().FirstOrDefault(); + return value != null; + } + + public ConstantValueAttributeReader ConstantValue => LazyGet(ref constantValueAttribute, () => TryGet(out var attribute) ? attribute : null); + + public CodeAttributeReader Code => LazyGet(ref codeAttribute, () => TryGet(out var attribute) ? attribute : null); + + public StackMapTableAttributeReader StackMapTable => LazyGet(ref stackMapTableAttribute, () => TryGet(out var attribute) ? attribute : null); + + public ExceptionsAttributeReader Exceptions => LazyGet(ref exceptionsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public InnerClassesAttributeReader InnerClasses => LazyGet(ref innerClassesAttribute, () => TryGet(out var attribute) ? attribute : null); + + public EnclosingMethodAttributeReader EnclosingMethod => LazyGet(ref enclosingMethodAttribute, () => TryGet(out var attribute) ? attribute : null); + + public SyntheticAttributeReader Synthetic => LazyGet(ref syntheticAttribute, () => TryGet(out var attribute) ? attribute : null); + + public SignatureAttributeReader Signature => LazyGet(ref signatureAttribute, () => TryGet(out var attribute) ? attribute : null); + + public SourceFileAttributeReader SourceFile => LazyGet(ref sourceFileAttribute, () => TryGet(out var attribute) ? attribute : null); + + public SourceDebugExtensionAttributeReader SourceDebugExtension => LazyGet(ref sourceDebugExtensionAttribute, () => TryGet(out var attribute) ? attribute : null); + + public LineNumberTableAttributeReader LineNumberTable => LazyGet(ref lineNumberTableAttribute, () => TryGet(out var attribute) ? attribute : null); + + public LocalVariableTableAttributeReader LocalVariableTable => LazyGet(ref localVariableTableAttribute, () => TryGet(out var attribute) ? attribute : null); + + public LocalVariableTypeTableAttributeReader LocalVariableTypeTable => LazyGet(ref localVariableTypeTableAttribute, () => TryGet(out var attribute) ? attribute : null); + + public DeprecatedAttributeReader Deprecated => LazyGet(ref deprecatedAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeVisibleAnnotationsAttributeReader RuntimeVisibleAnnotations => LazyGet(ref runtimeVisibleAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeInvisibleAnnotationsAttributeReader RuntimeInvisibleAnnotations => LazyGet(ref runtimeInvisibleAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeVisibleParameterAnnotationsAttributeReader RuntimeVisibleParameterAnnotations => LazyGet(ref runtimeVisibleParameterAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeInvisibleParameterAnnotationsAttributeReader RuntimeInvisibleParameterAnnotations => LazyGet(ref runtimeInvisibleParameterAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeVisibleTypeAnnotationsAttributeReader RuntimeVisibleTypeAnnotations => LazyGet(ref runtimeVisibleTypeAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RuntimeInvisibleTypeAnnotationsAttributeReader RuntimeInvisibleTypeAnnotations => LazyGet(ref runtimeInvisibleTypeAnnotationsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public AnnotationDefaultAttributeReader AnnotationDefault => LazyGet(ref annotationDefaultAttribute, () => TryGet(out var attribute) ? attribute : null); + + public BootstrapMethodsAttributeReader BootstrapMethods => LazyGet(ref bootstrapMethodsAttribute, () => TryGet(out var attribute) ? attribute : null); + + public MethodParametersAttributeReader MethodParameters => LazyGet(ref methodParametersAttribute, () => TryGet(out var attribute) ? attribute : null); + + public ModuleAttributeReader Module => LazyGet(ref moduleAttribute, () => TryGet(out var attribute) ? attribute : null); + + public ModulePackagesAttributeReader ModulePackages => LazyGet(ref modulePackagesAttribute, () => TryGet(out var attribute) ? attribute : null); + + public ModuleMainClassAttributeReader ModuleMainClass => LazyGet(ref moduleMainClassAttribute, () => TryGet(out var attribute) ? attribute : null); + + public NestHostAttributeReader NestHost => LazyGet(ref nestHostAttribute, () => TryGet(out var attribute) ? attribute : null); + + public NestMembersAttributeReader NestMembers => LazyGet(ref nestMembersAttribute, () => TryGet(out var attribute) ? attribute : null); + + public RecordAttributeReader Record => LazyGet(ref recordAttribute, () => TryGet(out var attribute) ? attribute : null); + + public PermittedSubclassesAttributeReader PermittedSubclasses => LazyGet(ref permittedSubclassesAttribute, () => TryGet(out var attribute) ? attribute : null); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs new file mode 100644 index 0000000000..9d8be36f51 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class BootstrapMethodsAttributeMethodReader : ReaderBase + { + + MethodrefConstantReader methodref; + IReadOnlyList arguments; + + /// + /// Initializes a new instance. + /// + /// + /// + public BootstrapMethodsAttributeMethodReader(ClassReader declaringClass, BootstrapMethodsAttributeMethodRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the method being referenced. + /// + public MethodrefConstantReader Methodref => LazyGet(ref methodref, () => DeclaringClass.Constants.Get(Record.MethodRefIndex)); + + /// + /// Gets the arguments bound to the method reference. + /// + public IReadOnlyList Arguments => LazyGet(ref arguments, () => new DelegateLazyReaderList(DeclaringClass, Record.Arguments, (_, index) => DeclaringClass.Constants[index])); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeReader.cs b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeReader.cs new file mode 100644 index 0000000000..ee487fb355 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeReader.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class BootstrapMethodsAttributeReader : AttributeReader + { + + IReadOnlyList methods; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal BootstrapMethodsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, BootstrapMethodsAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the set of methods described by this bootstrap attribute. + /// + public IReadOnlyList Methods => LazyGet(ref methods, () => new DelegateLazyReaderList(DeclaringClass, Record.Methods, (_, record) => new BootstrapMethodsAttributeMethodReader(DeclaringClass, record))); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ClassConstantReader.cs b/src/IKVM.ByteCode/Reading/ClassConstantReader.cs new file mode 100644 index 0000000000..de2ae10291 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ClassConstantReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ClassConstantReader : ConstantReader + { + + Utf8ConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public ClassConstantReader(ClassReader declaringClass, ushort index, ClassConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the name of the class. + /// + public Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Returns whether or not this constant is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(49, 0); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ClassReader.cs b/src/IKVM.ByteCode/Reading/ClassReader.cs new file mode 100644 index 0000000000..aaa90c09d9 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ClassReader.cs @@ -0,0 +1,252 @@ +using System; +using System.Buffers; +using System.IO; +using System.IO.Pipelines; +using System.Threading; +using System.Threading.Tasks; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Provides stateful operations for reading a class file. + /// + internal sealed class ClassReader : ReaderBase + { + + /// + /// Attempts to read a class from the given buffer. + /// + /// + /// + /// + public static bool TryRead(ReadOnlyMemory buffer, out ClassReader clazz) + { + return TryRead(new ReadOnlySequence(buffer), out clazz); + } + + /// + /// Attempts to read a class from the given buffer. + /// + /// + /// + /// + public static ClassReader Read(ReadOnlyMemory buffer) + { + return TryRead(buffer, out var clazz) ? clazz : throw new ByteCodeException("Failed to open ClassReader. Incomplete class data."); + } + + /// + /// Attempts to read a class from the given buffer. + /// + /// + /// + /// + public static bool TryRead(in ReadOnlySequence buffer, out ClassReader clazz) + { + var reader = new ClassFormatReader(buffer); + return TryRead(ref reader, out clazz); + } + + /// + /// Attempts to read a class from the given buffer. + /// + /// + /// + /// + public static ClassReader Read(in ReadOnlySequence buffer) + { + var reader = new ClassFormatReader(buffer); + return TryRead(ref reader, out var clazz) ? clazz : throw new ByteCodeException("Failed to open ClassReader. Incomplete class data."); + } + + /// + /// Attempts to read a class from the given reader. + /// + /// + /// + /// + /// + public static bool TryRead(ref ClassFormatReader reader, out ClassReader clazz) + { + clazz = null; + + if (ClassRecord.TryRead(ref reader, out var record) == false) + return false; + + clazz = new ClassReader(record); + return true; + } + + /// + /// Reads the next class from the stream. + /// + /// + public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken = default) + { + if (stream is null) + throw new ArgumentNullException(nameof(stream)); + + var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(minimumReadSize: 1, leaveOpen: true)); + + try + { + return await ReadAsync(reader, cancellationToken); + } + catch (Exception e) + { + await reader.CompleteAsync(e); + throw; + } + finally + { + await reader.CompleteAsync(); + } + } + + /// + /// Reads the next class from the stream. + /// + /// + public static async Task ReadAsync(PipeReader reader, CancellationToken cancellationToken = default) + { + while (true) + { + var result = await reader.ReadAtLeastAsync(30, cancellationToken); + if (result.IsCanceled) + throw new OperationCanceledException(); + + // attempt to read at least one class + if (TryRead(result.Buffer, out var clazz) == false) + { + reader.AdvanceTo(result.Buffer.Start, result.Buffer.End); + continue; + } + + reader.AdvanceTo(result.Buffer.End); + return clazz; + } + } + + /// + /// Reads the next class from the stream. + /// + /// + public static ClassReader Read(Stream stream) + { + if (stream is null) + throw new ArgumentNullException(nameof(stream)); + + var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(minimumReadSize: 1, leaveOpen: true)); + + try + { + return Read(reader); + } + catch (Exception e) + { + reader.Complete(e); + throw; + } + finally + { + reader.Complete(); + } + } + + /// + /// Reads the next class from the stream. + /// + /// + public static ClassReader Read(PipeReader reader) + { + while (true) + { + var result = reader.ReadAtLeastAsync(30, CancellationToken.None).GetAwaiter().GetResult(); + if (result.IsCanceled) + throw new OperationCanceledException(); + + // attempt to read at least one class + if (TryRead(result.Buffer, out var clazz) == false) + { + reader.AdvanceTo(result.Buffer.Start, result.Buffer.End); + continue; + } + + reader.AdvanceTo(result.Buffer.End); + return clazz; + } + } + + internal int MAX_STACK_ALLOC = 1024; + + ClassConstantReader @this; + ClassConstantReader super; + ConstantReaderCollection constants; + InterfaceReaderCollection interfaces; + FieldReaderCollection fields; + MethodReaderCollection methods; + AttributeReaderCollection attributes; + + /// + /// Initializes a new instance. + /// + /// + internal ClassReader(ClassRecord record) : + base(null, record) + { + + } + + /// + /// Gets the version of the class. + /// + public ClassFormatVersion Version => new ClassFormatVersion(Record.MajorVersion, Record.MinorVersion); + + /// + /// Gets the set of constants declared by the class. + /// + public ConstantReaderCollection Constants => LazyGet(ref constants, () => new ConstantReaderCollection(this, Record.Constants)); + + /// + /// Gets the access flags of the class. + /// + public AccessFlag AccessFlags => Record.AccessFlags; + + /// + /// Gets the name of the class. + /// + public ClassConstantReader This => LazyGet(ref @this, () => Constants.Get(Record.ThisClassIndex)); + + /// + /// Gets the name of the super class. + /// + public ClassConstantReader Super => LazyGet(ref super, () => Constants.Get(Record.SuperClassIndex)); + + /// + /// Gets the set of the interfaces implemented by this class. + /// + public InterfaceReaderCollection Interfaces => LazyGet(ref interfaces, () => new InterfaceReaderCollection(this, Record.Interfaces)); + + /// + /// Gets the set of fields declared by the class. + /// + public FieldReaderCollection Fields => LazyGet(ref fields, () => new FieldReaderCollection(this, Record.Fields)); + + /// + /// Gets the set of methods declared by the class. + /// + public MethodReaderCollection Methods => LazyGet(ref methods, () => new MethodReaderCollection(this, Record.Methods)); + + /// + /// Gets the dictionary of attributes declared by the class. + /// + public AttributeReaderCollection Attributes => LazyGet(ref attributes, () => new AttributeReaderCollection(this, Record.Attributes)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/CodeAttributeReader.cs b/src/IKVM.ByteCode/Reading/CodeAttributeReader.cs new file mode 100644 index 0000000000..e311b7ff24 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/CodeAttributeReader.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class CodeAttributeReader : AttributeReader + { + + AttributeReaderCollection attributes; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal CodeAttributeReader(ClassReader declaringClass, AttributeInfoReader info, CodeAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + public ushort MaxStack => Record.MaxStack; + + public ushort MaxLocals => Record.MaxLocals; + + /// + /// Gets the byte code. + /// + public ReadOnlyMemory Code => Record.Code; + + public IReadOnlyList ExceptionTable => Record.ExceptionTable; + + /// + /// Gets the set of attributes applied to this attribute. + /// + public AttributeReaderCollection Attributes => LazyGet(ref attributes, () => new AttributeReaderCollection(DeclaringClass, Record.Attributes)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ConstantReader.cs b/src/IKVM.ByteCode/Reading/ConstantReader.cs new file mode 100644 index 0000000000..b3c17646a6 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ConstantReader.cs @@ -0,0 +1,83 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Provides static methods for reading constants. + /// + internal static class ConstantReader + { + + /// + /// Initializes a from a . + /// + /// + /// + /// + /// + /// + internal static IConstantReader Read(ClassReader declaringClass, ushort index, ConstantRecord record) => record switch + { + Utf8ConstantRecord c => new Utf8ConstantReader(declaringClass, index, c), + IntegerConstantRecord c => new IntegerConstantReader(declaringClass, index, c), + FloatConstantRecord c => new FloatConstantReader(declaringClass, index, c), + LongConstantRecord c => new LongConstantReader(declaringClass, index, c), + DoubleConstantRecord c => new DoubleConstantReader(declaringClass, index, c), + ClassConstantRecord c => new ClassConstantReader(declaringClass, index, c), + StringConstantRecord c => new StringConstantReader(declaringClass, index, c), + FieldrefConstantRecord c => new FieldrefConstantReader(declaringClass, index, c), + MethodrefConstantRecord c => new MethodrefConstantReader(declaringClass, index, c), + InterfaceMethodrefConstantRecord c => new InterfaceMethodrefConstantReader(declaringClass, index, c), + NameAndTypeConstantRecord c => new NameAndTypeConstantReader(declaringClass, index, c), + MethodHandleConstantRecord c => new MethodHandleConstantReader(declaringClass, index, c), + MethodTypeConstantRecord c => new MethodTypeConstantReader(declaringClass, index, c), + DynamicConstantRecord c => new DynamicConstantReader(declaringClass, index, c), + InvokeDynamicConstantRecord c => new InvokeDynamicConstantReader(declaringClass, index, c), + ModuleConstantRecord c => new ModuleConstantReader(declaringClass, index, c), + PackageConstantRecord c => new PackageConstantReader(declaringClass, index, c), + _ => throw new ByteCodeException($"Invalid constant type: {record.GetType().Name}"), + }; + + } + + /// + /// Base type for constant readers. + /// + /// + internal abstract class ConstantReader : ReaderBase, IConstantReader + where TRecord : ConstantRecord + { + + readonly ushort index; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + protected ConstantReader(ClassReader declaringClass, ushort index, TRecord record) : + base(declaringClass, record) + { + this.index = index; + } + + /// + /// Gets the index of the constant. + /// + public ushort Index => index; + + /// + /// Returns true if the constant is considered loadable according to the JVM specification. + /// + public virtual bool IsLoadable => false; + + /// + /// Gets the underlying constant being read. + /// + TRecord IConstantReader.Record => Record; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ConstantReaderCollection.cs b/src/IKVM.ByteCode/Reading/ConstantReaderCollection.cs new file mode 100644 index 0000000000..dc44a309c0 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ConstantReaderCollection.cs @@ -0,0 +1,63 @@ +using System; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of constants. + /// + internal sealed class ConstantReaderCollection : LazyReaderList + { + + readonly ClassReader declaringClass; + + /// + /// Initializes a new instance. + /// + /// + /// + internal ConstantReaderCollection(ClassReader declaringClass, ConstantRecord[] records) : + base(declaringClass, records, 0) + { + this.declaringClass = declaringClass ?? throw new ArgumentNullException(nameof(declaringClass)); + } + + /// + /// Creates a new reader for the given record. + /// + /// + /// + /// + protected override IConstantReader CreateReader(int index, ConstantRecord record) + { + return record is not null ? ConstantReader.Read(declaringClass, (ushort)index, record) : null; + } + + /// + /// Attempts to get the constant reader at the specified index. + /// + /// + /// + /// + /// + public TReader Get(int index) + where TReader : class, IConstantReader + { + if (index == 0) + return null; + + try + { + return this[index] as TReader ?? throw new ByteCodeException($"Invalid constant resolution. Reader at index {index} is not a {typeof(TReader).Name}."); + } + catch (ArgumentOutOfRangeException e) + { + throw new ByteCodeException($"Invalid constant resolution. Reader at index {index} is not valid.", e); + } + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ConstantValueAttributeReader.cs b/src/IKVM.ByteCode/Reading/ConstantValueAttributeReader.cs new file mode 100644 index 0000000000..71f6d074a0 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ConstantValueAttributeReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ConstantValueAttributeReader : AttributeReader + { + + object value; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal ConstantValueAttributeReader(ClassReader declaringClass, AttributeInfoReader info, ConstantValueAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + public object Value => value ??= ResolveValue(); + + object ResolveValue() => DeclaringClass.Constants.Get(Record.ValueIndex) switch + { + LongConstantReader l => l.Value, + FloatConstantReader f => f.Value, + DoubleConstantReader d => d.Value, + IntegerConstantReader i => i.Value, + StringConstantReader s => s.Value, + _ => throw new ByteCodeException("Invalid constant type for constant attribute."), + }; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/DelegateLazyReaderList.cs b/src/IKVM.ByteCode/Reading/DelegateLazyReaderList.cs new file mode 100644 index 0000000000..390811aa1d --- /dev/null +++ b/src/IKVM.ByteCode/Reading/DelegateLazyReaderList.cs @@ -0,0 +1,40 @@ +using System; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Concrete lazy list implementation that creates readers lazily based on a delegate. + /// + /// + /// + internal sealed class DelegateLazyReaderList : LazyReaderList + where TReader : class + { + + readonly Func create; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + public DelegateLazyReaderList(ClassReader declaringClass, TSource[] sources, Func create) : + base(declaringClass, sources) + { + this.create = create ?? throw new ArgumentNullException(nameof(create)); + } + + /// + /// Creates the new reader. + /// + /// + /// + /// + protected override TReader CreateReader(int index, TSource source) => create(index, source); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/DeprecatedAttributeReader.cs b/src/IKVM.ByteCode/Reading/DeprecatedAttributeReader.cs new file mode 100644 index 0000000000..e3c1d966d4 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/DeprecatedAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class DeprecatedAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal DeprecatedAttributeReader(ClassReader declaringClass, AttributeInfoReader info, DeprecatedAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/DoubleConstantReader.cs b/src/IKVM.ByteCode/Reading/DoubleConstantReader.cs new file mode 100644 index 0000000000..78e4fbfb65 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/DoubleConstantReader.cs @@ -0,0 +1,33 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class DoubleConstantReader : ConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public DoubleConstantReader(ClassReader declaringClass, ushort index, DoubleConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the constant. + /// + public double Value => Record.Value; + + /// + /// Returns whether or not this constant is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(45, 3); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/DynamicConstantReader.cs b/src/IKVM.ByteCode/Reading/DynamicConstantReader.cs new file mode 100644 index 0000000000..b0e9d83b5e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/DynamicConstantReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class DynamicConstantReader : ConstantReader + { + + NameAndTypeConstantReader nameAndType; + + /// + /// Initializes a new instance. + /// + /// + /// + public DynamicConstantReader(ClassReader declaringClass, ushort index, DynamicConstantRecord record) : + base(declaringClass, index, record) + { + + } + + public ushort BootstrapMethodAttributeIndex => Record.BootstrapMethodAttributeIndex; + + public NameAndTypeConstantReader NameAndType => LazyGet(ref nameAndType, () => DeclaringClass.Constants.Get(Record.NameAndTypeIndex)); + + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(55, 0); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ElementValueAnnotationReader.cs b/src/IKVM.ByteCode/Reading/ElementValueAnnotationReader.cs new file mode 100644 index 0000000000..f5987d5f47 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueAnnotationReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ElementValueAnnotationReader : ElementValueReader + { + + AnnotationReader annotation; + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueAnnotationReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the annotation included with this element value. + /// + public AnnotationReader Annotation => LazyGet(ref annotation, () => new AnnotationReader(DeclaringClass, ValueRecord.Annotation)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ElementValueArrayReader.cs b/src/IKVM.ByteCode/Reading/ElementValueArrayReader.cs new file mode 100644 index 0000000000..ef7a28b56b --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueArrayReader.cs @@ -0,0 +1,28 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ElementValueArrayReader : ElementValueReader + { + + ElementValueReaderCollection values; + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueArrayReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + public ElementValueReaderCollection Values => LazyGet(ref values, () => new ElementValueReaderCollection(DeclaringClass, ValueRecord.Values)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ElementValueClassReader.cs b/src/IKVM.ByteCode/Reading/ElementValueClassReader.cs new file mode 100644 index 0000000000..69a780d5da --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueClassReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ElementValueClassReader : ElementValueReader + { + + Utf8ConstantReader clazz; + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueClassReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the class included with this element value. + /// + public Utf8ConstantReader Class => LazyGet(ref clazz, () => DeclaringClass.Constants.Get(ValueRecord.ClassIndex)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ElementValueConstantReader.cs b/src/IKVM.ByteCode/Reading/ElementValueConstantReader.cs new file mode 100644 index 0000000000..1abc7f9c87 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueConstantReader.cs @@ -0,0 +1,55 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ElementValueConstantReader : ElementValueReader + { + + IConstantReader value; + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueConstantReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the type of the element value. + /// + public ElementValueTag Tag => Record.Tag; + + /// + /// Gets the constant value. + /// + public IConstantReader Value => LazyGet(ref value, ResolveValue); + + /// + /// Gets the value of the constant element. + /// + /// + /// + IConstantReader ResolveValue() => Record.Tag switch + { + ElementValueTag.Byte => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Char => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Double => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Float => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Integer => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Long => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Short => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.Boolean => DeclaringClass.Constants.Get(ValueRecord.Index), + ElementValueTag.String => DeclaringClass.Constants.Get(ValueRecord.Index), + _ => throw new ByteCodeException($"Unknown element value constant tag '{Record.Tag}'.") + }; + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ElementValueEnumConstantReader.cs b/src/IKVM.ByteCode/Reading/ElementValueEnumConstantReader.cs new file mode 100644 index 0000000000..46c3f9d31e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueEnumConstantReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ElementValueEnumConstantReader : ElementValueReader + { + + Utf8ConstantReader typeName; + Utf8ConstantReader constantName; + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueEnumConstantReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + public Utf8ConstantReader TypeName => LazyGet(ref typeName, () => DeclaringClass.Constants.Get(ValueRecord.TypeNameIndex)); + + public Utf8ConstantReader ConstantName => LazyGet(ref constantName, () => DeclaringClass.Constants.Get(ValueRecord.ConstantNameIndex)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ElementValuePairReaderCollection.cs b/src/IKVM.ByteCode/Reading/ElementValuePairReaderCollection.cs new file mode 100644 index 0000000000..74b528ce5b --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValuePairReaderCollection.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of element value data. + /// + internal sealed class ElementValueKeyReaderCollection : LazyNamedReaderDictionary + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public ElementValueKeyReaderCollection(ClassReader declaringClass, ElementValuePairRecord[] records, int minIndex = 0) : + base(declaringClass, records, minIndex) + { + + } + + /// + /// Creates a new reader for the given record at the specified index. + /// + /// + /// + /// + protected override ElementValueReader CreateReader(int index, ElementValuePairRecord record) + { + return ElementValueReader.Resolve(DeclaringClass, record.Value); + } + + /// + /// Gets the key for the given record and the specified index. + /// + /// + /// + /// + protected override string GetName(int index, ElementValuePairRecord record) + { + return DeclaringClass.Constants.Get(record.NameIndex).Value; + } + + /// + /// Gets an enumerator over the items in this collection. + /// + /// + public new IEnumerator> GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ElementValueReaderCollection.cs b/src/IKVM.ByteCode/Reading/ElementValueReaderCollection.cs new file mode 100644 index 0000000000..50423c6bc2 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueReaderCollection.cs @@ -0,0 +1,30 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of attribute data. + /// + internal sealed class ElementValueReaderCollection : LazyReaderList + { + + /// + /// Initializes a new instance. + /// + /// + /// + public ElementValueReaderCollection(ClassReader delcaringClass, ElementValueRecord[] records) : + base(delcaringClass, records) + { + + } + + protected override ElementValueReader CreateReader(int index, ElementValueRecord record) + { + return ElementValueReader.Resolve(DeclaringClass, record); + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ElementValueValueReader.cs b/src/IKVM.ByteCode/Reading/ElementValueValueReader.cs new file mode 100644 index 0000000000..eab07860dd --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ElementValueValueReader.cs @@ -0,0 +1,64 @@ +using System; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal abstract class ElementValueReader : ReaderBase + { + + /// + /// Resolves the given element value record to a reader type. + /// + /// + /// + /// + /// + public static ElementValueReader Resolve(ClassReader declaringClass, ElementValueRecord record) => record.Value switch + { + ElementValueAnnotationValueRecord => new ElementValueAnnotationReader(declaringClass, record), + ElementValueArrayValueRecord => new ElementValueArrayReader(declaringClass, record), + ElementValueClassValueRecord => new ElementValueClassReader(declaringClass, record), + ElementValueConstantValueRecord => new ElementValueConstantReader(declaringClass, record), + ElementValueEnumConstantValueRecord => new ElementValueEnumConstantReader(declaringClass, record), + _ => throw new ByteCodeException("Cannot resolve element value reader."), + }; + + /// + /// Initializes a new instance. + /// + /// + /// + protected ElementValueReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + } + + internal abstract class ElementValueReader : ElementValueReader + where TValueRecord : ElementValueValueRecord + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + protected ElementValueReader(ClassReader declaringClass, ElementValueRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the underlying value record. + /// + public TValueRecord ValueRecord => (TValueRecord)Record.Value; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/EnclosingMethodAttributeReader.cs b/src/IKVM.ByteCode/Reading/EnclosingMethodAttributeReader.cs new file mode 100644 index 0000000000..83f98567c5 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/EnclosingMethodAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class EnclosingMethodAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal EnclosingMethodAttributeReader(ClassReader declaringClass, AttributeInfoReader info, EnclosingMethodAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ExceptionsAttributeReader.cs b/src/IKVM.ByteCode/Reading/ExceptionsAttributeReader.cs new file mode 100644 index 0000000000..5b115ba3fc --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ExceptionsAttributeReader.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ExceptionsAttributeReader : AttributeReader + { + + DelegateLazyReaderList exceptions; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal ExceptionsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, ExceptionsAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the names of the exceptions. + /// + public IReadOnlyList Exceptions => LazyGet(ref exceptions, () => new DelegateLazyReaderList(DeclaringClass, Record.ExceptionsIndexes, (_, index) => DeclaringClass.Constants.Get(index))); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/FieldOrMethodReader.cs b/src/IKVM.ByteCode/Reading/FieldOrMethodReader.cs new file mode 100644 index 0000000000..2edbe9e5bd --- /dev/null +++ b/src/IKVM.ByteCode/Reading/FieldOrMethodReader.cs @@ -0,0 +1,40 @@ +namespace IKVM.ByteCode.Reading +{ + + internal abstract class FieldOrMethodReader : ReaderBase + { + + /// + /// Initializes a new instance. + /// + /// + /// + protected FieldOrMethodReader(ClassReader declaringClass, TRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the access flags of this field or method. + /// + public abstract AccessFlag AccessFlags { get; } + + /// + /// Gets the name of this field or method. + /// + public abstract Utf8ConstantReader Name { get; } + + /// + /// Gets the descriptor of this field or method. + /// + public abstract Utf8ConstantReader Descriptor { get; } + + /// + /// Gets the attributes of this field or method. + /// + public abstract AttributeReaderCollection Attributes { get; } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/FieldReader.cs b/src/IKVM.ByteCode/Reading/FieldReader.cs new file mode 100644 index 0000000000..d86faf7b21 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/FieldReader.cs @@ -0,0 +1,48 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class FieldReader : FieldOrMethodReader + { + + Utf8ConstantReader name; + Utf8ConstantReader descriptor; + AttributeReaderCollection attributes; + + /// + /// Initializes a new instance. + /// + /// + /// + internal FieldReader(ClassReader declaringClass, FieldRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the access flags of the field. + /// + public override AccessFlag AccessFlags => Record.AccessFlags; + + /// + /// Gets the name of the field. + /// + public override Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Gets the descriptor of the field. + /// + public override Utf8ConstantReader Descriptor => LazyGet(ref descriptor, () => DeclaringClass.Constants.Get(Record.DescriptorIndex)); + + /// + /// Gets the attributes of the field. + /// + public override AttributeReaderCollection Attributes => LazyGet(ref attributes, () => new AttributeReaderCollection(DeclaringClass, Record.Attributes)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/FieldReaderCollection.cs b/src/IKVM.ByteCode/Reading/FieldReaderCollection.cs new file mode 100644 index 0000000000..a257f86613 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/FieldReaderCollection.cs @@ -0,0 +1,47 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of fields. + /// + internal sealed class FieldReaderCollection : LazyNamedReaderDictionary + { + + /// + /// Initializes a new instance. + /// + /// + /// + internal FieldReaderCollection(ClassReader declaringClass, FieldRecord[] records) : + base(declaringClass, records) + { + + } + + /// + /// Creates a new field reader. + /// + /// + /// + /// + protected override FieldReader CreateReader(int index, FieldRecord record) + { + return new FieldReader(DeclaringClass, record); + } + + /// + /// Gets the key for the specified record. + /// + /// + /// + /// + protected override string GetName(int index, FieldRecord record) + { + return DeclaringClass.Constants.Get(record.NameIndex).Value; + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/FieldrefConstantReader.cs b/src/IKVM.ByteCode/Reading/FieldrefConstantReader.cs new file mode 100644 index 0000000000..689d36bc45 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/FieldrefConstantReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class FieldrefConstantReader : RefConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public FieldrefConstantReader(ClassReader declaringClass, ushort index, FieldrefConstantRecord record) : + base(declaringClass, index, record) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/FloatConstantReader.cs b/src/IKVM.ByteCode/Reading/FloatConstantReader.cs new file mode 100644 index 0000000000..6cd95da0c5 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/FloatConstantReader.cs @@ -0,0 +1,33 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class FloatConstantReader : ConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public FloatConstantReader(ClassReader declaringClass, ushort index, FloatConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the constant. + /// + public float Value => Record.Value; + + /// + /// Returns true if this constant is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(45, 3); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/IConstantReader.cs b/src/IKVM.ByteCode/Reading/IConstantReader.cs new file mode 100644 index 0000000000..d9f1b8b516 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/IConstantReader.cs @@ -0,0 +1,38 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Interface supported by all of the constant readers. + /// + internal interface IConstantReader + { + + /// + /// Gets the index of this constant. + /// + ushort Index { get; } + + /// + /// Returns true if this constant is loadable. + /// + bool IsLoadable { get; } + + } + + /// + /// Interface supported by all of the constant readers. + /// + internal interface IConstantReader : IConstantReader + where TRecord : ConstantRecord + { + + /// + /// Gets the record. + /// + TRecord Record { get; } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/IRefConstantReader.cs b/src/IKVM.ByteCode/Reading/IRefConstantReader.cs new file mode 100644 index 0000000000..1e97bffcef --- /dev/null +++ b/src/IKVM.ByteCode/Reading/IRefConstantReader.cs @@ -0,0 +1,24 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Interface supported by all of the readers for Ref constants. + /// + internal interface IRefConstantReader : IConstantReader + { + + /// + /// Gets the class name of the reference. + /// + ClassConstantReader Class { get; } + + /// + /// Gets the name and type reference. + /// + NameAndTypeConstantReader NameAndType { get; } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/InnerClassesAttributeItemReader.cs b/src/IKVM.ByteCode/Reading/InnerClassesAttributeItemReader.cs new file mode 100644 index 0000000000..393ac5bbe9 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InnerClassesAttributeItemReader.cs @@ -0,0 +1,48 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class InnerClassesAttributeItemReader : ReaderBase + { + + ClassConstantReader innerClass; + ClassConstantReader outerClass; + Utf8ConstantReader innerName; + + /// + /// Initializes a new instance. + /// + /// + /// + public InnerClassesAttributeItemReader(ClassReader declaringClass, InnerClassesAttributeItemRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the name of the inner class. + /// + public ClassConstantReader InnerClass => LazyGet(ref innerClass, () => DeclaringClass.Constants.Get(Record.InnerClassIndex)); + + /// + /// Gets the name of the outer class. + /// + public ClassConstantReader OuterClass => LazyGet(ref outerClass, () => DeclaringClass.Constants.Get(Record.OuterClassIndex)); + + /// + /// Gets the inner name. + /// + public Utf8ConstantReader InnerName => LazyGet(ref innerName, () => DeclaringClass.Constants.Get(Record.InnerNameIndex)); + + /// + /// Gets the access flags of the inner class. + /// + public AccessFlag InnerClassAccessFlags => Record.InnerClassAccessFlags; + + } + +} diff --git a/src/IKVM.ByteCode/Reading/InnerClassesAttributeReader.cs b/src/IKVM.ByteCode/Reading/InnerClassesAttributeReader.cs new file mode 100644 index 0000000000..b26ebcff26 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InnerClassesAttributeReader.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class InnerClassesAttributeReader : AttributeReader + { + + IReadOnlyList items; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public InnerClassesAttributeReader(ClassReader declaringClass, AttributeInfoReader info, InnerClassesAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the items on the attribute. + /// + public IReadOnlyList Items => LazyGet(ref items, () => new DelegateLazyReaderList(DeclaringClass, Record.Items, (_, record) => new InnerClassesAttributeItemReader(DeclaringClass, record))); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/IntegerConstantReader.cs b/src/IKVM.ByteCode/Reading/IntegerConstantReader.cs new file mode 100644 index 0000000000..a0c172008b --- /dev/null +++ b/src/IKVM.ByteCode/Reading/IntegerConstantReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class IntegerConstantReader : ConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public IntegerConstantReader(ClassReader declaringClass, ushort index, IntegerConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the constant. + /// + public int Value => Record.Value; + + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(45, 3); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/InterfaceMethodrefConstantReader.cs b/src/IKVM.ByteCode/Reading/InterfaceMethodrefConstantReader.cs new file mode 100644 index 0000000000..c1a9d9fa9d --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InterfaceMethodrefConstantReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class InterfaceMethodrefConstantReader : RefConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public InterfaceMethodrefConstantReader(ClassReader declaringClass, ushort index, InterfaceMethodrefConstantRecord record) : + base(declaringClass, index, record) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/InterfaceReader.cs b/src/IKVM.ByteCode/Reading/InterfaceReader.cs new file mode 100644 index 0000000000..05b01b1f76 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InterfaceReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class InterfaceReader : ReaderBase + { + + ClassConstantReader clazz; + + /// + /// Initializes a new instance. + /// + /// + /// + internal InterfaceReader(ClassReader declaringClass, InterfaceRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the name of the interface. + /// + public ClassConstantReader Class => LazyGet(ref clazz, () => DeclaringClass.Constants.Get(Record.ClassIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/InterfaceReaderCollection.cs b/src/IKVM.ByteCode/Reading/InterfaceReaderCollection.cs new file mode 100644 index 0000000000..023d3c3126 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InterfaceReaderCollection.cs @@ -0,0 +1,46 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of method data. + /// + internal sealed class InterfaceReaderCollection : LazyNamedReaderDictionary + { + + /// + /// Initializes a new instance. + /// + /// + /// + internal InterfaceReaderCollection(ClassReader declaringClass, InterfaceRecord[] records) : + base(declaringClass, records) + { + + } + + /// + /// Creates a new interface reader. + /// + /// + /// + protected override InterfaceReader CreateReader(int index, InterfaceRecord record) + { + return new InterfaceReader(DeclaringClass, record); + } + + /// + /// Gets the name of the interface. + /// + /// + /// + /// + protected override string GetName(int index, InterfaceRecord record) + { + return DeclaringClass.Constants.Get(record.ClassIndex).Name.Value; + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/InvokeDynamicConstantReader.cs b/src/IKVM.ByteCode/Reading/InvokeDynamicConstantReader.cs new file mode 100644 index 0000000000..6fb3584726 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/InvokeDynamicConstantReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class InvokeDynamicConstantReader : ConstantReader + { + + NameAndTypeConstantReader nameAndType; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public InvokeDynamicConstantReader(ClassReader owner, ushort index, InvokeDynamicConstantRecord record) : + base(owner, index, record) + { + + } + + /// + /// Gets the index into the BootstrapMethod table that is referenced by this constant. + /// + public ushort BootstrapMethodAttributeIndex => Record.BootstrapMethodAttributeIndex; + + /// + /// Gets the name of the InvokeDynamic constant. + /// + public NameAndTypeConstantReader NameAndType => LazyGet(ref nameAndType, () => DeclaringClass.Constants.Get(Record.NameAndTypeIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LazyNamedReaderDictionary.cs b/src/IKVM.ByteCode/Reading/LazyNamedReaderDictionary.cs new file mode 100644 index 0000000000..17fb0dbe96 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LazyNamedReaderDictionary.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Base implementation of a dictionary that generates reader instances on demand. + /// + /// + /// + internal abstract class LazyNamedReaderDictionary : IReadOnlyDictionary + where TReader : class + { + + readonly ClassReader declaringClass; + readonly TRecord[] records; + readonly int minIndex; + + TReader[] readers; + string[] names; + + /// + /// Initializes a new instance. + /// + /// + /// + public LazyNamedReaderDictionary(ClassReader declaringClass, TRecord[] records, int minIndex = 0) + { + this.declaringClass = declaringClass ?? throw new ArgumentNullException(nameof(declaringClass)); + this.records = records ?? throw new ArgumentNullException(nameof(records)); + this.minIndex = minIndex; + } + + /// + /// Gets the underlying records that make up the list. + /// + protected IReadOnlyList Records => records; + + /// + /// Creates the appropriate reader. + /// + /// + /// + /// + protected abstract TReader CreateReader(int index, TRecord record); + + /// + /// Gets the name for the given record. + /// + /// + /// + /// + protected abstract string GetName(int index, TRecord record); + + /// + /// Resolves the specified reader at the given index. + /// + /// + TReader ResolveReader(int index) + { + if (index < minIndex || index >= records.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + // initialize cache if not initialized + if (readers == null) + Interlocked.CompareExchange(ref readers, new TReader[records.Length], null); + + // consult cache + if (readers[index] is TReader reader) + return reader; + + // atomic set, only one winner + Interlocked.CompareExchange(ref readers[index], CreateReader(index, records[index]), null); + return readers[index]; + } + + /// + /// Resolves the specified name at the given index. + /// + /// + /// + /// + string ResolveName(int index) + { + if (index < minIndex || index >= records.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + // initialize cache if not initialized + if (names == null) + Interlocked.CompareExchange(ref names, new string[records.Length], null); + + // consult cache + if (names[index] is string name) + return name; + + // atomic set, only one winner + Interlocked.CompareExchange(ref names[index], GetName(index, records[index]), null); + return names[index]; + } + + /// + /// Gets the class that declared this list. + /// + public ClassReader DeclaringClass => declaringClass; + + /// + /// Gets the reader at the specified index. + /// + /// + /// + public TReader this[int index] => ResolveReader(index); + + /// + /// Gets the reader with the specified name. + /// + /// + /// + public TReader this[string name] => Enumerable.Range(minIndex, records.Length).Where(i => ResolveName(i) == name).Select(ResolveReader).FirstOrDefault() ?? throw new KeyNotFoundException(); + + /// + /// Gets the count of readers. + /// + public int Count => records.Length; + + /// + /// Attempts to get the value at the specified index, or returns the default value if out of range. + /// + /// + /// + /// + public bool TryGet(int index, out TReader value) + { + value = default; + + if (index < minIndex || index >= records.Length) + return false; + + value = this[index]; + return true; + } + + /// + /// Attempts to get the value at the specified index, or returns the default value if out of range. + /// + /// + /// + /// + public bool TryGet(string name, out TReader value) => (value = Enumerable.Range(minIndex, records.Length).Where(i => ResolveName(i) == name).Select(ResolveReader).FirstOrDefault()) != null; + + /// + /// Returns true if the collection contains a reader with the specified name. + /// + /// + /// + public bool Contains(string name) => Enumerable.Range(minIndex, records.Length).Any(i => ResolveName(i) == name); + + /// + /// Gets an enumerator over each reader. + /// + /// + public IEnumerator GetEnumerator() => Enumerable.Range(minIndex, records.Length).Select(i => this[i]).GetEnumerator(); + + /// + /// Gets an enumerator over each reader. + /// + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Gets an enumerator over each reader. + /// + /// + IEnumerator> IEnumerable>.GetEnumerator() => Enumerable.Range(minIndex, records.Length).Select(i => new KeyValuePair(ResolveName(i), ResolveReader(i))).GetEnumerator(); + + /// + /// Gets all of the available keys. + /// + IEnumerable IReadOnlyDictionary.Keys => Enumerable.Range(minIndex, records.Length).Select(ResolveName); + + /// + /// Gets all of the available values. + /// + IEnumerable IReadOnlyDictionary.Values => Enumerable.Range(minIndex, records.Length).Select(ResolveReader); + + /// + /// Attempts to retrieve the reader with the specified key. + /// + /// + /// + /// + bool IReadOnlyDictionary.TryGetValue(string key, out TReader value) => TryGet(key, out value); + + /// + /// Returns true if the collection contains the specified key. + /// + /// + /// + bool IReadOnlyDictionary.ContainsKey(string key) => Contains(key); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LazyReaderList.cs b/src/IKVM.ByteCode/Reading/LazyReaderList.cs new file mode 100644 index 0000000000..2d66ecb31f --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LazyReaderList.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Base implementation of a list that generates reader instances on demand. + /// + /// + /// + internal abstract class LazyReaderList : IReadOnlyList + where TReader : class + { + + readonly ClassReader declaringClass; + readonly TRecord[] records; + readonly uint minIndex; + + TReader[] readers; + + /// + /// Initializes a new instance. + /// + /// + /// + public LazyReaderList(ClassReader declaringClass, TRecord[] records, uint minIndex = 0) + { + this.declaringClass = declaringClass ?? throw new ArgumentNullException(nameof(declaringClass)); + this.records = records ?? throw new ArgumentNullException(nameof(records)); + this.minIndex = minIndex; + } + + /// + /// Gets the underlying records that make up the list. + /// + protected IReadOnlyList Records => records; + + /// + /// Creates the appropriate reader. + /// + /// + /// + /// + protected abstract TReader CreateReader(int index, TRecord record); + + /// + /// Resolves the specified reader at the given index. + /// + /// + TReader ResolveReader(int index) + { + if (index < minIndex || index >= records.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + // initialize cache if not initialized + if (readers == null) + Interlocked.CompareExchange(ref readers, new TReader[records.Length], null); + + // consult cache + if (readers[index] is TReader reader) + return reader; + + // atomic set, only one winner + Interlocked.CompareExchange(ref readers[index], CreateReader(index, records[index]), null); + return readers[index]; + } + + /// + /// Gets the class that declared this list. + /// + public ClassReader DeclaringClass => declaringClass; + + /// + /// Gets the reader at the specified index. + /// + /// + /// + public TReader this[int index] => ResolveReader(index); + + /// + /// Gets the count of readers. + /// + public int Count => records.Length; + + /// + /// Gets an enumerator over each reader. + /// + /// + public IEnumerator GetEnumerator() => Enumerable.Range(0, records.Length).Select(i => this[i]).GetEnumerator(); + + /// + /// Gets an enumerator over each reader. + /// + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LineNumberTableAttributeReader.cs b/src/IKVM.ByteCode/Reading/LineNumberTableAttributeReader.cs new file mode 100644 index 0000000000..35d5977a06 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LineNumberTableAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class LineNumberTableAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal LineNumberTableAttributeReader(ClassReader declaringClass, AttributeInfoReader info, LineNumberTableAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LocalVariableTableAttributeReader.cs b/src/IKVM.ByteCode/Reading/LocalVariableTableAttributeReader.cs new file mode 100644 index 0000000000..24876f5b9a --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LocalVariableTableAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class LocalVariableTableAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal LocalVariableTableAttributeReader(ClassReader declaringClass, AttributeInfoReader info, LocalVariableTableAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LocalVariableTypeTableAttributeReader.cs b/src/IKVM.ByteCode/Reading/LocalVariableTypeTableAttributeReader.cs new file mode 100644 index 0000000000..6b0794f772 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LocalVariableTypeTableAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class LocalVariableTypeTableAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal LocalVariableTypeTableAttributeReader(ClassReader declaringClass, AttributeInfoReader info, LocalVariableTypeTableAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/LongConstantReader.cs b/src/IKVM.ByteCode/Reading/LongConstantReader.cs new file mode 100644 index 0000000000..4cf3ee55fb --- /dev/null +++ b/src/IKVM.ByteCode/Reading/LongConstantReader.cs @@ -0,0 +1,34 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class LongConstantReader : ConstantReader + { + + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public LongConstantReader(ClassReader declaringClass, ushort index, LongConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the constant. + /// + public long Value => Record.Value; + + /// + /// Returns true if this constant is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(45, 3); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/MethodHandleConstantReader.cs b/src/IKVM.ByteCode/Reading/MethodHandleConstantReader.cs new file mode 100644 index 0000000000..0c1d7bfe30 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodHandleConstantReader.cs @@ -0,0 +1,42 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodHandleConstantReader : ConstantReader + { + + IRefConstantReader reference; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public MethodHandleConstantReader(ClassReader declaringClass, ushort index, MethodHandleConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the kind of this reference. + /// + public ReferenceKind ReferenceKind => Record.Kind; + + /// + /// Gets the constant refered to by this MethodHandle. + /// + public IRefConstantReader Reference => LazyGet(ref reference, () => DeclaringClass.Constants.Get(Record.Index)); + + /// + /// Returns true if this constant type is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(51, 0); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/MethodParametersAttributeParameterReader.cs b/src/IKVM.ByteCode/Reading/MethodParametersAttributeParameterReader.cs new file mode 100644 index 0000000000..f6b070bbce --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodParametersAttributeParameterReader.cs @@ -0,0 +1,35 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodParametersAttributeParameterReader : ReaderBase + { + + Utf8ConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + public MethodParametersAttributeParameterReader(ClassReader declaringClass, MethodParametersAttributeParameterRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the name of the parameters. + /// + public Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Gets the access flags of the parameter. + /// + public AccessFlag AccessFlags => Record.AccessFlags; + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/MethodParametersAttributeReader.cs b/src/IKVM.ByteCode/Reading/MethodParametersAttributeReader.cs new file mode 100644 index 0000000000..cd0e72ff96 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodParametersAttributeReader.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodParametersAttributeReader : AttributeReader + { + + IReadOnlyList parameters; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal MethodParametersAttributeReader(ClassReader declaringClass, AttributeInfoReader info, MethodParametersAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public IReadOnlyList Parameters => LazyGet(ref parameters, () => new DelegateLazyReaderList(DeclaringClass, Record.Parameters, (_, record) => new MethodParametersAttributeParameterReader(DeclaringClass, record))); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/MethodReader.cs b/src/IKVM.ByteCode/Reading/MethodReader.cs new file mode 100644 index 0000000000..f33b567c71 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodReader.cs @@ -0,0 +1,48 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodReader : FieldOrMethodReader + { + + Utf8ConstantReader name; + Utf8ConstantReader descriptor; + AttributeReaderCollection attributes; + + /// + /// Initializes a new instance. + /// + /// + /// + internal MethodReader(ClassReader declaringClass, MethodRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the access flags of the method. + /// + public override AccessFlag AccessFlags => Record.AccessFlags; + + /// + /// Gets the name of the method. + /// + public override Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Gets the descriptor of the method. + /// + public override Utf8ConstantReader Descriptor => LazyGet(ref descriptor, () => DeclaringClass.Constants.Get(Record.DescriptorIndex)); + + /// + /// Gets the attributes of the method. + /// + public override AttributeReaderCollection Attributes => LazyGet(ref attributes, () => new AttributeReaderCollection(DeclaringClass, Record.Attributes)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/MethodReaderCollection.cs b/src/IKVM.ByteCode/Reading/MethodReaderCollection.cs new file mode 100644 index 0000000000..d7aa27772d --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodReaderCollection.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Linq; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of methods. + /// + internal sealed class MethodReaderCollection : LazyNamedReaderDictionary + { + + /// + /// Initializes a new instance. + /// + /// + /// + internal MethodReaderCollection(ClassReader declaringClass, MethodRecord[] records) : + base(declaringClass, records) + { + + } + + /// + /// Creates a new method reader. + /// + /// + /// + /// + protected override MethodReader CreateReader(int index, MethodRecord record) + { + return new MethodReader(DeclaringClass, record); + } + + /// + /// Gets the name ofr the given record. + /// + /// + /// + /// + protected override string GetName(int index, MethodRecord record) + { + return DeclaringClass.Constants.Get(record.NameIndex).Value; + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/MethodTypeConstantReader.cs b/src/IKVM.ByteCode/Reading/MethodTypeConstantReader.cs new file mode 100644 index 0000000000..6b5b06ee39 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodTypeConstantReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodTypeConstantReader : ConstantReader + { + + Utf8ConstantReader type; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public MethodTypeConstantReader(ClassReader owner, ushort index, MethodTypeConstantRecord record) : + base(owner, index, record) + { + + } + + /// + /// Gets the type of this MethodType constant. + /// + public Utf8ConstantReader Type => LazyGet(ref type, () => DeclaringClass.Constants.Get(Record.DescriptorIndex)); + + /// + /// Returns true if this constant is loadable. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(51, 0); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/MethodrefConstantReader.cs b/src/IKVM.ByteCode/Reading/MethodrefConstantReader.cs new file mode 100644 index 0000000000..6c505f4b24 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/MethodrefConstantReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class MethodrefConstantReader : RefConstantReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public MethodrefConstantReader(ClassReader declaringClass, ushort index, MethodrefConstantRecord record) : + base(declaringClass, index, record) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/ModuleAttributeReader.cs b/src/IKVM.ByteCode/Reading/ModuleAttributeReader.cs new file mode 100644 index 0000000000..068bb57d9b --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ModuleAttributeReader.cs @@ -0,0 +1,38 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ModuleAttributeReader : AttributeReader + { + + ModuleConstantReader name; + Utf8ConstantReader version; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public ModuleAttributeReader(ClassReader declaringClass, AttributeInfoReader info, ModuleAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + /// + /// Gets the module name. + /// + public ModuleConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Gets the module version. + /// + public Utf8ConstantReader Version => LazyGet(ref version, () => DeclaringClass.Constants.Get(Record.VersionIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ModuleConstantReader.cs b/src/IKVM.ByteCode/Reading/ModuleConstantReader.cs new file mode 100644 index 0000000000..fd9df1a926 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ModuleConstantReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ModuleConstantReader : ConstantReader + { + + Utf8ConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public ModuleConstantReader(ClassReader owner, ushort index, ModuleConstantRecord record) : + base(owner, index, record) + { + + } + + /// + /// Gets the name of this module. + /// + public Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ModuleMainClassAttributeReader.cs b/src/IKVM.ByteCode/Reading/ModuleMainClassAttributeReader.cs new file mode 100644 index 0000000000..c1b5c8bf31 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ModuleMainClassAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ModuleMainClassAttributeReader : AttributeReader + { + + /// + /// initializes a new instance. + /// + /// + /// + /// + public ModuleMainClassAttributeReader(ClassReader declaringClass, AttributeInfoReader info, ModuleMainClassAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ModulePackagesAttributeReader.cs b/src/IKVM.ByteCode/Reading/ModulePackagesAttributeReader.cs new file mode 100644 index 0000000000..a4604a1b16 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ModulePackagesAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ModulePackagesAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal ModulePackagesAttributeReader(ClassReader declaringClass, AttributeInfoReader info, ModulePackagesAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/NameAndTypeConstantReader.cs b/src/IKVM.ByteCode/Reading/NameAndTypeConstantReader.cs new file mode 100644 index 0000000000..1f82d0b2e9 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/NameAndTypeConstantReader.cs @@ -0,0 +1,38 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class NameAndTypeConstantReader : ConstantReader + { + + Utf8ConstantReader name; + Utf8ConstantReader type; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public NameAndTypeConstantReader(ClassReader declaringClass, ushort index, NameAndTypeConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the name of this name and type constant. + /// + public Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + /// + /// Gets the type of this name and type constant. + /// + public Utf8ConstantReader Type => LazyGet(ref type, () => DeclaringClass.Constants.Get(Record.DescriptorIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/NestHostAttributeReader.cs b/src/IKVM.ByteCode/Reading/NestHostAttributeReader.cs new file mode 100644 index 0000000000..12f992a857 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/NestHostAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class NestHostAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public NestHostAttributeReader(ClassReader declaringClass, AttributeInfoReader info, NestHostAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/NestMembersAttributeReader.cs b/src/IKVM.ByteCode/Reading/NestMembersAttributeReader.cs new file mode 100644 index 0000000000..ac739175ec --- /dev/null +++ b/src/IKVM.ByteCode/Reading/NestMembersAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class NestMembersAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public NestMembersAttributeReader(ClassReader declaringClass, AttributeInfoReader info, NestMembersAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/PackageConstantReader.cs b/src/IKVM.ByteCode/Reading/PackageConstantReader.cs new file mode 100644 index 0000000000..de63935076 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/PackageConstantReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class PackageConstantReader : ConstantReader + { + + Utf8ConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + /// + public PackageConstantReader(ClassReader owner, ushort index, PackageConstantRecord record) : + base(owner, index, record) + { + + } + + /// + /// Gest the name of this package. + /// + public Utf8ConstantReader Name => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ParameterAnnotationReader.cs b/src/IKVM.ByteCode/Reading/ParameterAnnotationReader.cs new file mode 100644 index 0000000000..60c1b292b4 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ParameterAnnotationReader.cs @@ -0,0 +1,31 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class ParameterAnnotationReader : ReaderBase + { + + AnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + public ParameterAnnotationReader(ClassReader declaringClass, ParameterAnnotationRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the element values of the annotation. + /// + public AnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new AnnotationReaderCollection(DeclaringClass, Record.Annotations)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ParameterAnnotationReaderCollection.cs b/src/IKVM.ByteCode/Reading/ParameterAnnotationReaderCollection.cs new file mode 100644 index 0000000000..73db31c4a8 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ParameterAnnotationReaderCollection.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of annotation data. + /// + internal sealed class ParameterAnnotationReaderCollection : IReadOnlyList + { + + readonly ClassReader ownerClass; + readonly ParameterAnnotationRecord[] records; + ParameterAnnotationReader[] cache; + + /// + /// Initializes a new instance. + /// + /// + /// + public ParameterAnnotationReaderCollection(ClassReader ownerClass, ParameterAnnotationRecord[] records) + { + this.ownerClass = ownerClass ?? throw new ArgumentNullException(nameof(ownerClass)); + this.records = records ?? throw new ArgumentNullException(nameof(records)); + } + /// + /// Resolves the specified annotation of the class from the records. + /// + /// + ParameterAnnotationReader ResolveAnnotation(int index) + { + if (index < 0 || index >= records.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + // initialize cache if not initialized + if (cache == null) + Interlocked.CompareExchange(ref cache, new ParameterAnnotationReader[records.Length], null); + + // consult cache + if (cache[index] is ParameterAnnotationReader reader) + return reader; + + reader = new ParameterAnnotationReader(ownerClass, records[index]); + + // atomic set, only one winner + Interlocked.CompareExchange(ref cache[index], reader, null); + return cache[index]; + } + + /// + /// Gets the annotation at the specified index. + /// + /// + /// + public ParameterAnnotationReader this[int index] => ResolveAnnotation(index); + + /// + /// Gets the count of annotations. + /// + public int Count => records.Length; + + /// + /// Gets an enumerator over each annotation. + /// + /// + public IEnumerator GetEnumerator() => Enumerable.Range(0, records.Length).Select(ResolveAnnotation).GetEnumerator(); + + /// + /// Gets an enumerator over each annotation. + /// + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/PermittedSubclassesAttributeReader.cs b/src/IKVM.ByteCode/Reading/PermittedSubclassesAttributeReader.cs new file mode 100644 index 0000000000..2a4721bf1c --- /dev/null +++ b/src/IKVM.ByteCode/Reading/PermittedSubclassesAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class PermittedSubclassesAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public PermittedSubclassesAttributeReader(ClassReader declaringClass, AttributeInfoReader info, PermittedSubclassesAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/ReaderBase.cs b/src/IKVM.ByteCode/Reading/ReaderBase.cs new file mode 100644 index 0000000000..4ec6dcf9cf --- /dev/null +++ b/src/IKVM.ByteCode/Reading/ReaderBase.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Base class for a reader. + /// + internal abstract class ReaderBase + { + + readonly ClassReader declaringClass; + + /// + /// Initializes a new instance. + /// + /// + /// + protected ReaderBase(ClassReader declaringClass) + { + this.declaringClass = declaringClass ?? (this is ClassReader self ? self : throw new ArgumentNullException(nameof(declaringClass))); + } + + /// + /// Gets the class reader from which this entity is being read. + /// + public ClassReader DeclaringClass => declaringClass; + + } + + /// + /// Base class for a reader of a specific record type. + /// + /// + internal abstract class ReaderBase : ReaderBase + { + + readonly TRecord record; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public ReaderBase(ClassReader declaringClass, TRecord record) : + base(declaringClass) + { + this.record = record; + } + + /// + /// Gets the underlying method being read. + /// + public TRecord Record => record; + + } +} diff --git a/src/IKVM.ByteCode/Reading/RecordAttributeReader.cs b/src/IKVM.ByteCode/Reading/RecordAttributeReader.cs new file mode 100644 index 0000000000..11114a7c13 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RecordAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class RecordAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public RecordAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RecordAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/RefConstantReader.cs b/src/IKVM.ByteCode/Reading/RefConstantReader.cs new file mode 100644 index 0000000000..6e73785236 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RefConstantReader.cs @@ -0,0 +1,48 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Base type for a ref constant reader. + /// + /// + internal abstract class RefConstantReader : ConstantReader, IRefConstantReader + where TRecord : RefConstantRecord + { + + ClassConstantReader clazz; + NameAndTypeConstantReader name; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public RefConstantReader(ClassReader declaringClass, ushort index, TRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the underlying record. + /// + RefConstantRecord IConstantReader.Record => Record; + + /// + /// Gets the class name of the reference. + /// + public ClassConstantReader Class => LazyGet(ref clazz, () => DeclaringClass.Constants.Get(Record.ClassIndex)); + + /// + /// Gets the name and type of the reference. + /// + public NameAndTypeConstantReader NameAndType => LazyGet(ref name, () => DeclaringClass.Constants.Get(Record.NameAndTypeIndex)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/RuntimeInvisibleAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeInvisibleAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..c6612e93ab --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeInvisibleAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class RuntimeInvisibleAnnotationsAttributeReader : AttributeReader + { + + AnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RuntimeInvisibleAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeInvisibleAnnotationsAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public AnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new AnnotationReaderCollection(DeclaringClass, Record.Annotations)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/RuntimeInvisibleParameterAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeInvisibleParameterAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..b8bb5158c7 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeInvisibleParameterAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class RuntimeInvisibleParameterAnnotationsAttributeReader : AttributeReader + { + + ParameterAnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public RuntimeInvisibleParameterAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeInvisibleParameterAnnotationsAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public ParameterAnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new ParameterAnnotationReaderCollection(DeclaringClass, Record.Parameters)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/RuntimeInvisibleTypeAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeInvisibleTypeAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..1295b8d997 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeInvisibleTypeAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class RuntimeInvisibleTypeAnnotationsAttributeReader : AttributeReader + { + + TypeAnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RuntimeInvisibleTypeAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeInvisibleTypeAnnotationsAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public TypeAnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new TypeAnnotationReaderCollection(DeclaringClass, Record.Annotations)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/RuntimeVisibleAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeVisibleAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..e306a8ff8e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeVisibleAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class RuntimeVisibleAnnotationsAttributeReader : AttributeReader + { + + AnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RuntimeVisibleAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeVisibleAnnotationsAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public AnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new AnnotationReaderCollection(DeclaringClass, Record.Annotations)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/RuntimeVisibleParameterAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeVisibleParameterAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..fdf2ad7dbb --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeVisibleParameterAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class RuntimeVisibleParameterAnnotationsAttributeReader : AttributeReader + { + + ParameterAnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RuntimeVisibleParameterAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeVisibleParameterAnnotationsAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public ParameterAnnotationReaderCollection Parameters => LazyGet(ref annotations, () => new ParameterAnnotationReaderCollection(DeclaringClass, Record.Parameters)); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/RuntimeVisibleTypeAnnotationsAttributeReader.cs b/src/IKVM.ByteCode/Reading/RuntimeVisibleTypeAnnotationsAttributeReader.cs new file mode 100644 index 0000000000..79295f21a8 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/RuntimeVisibleTypeAnnotationsAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class RuntimeVisibleTypeAnnotationsAttributeReader : AttributeReader + { + + TypeAnnotationReaderCollection annotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RuntimeVisibleTypeAnnotationsAttributeReader(ClassReader declaringClass, AttributeInfoReader info, RuntimeVisibleTypeAnnotationsAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + /// + /// Gets the set of annotations described by this attribute. + /// + public TypeAnnotationReaderCollection Annotations => LazyGet(ref annotations, () => new TypeAnnotationReaderCollection(DeclaringClass, Record.Annotations)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/SignatureAttributeReader.cs b/src/IKVM.ByteCode/Reading/SignatureAttributeReader.cs new file mode 100644 index 0000000000..245e629275 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/SignatureAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class SignatureAttributeReader : AttributeReader + { + + string value; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal SignatureAttributeReader(ClassReader declaringClass, AttributeInfoReader info, SignatureAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + /// + /// Gets the signature value. + /// + public string Value => LazyGet(ref value, () => DeclaringClass.Constants.Get(Record.SignatureIndex).Value); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/SourceDebugExtensionAttributeReader.cs b/src/IKVM.ByteCode/Reading/SourceDebugExtensionAttributeReader.cs new file mode 100644 index 0000000000..3fca5d2484 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/SourceDebugExtensionAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class SourceDebugExtensionAttributeReader : AttributeReader + { + + /// + /// Initalizes a new instance. + /// + /// + /// + /// + internal SourceDebugExtensionAttributeReader(ClassReader declaringClass, AttributeInfoReader info, SourceDebugExtensionAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/SourceFileAttributeReader.cs b/src/IKVM.ByteCode/Reading/SourceFileAttributeReader.cs new file mode 100644 index 0000000000..5a64683f8e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/SourceFileAttributeReader.cs @@ -0,0 +1,32 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal class SourceFileAttributeReader : AttributeReader + { + + Utf8ConstantReader sourceFile; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal SourceFileAttributeReader(ClassReader declaringClass, AttributeInfoReader info, SourceFileAttributeRecord record) : + base(declaringClass, info, record) + { + + } + + /// + /// Gets the path to the source file. + /// + public Utf8ConstantReader SourceFile => LazyGet(ref sourceFile, () => DeclaringClass.Constants.Get(Record.SourceFileIndex)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/StackMapTableAttributeReader.cs b/src/IKVM.ByteCode/Reading/StackMapTableAttributeReader.cs new file mode 100644 index 0000000000..c71fad15d4 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/StackMapTableAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class StackMapTableAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal StackMapTableAttributeReader(ClassReader declaringClass, AttributeInfoReader info, StackMapTableAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/StringConstantReader.cs b/src/IKVM.ByteCode/Reading/StringConstantReader.cs new file mode 100644 index 0000000000..fd8efbfbf1 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/StringConstantReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class StringConstantReader : ConstantReader + { + + Utf8ConstantReader value; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public StringConstantReader(ClassReader declaringClass, ushort index, StringConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the string constant. Result may not actually be a string object as overrides can apply. + /// + public Utf8ConstantReader Value => LazyGet(ref value, () => DeclaringClass.Constants.Get(Record.ValueIndex)); + + /// + /// Returns true if this class is loadable according to the Java specification. + /// + public override bool IsLoadable => DeclaringClass.Version >= new ClassFormatVersion(45, 3); + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/SyntheticAttributeReader.cs b/src/IKVM.ByteCode/Reading/SyntheticAttributeReader.cs new file mode 100644 index 0000000000..78a9b13d17 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/SyntheticAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class SyntheticAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal SyntheticAttributeReader(ClassReader declaringClass, AttributeInfoReader info, SyntheticAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Reading/TypeAnnotationReader.cs b/src/IKVM.ByteCode/Reading/TypeAnnotationReader.cs new file mode 100644 index 0000000000..d39691d44e --- /dev/null +++ b/src/IKVM.ByteCode/Reading/TypeAnnotationReader.cs @@ -0,0 +1,37 @@ +using IKVM.ByteCode.Parsing; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class TypeAnnotationReader : ReaderBase + { + + Utf8ConstantReader type; + ElementValueKeyReaderCollection elements; + + /// + /// Initializes a new instance. + /// + /// + /// + public TypeAnnotationReader(ClassReader declaringClass, TypeAnnotationRecord record) : + base(declaringClass, record) + { + + } + + /// + /// Gets the type of the annotation. + /// + public Utf8ConstantReader Type => LazyGet(ref type, () => DeclaringClass.Constants.Get(Record.TypeIndex)); + + /// + /// Gets the element values of the annotation. + /// + public ElementValueKeyReaderCollection Elements => LazyGet(ref elements, () => new ElementValueKeyReaderCollection(DeclaringClass, Record.Elements)); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/TypeAnnotationReaderCollection.cs b/src/IKVM.ByteCode/Reading/TypeAnnotationReaderCollection.cs new file mode 100644 index 0000000000..def805764b --- /dev/null +++ b/src/IKVM.ByteCode/Reading/TypeAnnotationReaderCollection.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + /// + /// Lazy init collection of annotation data. + /// + internal sealed class TypeAnnotationReaderCollection : IReadOnlyList + { + + readonly ClassReader ownerClass; + readonly TypeAnnotationRecord[] records; + + TypeAnnotationReader[] cache; + + /// + /// Initializes a new instance. + /// + /// + /// + public TypeAnnotationReaderCollection(ClassReader ownerClass, TypeAnnotationRecord[] records) + { + this.ownerClass = ownerClass ?? throw new ArgumentNullException(nameof(ownerClass)); + this.records = records ?? throw new ArgumentNullException(nameof(records)); + } + /// + /// Resolves the specified annotation of the class from the records. + /// + /// + TypeAnnotationReader ResolveAnnotation(int index) + { + if (index < 0 || index >= records.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + // initialize cache if not initialized + if (cache == null) + Interlocked.CompareExchange(ref cache, new TypeAnnotationReader[records.Length], null); + + // consult cache + if (cache[index] is TypeAnnotationReader reader) + return reader; + + reader = new TypeAnnotationReader(ownerClass, records[index]); + + // atomic set, only one winner + Interlocked.CompareExchange(ref cache[index], reader, null); + return cache[index]; + } + + /// + /// Gets the annotation at the specified index. + /// + /// + /// + public TypeAnnotationReader this[int index] => ResolveAnnotation(index); + + /// + /// Gets the count of annotations. + /// + public int Count => records.Length; + + /// + /// Gets an enumerator over each annotation. + /// + /// + public IEnumerator GetEnumerator() => Enumerable.Range(0, records.Length).Select(ResolveAnnotation).GetEnumerator(); + + /// + /// Gets an enumerator over each annotation. + /// + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + } + +} diff --git a/src/IKVM.ByteCode/Reading/UnknownAttributeReader.cs b/src/IKVM.ByteCode/Reading/UnknownAttributeReader.cs new file mode 100644 index 0000000000..3dd6867488 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/UnknownAttributeReader.cs @@ -0,0 +1,23 @@ +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Reading +{ + + internal class UnknownAttributeReader : AttributeReader + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public UnknownAttributeReader(ClassReader declaringClass, AttributeInfoReader info, UnknownAttributeRecord data) : + base(declaringClass, info, data) + { + + } + + } + +} diff --git a/src/IKVM.ByteCode/Reading/Utf8ConstantReader.cs b/src/IKVM.ByteCode/Reading/Utf8ConstantReader.cs new file mode 100644 index 0000000000..cee7fa7461 --- /dev/null +++ b/src/IKVM.ByteCode/Reading/Utf8ConstantReader.cs @@ -0,0 +1,33 @@ +using IKVM.ByteCode.Parsing; +using IKVM.ByteCode.Text; + +using static IKVM.ByteCode.Util; + +namespace IKVM.ByteCode.Reading +{ + + internal sealed class Utf8ConstantReader : ConstantReader + { + + string value; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public Utf8ConstantReader(ClassReader declaringClass, ushort index, Utf8ConstantRecord record) : + base(declaringClass, index, record) + { + + } + + /// + /// Gets the value of the constant. Result is interned. + /// + public string Value => LazyGet(ref value, () => MUTF8Encoding.GetMUTF8(DeclaringClass.Version.Major).GetString(Record.Value)); + + } + +} diff --git a/src/IKVM.ByteCode/ReferenceKind.cs b/src/IKVM.ByteCode/ReferenceKind.cs new file mode 100644 index 0000000000..ecb1e3d960 --- /dev/null +++ b/src/IKVM.ByteCode/ReferenceKind.cs @@ -0,0 +1,19 @@ +namespace IKVM.ByteCode +{ + + internal enum ReferenceKind : byte + { + + GetField = 1, + GetStatic = 2, + PutField = 3, + PutStatic = 4, + InvokeVirtual = 5, + InvokeStatic = 6, + InvokeSpecial = 7, + NewInvokeSpecial = 8, + InvokeInterface = 9, + + } + +} diff --git a/src/IKVM.ByteCode/Text/EncodingExtensions.cs b/src/IKVM.ByteCode/Text/EncodingExtensions.cs new file mode 100644 index 0000000000..623cf019b0 --- /dev/null +++ b/src/IKVM.ByteCode/Text/EncodingExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Text; + +namespace IKVM.ByteCode.Text +{ + + static class EncodingExtensions + { + +#if NETFRAMEWORK + + public static unsafe string GetString(this Encoding self, ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = bytes) + return self.GetString(bytesPtr, bytes.Length); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Text/MUTF8Decoder.cs b/src/IKVM.ByteCode/Text/MUTF8Decoder.cs similarity index 96% rename from src/IKVM.Runtime/Text/MUTF8Decoder.cs rename to src/IKVM.ByteCode/Text/MUTF8Decoder.cs index 9503ae2c3f..5697cbeef6 100644 --- a/src/IKVM.Runtime/Text/MUTF8Decoder.cs +++ b/src/IKVM.ByteCode/Text/MUTF8Decoder.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace IKVM.Runtime.Text +namespace IKVM.ByteCode.Text { internal class MUTF8Decoder : Decoder diff --git a/src/IKVM.Runtime/Text/MUTF8Encoder.cs b/src/IKVM.ByteCode/Text/MUTF8Encoder.cs similarity index 96% rename from src/IKVM.Runtime/Text/MUTF8Encoder.cs rename to src/IKVM.ByteCode/Text/MUTF8Encoder.cs index a00ec0bcf7..8b13e041e1 100644 --- a/src/IKVM.Runtime/Text/MUTF8Encoder.cs +++ b/src/IKVM.ByteCode/Text/MUTF8Encoder.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace IKVM.Runtime.Text +namespace IKVM.ByteCode.Text { internal class MUTF8Encoder : Encoder diff --git a/src/IKVM.Runtime/Text/MUTF8Encoding.cs b/src/IKVM.ByteCode/Text/MUTF8Encoding.cs similarity index 56% rename from src/IKVM.Runtime/Text/MUTF8Encoding.cs rename to src/IKVM.ByteCode/Text/MUTF8Encoding.cs index 6cceb1eddd..e68c5c7884 100644 --- a/src/IKVM.Runtime/Text/MUTF8Encoding.cs +++ b/src/IKVM.ByteCode/Text/MUTF8Encoding.cs @@ -1,21 +1,33 @@ using System; using System.Text; -namespace IKVM.Runtime.Text +namespace IKVM.ByteCode.Text { /// - /// Implements a for Sun's modified UTF-8. + /// Implements an for Sun's modified UTF-8. /// internal class MUTF8Encoding : Encoding { - static MUTF8Encoding mutf8; + readonly static MUTF8Encoding JavaSE_1_0 = new MUTF8Encoding(0); + readonly static MUTF8Encoding JavaSE_1_4 = new MUTF8Encoding(48); /// - /// Gets an instance of the Sun modified UTF8 encoding. + /// Gets an instance of the Sun modified UTF8 encoding targeting the specified JavaSE version. /// - public static MUTF8Encoding MUTF8 => mutf8 ??= new MUTF8Encoding(); + public static MUTF8Encoding GetMUTF8(int majorVersion) => majorVersion >= 48 ? JavaSE_1_4 : JavaSE_1_0; + + readonly int majorVersion; + + /// + /// Initializes a new instance. + /// + /// + public MUTF8Encoding(int majorVersion = 52) + { + this.majorVersion = majorVersion; + } /// /// Scans for the position of the first NULL given the specified pointer, up to a maximum offset of . @@ -23,7 +35,7 @@ internal class MUTF8Encoding : Encoding /// /// /// - public static unsafe int IndexOfNull(byte* ptr, int max = int.MaxValue) + public unsafe int IndexOfNull(byte* ptr, int max = int.MaxValue) { if (ptr is null) throw new ArgumentNullException(nameof(ptr)); @@ -71,18 +83,14 @@ public override unsafe int GetByteCount(char* chars, int count) if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - // avoid problems with empty buffer - if (count == 0) - return 0; + var len = 0; - int len = 0; - - for (int i = 0; i < count; i++) + for (int i = 0, e = count; i < e; i++) { - var ch = chars[i]; - if ((ch != 0) && (ch <= 0x7F)) - len++; - else if (ch <= 0x7FF) + var c = chars[i]; + if (c > 0 && c <= 0x007F) + len += 1; + else if (c <= 0x07FF) len += 2; else len += 3; @@ -91,6 +99,7 @@ public override unsafe int GetByteCount(char* chars, int count) return len; } + /// public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (s is null) @@ -150,10 +159,6 @@ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount)); - // avoid problems with empty buffer - if (charCount == 0) - return 0; - int j = 0; for (int i = 0; i < charCount; i++) @@ -162,14 +167,14 @@ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int if ((ch != 0) && (ch <= 0x7F)) { if (j + 1 > byteCount) - throw new ArgumentException(); + throw new EncoderFallbackException("Out of memory."); bytes[j++] = (byte)ch; } else if (ch <= 0x7FF) { if (j + 2 > byteCount) - throw new ArgumentException(); + throw new EncoderFallbackException("Out of memory."); bytes[j++] = (byte)((ch >> 6) | 0xC0); bytes[j++] = (byte)((ch & 0x3F) | 0x80); @@ -177,7 +182,7 @@ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int else { if (j + 3 > byteCount) - throw new ArgumentException(); + throw new EncoderFallbackException("Out of memory."); bytes[j++] = (byte)((ch >> 12) | 0xE0); bytes[j++] = (byte)(((ch >> 6) & 0x3F) | 0x80); @@ -202,7 +207,7 @@ public unsafe int GetCharCount(ReadOnlySpan bytes) #endif /// - public unsafe override int GetCharCount(byte[] bytes, int index, int count) + public override int GetCharCount(byte[] bytes, int index, int count) { if (bytes is null) throw new ArgumentNullException(nameof(bytes)); @@ -224,48 +229,68 @@ public override unsafe int GetCharCount(byte* bytes, int count) if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - // avoid problems with empty buffer - if (count == 0) - return 0; - - var hasNonAscii = false; - for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) { - if (bytes[i] >= 128) - { - hasNonAscii = true; - break; - } - } - - // no non-ascii detected, we can just parse as ASCII, where byte count is char count - if (hasNonAscii == false) - return count; - - int l = 0; + if (bytes[j] == 0) + throw new DecoderFallbackException("Illegal value in modified UTF8."); - for (int i = 0; i < count; i++) - { - int c = *bytes++; - switch (c >> 4) + if (bytes[j] >= 128) { - case 12: - case 13: - (*bytes)++; - i++; - break; - case 14: - (*bytes)++; - (*bytes)++; - i++; - i++; - break; + int l = 0; + for (int i = 0; i < count; i++) + { + uint c = bytes[i]; + uint char2, char3; + + if (c == 0) + throw new DecoderFallbackException("Illegal value in modified UTF8."); + + switch (c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = bytes[++i]; + if ((char2 & 0xc0) != 0x80 || i >= count) + goto default; + + c = ((c & 0x1F) << 6) | (char2 & 0x3F); + if (c < 0x80 && c != 0 && majorVersion >= 48) + goto default; + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = bytes[++i]; + char3 = bytes[++i]; + if ((char2 & 0xc0) != 0x80 || (char3 & 0xc0) != 0x80 || i >= count) + goto default; + c = ((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0); + if (c < 0x800 && majorVersion >= 48) + goto default; + break; + default: + throw new DecoderFallbackException("Illegal value in modified UTF8."); + } + + l++; + } + + return l; } - - l++; } - return l; + // fallback to ASCII (char count == byte count) + return count; } /// @@ -276,6 +301,7 @@ public unsafe override int GetChars(byte[] bytes, int byteIndex, int byteCount, #if NETFRAMEWORK + /// public unsafe int GetChars(ReadOnlySpan bytes, Span chars) { // avoid problems with empty buffer @@ -301,52 +327,68 @@ public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount)); - // avoid problems with empty buffer - if (byteCount == 0) - return 0; - - var hasNonAscii = false; - for (int i = 0; i < byteCount; i++) + for (int j = 0; j < byteCount; j++) { - if (bytes[i] >= 128) - { - hasNonAscii = true; - break; - } - } + if (bytes[j] == 0) + throw new DecoderFallbackException("Illegal value in modified UTF8."); - // no non-ascii detected, we can just parse as ASCII - if (hasNonAscii == false) - return ASCII.GetChars(bytes, byteCount, chars, charCount); - - // current output count - int o = 0; - - for (int i = 0; i < byteCount && o < charCount; i++) - { - int c = *bytes++; - int char2, char3; - switch (c >> 4) + if (bytes[j] >= 128) { - case 12: - case 13: - char2 = *bytes++; - i++; - c = ((c & 0x1F) << 6) | (char2 & 0x3F); - break; - case 14: - char2 = *bytes++; - char3 = *bytes++; - i++; - i++; - c = ((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F); - break; + int l = 0; + for (int i = 0; i < byteCount; i++) + { + uint c = bytes[i]; + uint char2, char3; + + if (c == 0) + throw new DecoderFallbackException("Illegal value in modified UTF8."); + + switch (c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = bytes[++i]; + if ((char2 & 0xc0) != 0x80 || i >= byteCount) + goto default; + + c = ((c & 0x1F) << 6) | (char2 & 0x3F); + if (c < 0x80 && c != 0 && majorVersion >= 48) + goto default; + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = bytes[++i]; + char3 = bytes[++i]; + if ((char2 & 0xc0) != 0x80 || (char3 & 0xc0) != 0x80 || i >= byteCount) + goto default; + c = ((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0); + if (c < 0x800 && majorVersion >= 48) + goto default; + break; + default: + throw new DecoderFallbackException("Illegal value in modified UTF8."); + } + + chars[l++] = (char)c; + } + + return l; } - - chars[o++] = (char)c; } - return o; + // fallback to ASCII + return ASCII.GetChars(bytes, byteCount, chars, charCount); } /// diff --git a/src/IKVM.ByteCode/TypePathKind.cs b/src/IKVM.ByteCode/TypePathKind.cs new file mode 100644 index 0000000000..82d99bdaa2 --- /dev/null +++ b/src/IKVM.ByteCode/TypePathKind.cs @@ -0,0 +1,14 @@ +namespace IKVM.ByteCode +{ + + internal enum TypePathKind : byte + { + + ArrayType = 0, + NestedType = 1, + ParameterizedWildcardTypeArgument = 2, + ParameterizedType = 3, + + } + +} diff --git a/src/IKVM.ByteCode/UnsupportedClassVersionException.cs b/src/IKVM.ByteCode/UnsupportedClassVersionException.cs new file mode 100644 index 0000000000..f9aea3be8b --- /dev/null +++ b/src/IKVM.ByteCode/UnsupportedClassVersionException.cs @@ -0,0 +1,31 @@ +namespace IKVM.ByteCode +{ + + /// + /// Describes an attempt to parse an unsupported class file version. + /// + internal sealed class UnsupportedClassVersionException : + ByteCodeException + { + + readonly ClassFormatVersion version; + + /// + /// Initializes a new instance. + /// + /// + /// + internal UnsupportedClassVersionException(ClassFormatVersion version) : + base($"Unsupported class version {version}.") + { + this.version = version; + } + + /// + /// Gets the version that was found in the class file. + /// + public ClassFormatVersion Version => version; + + } + +} diff --git a/src/IKVM.ByteCode/Util.cs b/src/IKVM.ByteCode/Util.cs new file mode 100644 index 0000000000..95a096c801 --- /dev/null +++ b/src/IKVM.ByteCode/Util.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; + +namespace IKVM.ByteCode +{ + + internal static class Util + { + + /// + /// Gets the value at the given location or initializes it if null. + /// + /// + /// + /// + /// + public static T LazyGet(ref T location, Func create) + where T : class + { + if (create is null) + throw new ArgumentNullException(nameof(create)); + + if (location == null) + Interlocked.CompareExchange(ref location, create(), null); + + return location; + } + + } + +} diff --git a/src/IKVM.Image.JDK-bin/IKVM.Image.JDK-bin.csproj b/src/IKVM.Image.JDK-bin/IKVM.Image.JDK-bin.csproj index ebbe1c9b0e..99a29746af 100644 --- a/src/IKVM.Image.JDK-bin/IKVM.Image.JDK-bin.csproj +++ b/src/IKVM.Image.JDK-bin/IKVM.Image.JDK-bin.csproj @@ -1,7 +1,7 @@  net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) false @@ -17,6 +17,7 @@ + diff --git a/src/IKVM.Image.JDK.runtime.linux-arm64/IKVM.Image.JDK.runtime.linux-arm64.csproj b/src/IKVM.Image.JDK.runtime.linux-arm64/IKVM.Image.JDK.runtime.linux-arm64.csproj index beced37262..0667e0fe26 100644 --- a/src/IKVM.Image.JDK.runtime.linux-arm64/IKVM.Image.JDK.runtime.linux-arm64.csproj +++ b/src/IKVM.Image.JDK.runtime.linux-arm64/IKVM.Image.JDK.runtime.linux-arm64.csproj @@ -19,11 +19,6 @@ - TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 diff --git a/src/IKVM.Image.JDK.runtime.osx-x64/IKVM.Image.JDK.runtime.osx-x64.csproj b/src/IKVM.Image.JDK.runtime.osx-x64/IKVM.Image.JDK.runtime.osx-x64.csproj new file mode 100644 index 0000000000..52f517b5ed --- /dev/null +++ b/src/IKVM.Image.JDK.runtime.osx-x64/IKVM.Image.JDK.runtime.osx-x64.csproj @@ -0,0 +1,43 @@ + + + net461;netcoreapp3.1 + LICENSE.md + README.md + IKVM JDK Runtime Image + + + + true + true + + + + + + + + + + + + TargetFramework=net461 + RuntimeIdentifier=osx-x64 + ikvm\net461\osx-x64\bin + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvm\netcoreapp3.1\osx-x64\bin + + + + + + TargetFramework=$(TargetFramework) + RuntimeIdentifier=osx-x64 + ikvm\osx-x64\bin + false + + + + diff --git a/src/IKVM.Image.JDK.runtime.osx-x64/README.md b/src/IKVM.Image.JDK.runtime.osx-x64/README.md new file mode 100644 index 0000000000..29d8f24ba6 --- /dev/null +++ b/src/IKVM.Image.JDK.runtime.osx-x64/README.md @@ -0,0 +1,3 @@ +# IKVM.Image.JDK + +Native requirements for IKVM.Image.JDK. diff --git a/src/IKVM.Image.JDK.runtime.osx-x64/buildTransitive/IKVM.Image.JDK.runtime.osx-x64.props b/src/IKVM.Image.JDK.runtime.osx-x64/buildTransitive/IKVM.Image.JDK.runtime.osx-x64.props new file mode 100644 index 0000000000..c866c2dc61 --- /dev/null +++ b/src/IKVM.Image.JDK.runtime.osx-x64/buildTransitive/IKVM.Image.JDK.runtime.osx-x64.props @@ -0,0 +1,14 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + netcoreapp3.1 + osx-x64 + %(RecursiveDir)%(FileName)%(Extension) + + + + diff --git a/src/IKVM.Image.JDK.runtime.osx-x64/lib/net461/_._ b/src/IKVM.Image.JDK.runtime.osx-x64/lib/net461/_._ new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/IKVM.Image.JDK.runtime.osx-x64/lib/net461/_._ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IKVM.Image.JDK.runtime.osx-x64/lib/netcoreapp3.1/_._ b/src/IKVM.Image.JDK.runtime.osx-x64/lib/netcoreapp3.1/_._ new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/IKVM.Image.JDK.runtime.osx-x64/lib/netcoreapp3.1/_._ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IKVM.Image.JDK/IKVM.Image.JDK.csproj b/src/IKVM.Image.JDK/IKVM.Image.JDK.csproj index 9ca4536219..79547bda96 100644 --- a/src/IKVM.Image.JDK/IKVM.Image.JDK.csproj +++ b/src/IKVM.Image.JDK/IKVM.Image.JDK.csproj @@ -18,22 +18,25 @@ true - + true - + true - + true - + true - + true - + + true + + true diff --git a/src/IKVM.Image.JRE-bin/IKVM.Image.JRE-bin.csproj b/src/IKVM.Image.JRE-bin/IKVM.Image.JRE-bin.csproj index 87c244a930..20f8f7c54e 100644 --- a/src/IKVM.Image.JRE-bin/IKVM.Image.JRE-bin.csproj +++ b/src/IKVM.Image.JRE-bin/IKVM.Image.JRE-bin.csproj @@ -1,13 +1,16 @@ - + + + net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) false + diff --git a/src/IKVM.Image.JRE.runtime.linux-arm64/IKVM.Image.JRE.runtime.linux-arm64.csproj b/src/IKVM.Image.JRE.runtime.linux-arm64/IKVM.Image.JRE.runtime.linux-arm64.csproj index 222d3b7b4f..3349ea0889 100644 --- a/src/IKVM.Image.JRE.runtime.linux-arm64/IKVM.Image.JRE.runtime.linux-arm64.csproj +++ b/src/IKVM.Image.JRE.runtime.linux-arm64/IKVM.Image.JRE.runtime.linux-arm64.csproj @@ -19,11 +19,6 @@ - TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 diff --git a/src/IKVM.Image.JRE.runtime.osx-x64/IKVM.Image.JRE.runtime.osx-x64.csproj b/src/IKVM.Image.JRE.runtime.osx-x64/IKVM.Image.JRE.runtime.osx-x64.csproj new file mode 100644 index 0000000000..fa56363d4c --- /dev/null +++ b/src/IKVM.Image.JRE.runtime.osx-x64/IKVM.Image.JRE.runtime.osx-x64.csproj @@ -0,0 +1,43 @@ + + + net461;netcoreapp3.1 + LICENSE.md + README.md + IKVM JRE Runtime Image + + + + true + true + + + + + + + + + + + + TargetFramework=net461 + RuntimeIdentifier=osx-x64 + ikvm\net461\osx-x64\bin + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvm\netcoreapp3.1\osx-x64\bin + + + + + + TargetFramework=$(TargetFramework) + RuntimeIdentifier=osx-x64 + ikvm\osx-x64\bin + false + + + + diff --git a/src/IKVM.Image.JRE.runtime.osx-x64/README.md b/src/IKVM.Image.JRE.runtime.osx-x64/README.md new file mode 100644 index 0000000000..04793fb34c --- /dev/null +++ b/src/IKVM.Image.JRE.runtime.osx-x64/README.md @@ -0,0 +1,3 @@ +# IKVM.Image.JRE + +Native requirements for IKVM.Image.JDK. diff --git a/src/IKVM.Image.JRE.runtime.osx-x64/buildTransitive/IKVM.Image.JRE.runtime.osx-x64.props b/src/IKVM.Image.JRE.runtime.osx-x64/buildTransitive/IKVM.Image.JRE.runtime.osx-x64.props new file mode 100644 index 0000000000..c866c2dc61 --- /dev/null +++ b/src/IKVM.Image.JRE.runtime.osx-x64/buildTransitive/IKVM.Image.JRE.runtime.osx-x64.props @@ -0,0 +1,14 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + netcoreapp3.1 + osx-x64 + %(RecursiveDir)%(FileName)%(Extension) + + + + diff --git a/src/IKVM.Image.JRE.runtime.osx-x64/lib/net461/_._ b/src/IKVM.Image.JRE.runtime.osx-x64/lib/net461/_._ new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/IKVM.Image.JRE.runtime.osx-x64/lib/net461/_._ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IKVM.Image.JRE.runtime.osx-x64/lib/netcoreapp3.1/_._ b/src/IKVM.Image.JRE.runtime.osx-x64/lib/netcoreapp3.1/_._ new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/IKVM.Image.JRE.runtime.osx-x64/lib/netcoreapp3.1/_._ @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IKVM.Image.JRE.runtime.win81-arm/IKVM.Image.JRE.runtime.win81-arm.csproj b/src/IKVM.Image.JRE.runtime.win81-arm/IKVM.Image.JRE.runtime.win81-arm.csproj index 65d6f51489..cb20256f10 100644 --- a/src/IKVM.Image.JRE.runtime.win81-arm/IKVM.Image.JRE.runtime.win81-arm.csproj +++ b/src/IKVM.Image.JRE.runtime.win81-arm/IKVM.Image.JRE.runtime.win81-arm.csproj @@ -22,7 +22,7 @@ TargetFramework=net461 RuntimeIdentifier=win81-arm - ikvm\netcoreapp3.1\win81-arm\bin + ikvm\net461\win81-arm\bin TargetFramework=netcoreapp3.1 diff --git a/src/IKVM.Image.JRE/IKVM.Image.JRE.csproj b/src/IKVM.Image.JRE/IKVM.Image.JRE.csproj index 51827bd1d3..a5e9eb4498 100644 --- a/src/IKVM.Image.JRE/IKVM.Image.JRE.csproj +++ b/src/IKVM.Image.JRE/IKVM.Image.JRE.csproj @@ -15,22 +15,25 @@ true - + true - + true - + true - + true - + true - + + true + + true diff --git a/src/IKVM.Image/buildTransitive/IKVM.Image.targets b/src/IKVM.Image/buildTransitive/IKVM.Image.targets index 38a790b58a..1f0d8dc600 100644 --- a/src/IKVM.Image/buildTransitive/IKVM.Image.targets +++ b/src/IKVM.Image/buildTransitive/IKVM.Image.targets @@ -106,6 +106,7 @@ + diff --git a/src/IKVM.Image/ikvm/any/any/lib/security/java.security b/src/IKVM.Image/ikvm/any/any/lib/security/java.security new file mode 100644 index 0000000000..602ef68389 --- /dev/null +++ b/src/IKVM.Image/ikvm/any/any/lib/security/java.security @@ -0,0 +1,569 @@ +# +# This is the "master security properties file". +# +# An alternate java.security properties file may be specified +# from the command line via the system property +# +# -Djava.security.properties= +# +# This properties file appends to the master security properties file. +# If both properties files specify values for the same key, the value +# from the command-line properties file is selected, as it is the last +# one loaded. +# +# Also, if you specify +# +# -Djava.security.properties== (2 equals), +# +# then that properties file completely overrides the master security +# properties file. +# +# To disable the ability to specify an additional properties file from +# the command line, set the key security.overridePropertiesFile +# to false in the master security properties file. It is set to true +# by default. + +# In this file, various security properties are set for use by +# java.security classes. This is where users can statically register +# Cryptography Package Providers ("providers" for short). The term +# "provider" refers to a package or set of packages that supply a +# concrete implementation of a subset of the cryptography aspects of +# the Java Security API. A provider may, for example, implement one or +# more digital signature algorithms or message digest algorithms. +# +# Each provider must implement a subclass of the Provider class. +# To register a provider in this master security properties file, +# specify the Provider subclass name and priority in the format +# +# security.provider.= +# +# This declares a provider, and specifies its preference +# order n. The preference order is the order in which providers are +# searched for requested algorithms (when no specific provider is +# requested). The order is 1-based; 1 is the most preferred, followed +# by 2, and so on. +# +# must specify the subclass of the Provider class whose +# constructor sets the values of various properties that are required +# for the Java Security API to look up the algorithms or other +# facilities implemented by the provider. +# +# There must be at least one provider specification in java.security. +# There is a default provider that comes standard with the JDK. It +# is called the "SUN" provider, and its Provider subclass +# named Sun appears in the sun.security.provider package. Thus, the +# "SUN" provider is registered via the following: +# +# security.provider.1=sun.security.provider.Sun +# +# (The number 1 is used for the default provider.) +# +# Note: Providers can be dynamically registered instead by calls to +# either the addProvider or insertProviderAt method in the Security +# class. + +# +# List of providers and their preference orders (see above): +# +security.provider.1=sun.security.provider.Sun +security.provider.2=sun.security.rsa.SunRsaSign +security.provider.3=sun.security.ec.SunEC +security.provider.4=com.sun.net.ssl.internal.ssl.Provider +security.provider.5=com.sun.crypto.provider.SunJCE +security.provider.6=sun.security.jgss.SunProvider +security.provider.7=com.sun.security.sasl.Provider +security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI +security.provider.9=sun.security.smartcardio.SunPCSC + +# +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. +# +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. +# +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass +# +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. +# +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNG:SUN + +# +# Class to instantiate as the javax.security.auth.login.Configuration +# provider. +# +login.configuration.provider=sun.security.provider.ConfigFile + +# +# Default login configuration file +# +#login.config.url.1=file:${user.home}/.java.login.config + +# +# Class to instantiate as the system Policy. This is the name of the class +# that will be used as the Policy object. +# +policy.provider=sun.security.provider.PolicyFile + +# The default is to have a single system-wide policy file, +# and a policy file in the user's home directory. +policy.url.1=file:${java.home}/lib/security/java.policy +policy.url.2=file:${user.home}/.java.policy + +# whether or not we expand properties in the policy file +# if this is set to false, properties (${...}) will not be expanded in policy +# files. +policy.expandProperties=true + +# whether or not we allow an extra policy to be passed on the command line +# with -Djava.security.policy=somefile. Comment out this line to disable +# this feature. +policy.allowSystemProperty=true + +# whether or not we look into the IdentityScope for trusted Identities +# when encountering a 1.1 signed JAR file. If the identity is found +# and is trusted, we grant it AllPermission. +policy.ignoreIdentityScope=false + +# +# Default keystore type. +# +keystore.type=jks + +# +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, none of the class loaders supplied with the JDK call +# checkPackageDefinition. +# +package.definition=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries. + +# +# Determines whether this properties file can be appended to +# or overridden on the command line via -Djava.security.properties +# +security.overridePropertiesFile=true + +# +# Determines the default key and trust manager factory algorithms for +# the javax.net.ssl package. +# +ssl.KeyManagerFactory.algorithm=SunX509 +ssl.TrustManagerFactory.algorithm=PKIX + +# +# The Java-level namelookup cache policy for successful lookups: +# +# any negative value: caching forever +# any positive value: the number of seconds to cache an address for +# zero: do not cache +# +# default value is forever (FOREVER). For security reasons, this +# caching is made forever when a security manager is set. When a security +# manager is not set, the default behavior in this implementation +# is to cache for 30 seconds. +# +# NOTE: setting this to anything other than the default value can have +# serious security implications. Do not set it unless +# you are sure you are not exposed to DNS spoofing attack. +# +#networkaddress.cache.ttl=-1 + +# The Java-level namelookup cache policy for failed lookups: +# +# any negative value: cache forever +# any positive value: the number of seconds to cache negative lookup results +# zero: do not cache +# +# In some Microsoft Windows networking environments that employ +# the WINS name service in addition to DNS, name service lookups +# that fail may take a noticeably long time to return (approx. 5 seconds). +# For this reason the default caching policy is to maintain these +# results for 10 seconds. +# +# +networkaddress.cache.negative.ttl=10 + +# +# Properties to configure OCSP for certificate revocation checking +# + +# Enable OCSP +# +# By default, OCSP is not used for certificate revocation checking. +# This property enables the use of OCSP when set to the value "true". +# +# NOTE: SocketPermission is required to connect to an OCSP responder. +# +# Example, +# ocsp.enable=true + +# +# Location of the OCSP responder +# +# By default, the location of the OCSP responder is determined implicitly +# from the certificate being validated. This property explicitly specifies +# the location of the OCSP responder. The property is used when the +# Authority Information Access extension (defined in RFC 3280) is absent +# from the certificate or when it requires overriding. +# +# Example, +# ocsp.responderURL=http://ocsp.example.net:80 + +# +# Subject name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. In cases where +# the subject name alone is not sufficient to uniquely identify the certificate +# then both the "ocsp.responderCertIssuerName" and +# "ocsp.responderCertSerialNumber" properties must be used instead. When this +# property is set then those two properties are ignored. +# +# Example, +# ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp" + +# +# Issuer name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. When this +# property is set then the "ocsp.responderCertSerialNumber" property must also +# be set. When the "ocsp.responderCertSubjectName" property is set then this +# property is ignored. +# +# Example, +# ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp" + +# +# Serial number of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# of hexadecimal digits (colon or space separators may be present) which +# identifies a certificate in the set of certificates supplied during cert path +# validation. When this property is set then the "ocsp.responderCertIssuerName" +# property must also be set. When the "ocsp.responderCertSubjectName" property +# is set then this property is ignored. +# +# Example, +# ocsp.responderCertSerialNumber=2A:FF:00 + +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + +# Algorithm restrictions for certification path (CertPath) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# for certification path building and validation. For example, "MD2" is +# generally no longer considered to be a secure hash algorithm. This section +# describes the mechanism for disabling algorithms based on algorithm name +# and/or key length. This includes algorithms used in certificates, as well +# as revocation information such as CRLs and signed OCSP Responses. +# +# The syntax of the disabled algorithm string is described as this Java +# BNF-style: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint +# +# KeySizeConstraint: +# keySize Operator DecimalInteger +# +# Operator: +# <= | < | == | != | >= | > +# +# DecimalInteger: +# DecimalDigits +# +# DecimalDigits: +# DecimalDigit {DecimalDigit} +# +# DecimalDigit: one of +# 1 2 3 4 5 6 7 8 9 0 +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# algorithm. See "Java Cryptography Architecture Standard Algorithm Name +# Documentation" for information about Standard Algorithm Names. Matching +# is performed using a case-insensitive sub-element matching rule. (For +# example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and +# "ECDSA" for signatures.) If the assertion "AlgorithmName" is a +# sub-element of the certificate algorithm name, the algorithm will be +# rejected during certification path building and validation. For example, +# the assertion algorithm name "DSA" will disable all certificate algorithms +# that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion +# will not disable algorithms related to "ECDSA". +# +# A "Constraint" provides further guidance for the algorithm being specified. +# The "KeySizeConstraint" requires a key of a valid size range if the +# "AlgorithmName" is of a key algorithm. The "DecimalInteger" indicates the +# key size specified in number of bits. For example, "RSA keySize <= 1024" +# indicates that any RSA key with key size less than or equal to 1024 bits +# should be disabled, and "RSA keySize < 1024, RSA keySize > 2048" indicates +# that any RSA key with key size less than 1024 or greater than 2048 should +# be disabled. Note that the "KeySizeConstraint" only makes sense to key +# algorithms. +# +# Note: This property is currently used by Oracle's PKIX implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 +# +# +jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024 + +# Algorithm restrictions for Secure Socket Layer/Transport Layer Security +# (SSL/TLS) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# when using SSL/TLS. This section describes the mechanism for disabling +# algorithms during SSL/TLS security parameters negotiation, including +# protocol version negotiation, cipher suites selection, peer authentication +# and key exchange mechanisms. +# +# Disabled algorithms will not be negotiated for SSL/TLS connections, even +# if they are enabled explicitly in an application. +# +# For PKI-based peer authentication and key exchange mechanisms, this list +# of disabled algorithms will also be checked during certification path +# building and validation, including algorithms used in certificates, as +# well as revocation information such as CRLs and signed OCSP Responses. +# This is in addition to the jdk.certpath.disabledAlgorithms property above. +# +# See the specification of "jdk.certpath.disabledAlgorithms" for the +# syntax of the disabled algorithm string. +# +# Note: This property is currently used by Oracle's JSSE implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 +jdk.tls.disabledAlgorithms=SSLv3, RC4, DH keySize < 768 + +# Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) +# processing in JSSE implementation. +# +# In some environments, a certain algorithm may be undesirable but it +# cannot be disabled because of its use in legacy applications. Legacy +# algorithms may still be supported, but applications should not use them +# as the security strength of legacy algorithms are usually not strong enough +# in practice. +# +# During SSL/TLS security parameters negotiation, legacy algorithms will +# not be negotiated unless there are no other candidates. +# +# The syntax of the disabled algorithm string is described as this Java +# BNF-style: +# LegacyAlgorithms: +# " LegacyAlgorithm { , LegacyAlgorithm } " +# +# LegacyAlgorithm: +# AlgorithmName (standard JSSE algorithm name) +# +# See the specification of security property "jdk.certpath.disabledAlgorithms" +# for the syntax and description of the "AlgorithmName" notation. +# +# Per SSL/TLS specifications, cipher suites have the form: +# SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# or +# TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# +# For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the +# key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC +# mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest +# algorithm for HMAC. +# +# The LegacyAlgorithm can be one of the following standard algorithm names: +# 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA +# 2. JSSE key exchange algorithm name, e.g., RSA +# 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC +# 4. JSSE message digest algorithm name, e.g., SHA +# +# See SSL/TLS specifications and "Java Cryptography Architecture Standard +# Algorithm Name Documentation" for information about the algorithm names. +# +# Note: This property is currently used by Oracle's JSSE implementation. +# It is not guaranteed to be examined and used by other implementations. +# There is no guarantee the property will continue to exist or be of the +# same syntax in future releases. +# +# Example: +# jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 +# +jdk.tls.legacyAlgorithms= \ + K_NULL, C_NULL, M_NULL, \ + DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, \ + DH_RSA_EXPORT, RSA_EXPORT, \ + DH_anon, ECDH_anon, \ + RC4_128, RC4_40, DES_CBC, DES40_CBC diff --git a/src/IKVM.JTReg.TestAdapter.Core/IKVM.JTReg.TestAdapter.Core.csproj b/src/IKVM.JTReg.TestAdapter.Core/IKVM.JTReg.TestAdapter.Core.csproj index eac388be40..a11505e535 100644 --- a/src/IKVM.JTReg.TestAdapter.Core/IKVM.JTReg.TestAdapter.Core.csproj +++ b/src/IKVM.JTReg.TestAdapter.Core/IKVM.JTReg.TestAdapter.Core.csproj @@ -3,13 +3,14 @@ - - + + net461;netcoreapp3.1 false true + true diff --git a/src/IKVM.JTReg.TestAdapter.Core/IkvmTraceServer.cs b/src/IKVM.JTReg.TestAdapter.Core/IkvmTraceServer.cs index a0a4b32342..5c92c029dc 100644 --- a/src/IKVM.JTReg.TestAdapter.Core/IkvmTraceServer.cs +++ b/src/IKVM.JTReg.TestAdapter.Core/IkvmTraceServer.cs @@ -3,6 +3,7 @@ using System.IO.Pipelines; using System.Net; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; @@ -107,14 +108,24 @@ async Task FillPipeAsync(Socket client, PipeWriter writer) try { + var bytesRead = 0; + #if NETFRAMEWORK - // framwork cannot receive directly into Memory, so receive into temporary array var netBuffer = ArrayPool.Shared.Rent(memory.Length); - var bytesRead = await client.ReceiveAsync(new ArraySegment(netBuffer, 0, memory.Length), SocketFlags.None); - netBuffer.CopyTo(memory); - ArrayPool.Shared.Return(netBuffer); + + try + { + // framwork cannot receive directly into Memory, so receive into temporary array + bytesRead = await client.ReceiveAsync(new ArraySegment(netBuffer, 0, memory.Length), SocketFlags.None); + if (bytesRead > 0) + netBuffer.AsSpan(0, bytesRead).CopyTo(memory.Span); + } + finally + { + ArrayPool.Shared.Return(netBuffer); + } #else - var bytesRead = await client.ReceiveAsync(memory, SocketFlags.None); + bytesRead = await client.ReceiveAsync(memory, SocketFlags.None); #endif if (bytesRead == 0) break; diff --git a/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs b/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs index 7b8e918095..2e3e37b5d8 100644 --- a/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs +++ b/src/IKVM.JTReg.TestAdapter.Core/JTRegTestManager.cs @@ -52,10 +52,10 @@ static JTRegTestManager() { #if NETCOREAPP // executable permissions may not have made it onto the JRE binaries so attempt to set them - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var javaHome = java.lang.System.getProperty("java.home"); - foreach (var exec in new[] { "java", "javac", "jar", "jarsigner", "javadoc", "javah", "javap", "jdeps", "keytool", "native2ascii", "policytool", "rmic", "wsgen", "wsimport" }) + foreach (var exec in new[] { "java", "javac", "jar", "jarsigner", "javadoc", "javah", "javap", "jdeps", "keytool", "native2ascii", "orbd", "policytool", "rmic", "schemagen", "wsgen", "wsimport" }) { var execPath = Path.Combine(javaHome, "bin", exec); if (File.Exists(execPath)) @@ -370,13 +370,13 @@ dynamic CreateParameters(string source, dynamic testManager, dynamic testSuite, rp.setFile((java.io.File)wd.getFile("config.jti")); rp.setEnvVars(GetEnvVars(debugUri)); rp.setConcurrency(Environment.ProcessorCount); - rp.setTimeLimit(15000); + rp.setTimeoutFactor(6); rp.setRetainArgs(java.util.Collections.singletonList("all")); rp.setExcludeLists(excludeFileList.ToArray()); rp.setMatchLists(includeFileList.ToArray()); rp.setIgnoreKind(JTRegTypes.IgnoreKind.QUIET); rp.setPriorStatusValues(null); - rp.setUseWindowsSubsystemForLinux(true); + rp.setUseWindowsSubsystemForLinux(((string)JTRegTypes.OS.Current().family) == "windows"); rp.setTestNGPath(JTRegTypes.SearchPath.New(Path.Combine(JTREG_LIB, "testng.jar"))); rp.setJUnitPath(JTRegTypes.SearchPath.New(Path.Combine(JTREG_LIB, "junit.jar"))); rp.setAsmToolsPath(JTRegTypes.SearchPath.New(Path.Combine(JTREG_LIB, "asmtools.jar"))); @@ -404,7 +404,7 @@ dynamic CreateParameters(string source, dynamic testManager, dynamic testSuite, java.util.Map GetEnvVars(Uri debugUri) { var envVars = new java.util.TreeMap(); - var os = (string)JTRegTypes.OS.Current().family; + // import existing variables based on the current OS foreach (var var in ((string)JTRegTypes.OS.Current().family) == "windows" ? DEFAULT_WINDOWS_ENV_VARS : DEFAULT_UNIX_ENV_VARS) if (Environment.GetEnvironmentVariable(var) is string val) diff --git a/src/IKVM.JTReg.TestAdapter.Tests/IKVM.JTReg.TestAdapter.Tests.csproj b/src/IKVM.JTReg.TestAdapter.Tests/IKVM.JTReg.TestAdapter.Tests.csproj index 5160b49fea..737d026b5a 100644 --- a/src/IKVM.JTReg.TestAdapter.Tests/IKVM.JTReg.TestAdapter.Tests.csproj +++ b/src/IKVM.JTReg.TestAdapter.Tests/IKVM.JTReg.TestAdapter.Tests.csproj @@ -3,8 +3,8 @@ - - + + net461;netcoreapp3.1;net6.0 diff --git a/src/IKVM.JTReg.TestAdapter/IKVM.JTReg.TestAdapter.csproj b/src/IKVM.JTReg.TestAdapter/IKVM.JTReg.TestAdapter.csproj index b0b2e86ea6..f4a6f444c3 100644 --- a/src/IKVM.JTReg.TestAdapter/IKVM.JTReg.TestAdapter.csproj +++ b/src/IKVM.JTReg.TestAdapter/IKVM.JTReg.TestAdapter.csproj @@ -1,6 +1,6 @@  - - + + net461;netcoreapp3.1 diff --git a/src/IKVM.JTReg.TestAdapter/JTRegProxyUtil.cs b/src/IKVM.JTReg.TestAdapter/JTRegProxyUtil.cs index 2f7cd4249c..86dacf8eb2 100644 --- a/src/IKVM.JTReg.TestAdapter/JTRegProxyUtil.cs +++ b/src/IKVM.JTReg.TestAdapter/JTRegProxyUtil.cs @@ -92,7 +92,7 @@ public static TestResult Convert(JTRegTestResult rslt) var s = new AttachmentSet(new Uri(JTRegTestManager.URI), "IkvmJtRegTestAdapter"); r.Attachments.Add(s); foreach (var attachment in rslt.Attachments) - s.Attachments.Add(new UriDataAttachment(new Uri(attachment.Path), attachment.Name)); + s.Attachments.Add(UriDataAttachment.CreateFrom(attachment.Path, attachment.Name)); return r; } diff --git a/src/IKVM.Java-ref/IKVM.Java-ref.csproj b/src/IKVM.Java-ref/IKVM.Java-ref.csproj index 6f1e59aa51..80863cc681 100644 --- a/src/IKVM.Java-ref/IKVM.Java-ref.csproj +++ b/src/IKVM.Java-ref/IKVM.Java-ref.csproj @@ -3,6 +3,7 @@ IKVM.Java-ref net461;netcoreapp3.1 IKVM.Java + true diff --git a/src/IKVM.Java-ref/java/security/GeneralSecurityException.cs b/src/IKVM.Java-ref/java/security/GeneralSecurityException.cs new file mode 100644 index 0000000000..b998e59a01 --- /dev/null +++ b/src/IKVM.Java-ref/java/security/GeneralSecurityException.cs @@ -0,0 +1,11 @@ +namespace java.security +{ + + public class GeneralSecurityException : java.lang.Exception + { + + + + } + +} diff --git a/src/IKVM.Java-ref/java/security/InvalidAlgorithmParameterException.cs b/src/IKVM.Java-ref/java/security/InvalidAlgorithmParameterException.cs new file mode 100644 index 0000000000..3da04f9265 --- /dev/null +++ b/src/IKVM.Java-ref/java/security/InvalidAlgorithmParameterException.cs @@ -0,0 +1,11 @@ +namespace java.security +{ + + public class InvalidAlgorithmParameterException : GeneralSecurityException + { + + + + } + +} diff --git a/src/IKVM.Java.Extensions/IKVM.Java.Extensions.csproj b/src/IKVM.Java.Extensions/IKVM.Java.Extensions.csproj index 31135bc2e4..47916a4052 100644 --- a/src/IKVM.Java.Extensions/IKVM.Java.Extensions.csproj +++ b/src/IKVM.Java.Extensions/IKVM.Java.Extensions.csproj @@ -1,6 +1,6 @@ - - + + net461;netcoreapp3.1 diff --git a/src/IKVM.Java.Extensions/java/lang/DelegateRunnable.cs b/src/IKVM.Java.Extensions/java/lang/DelegateRunnable.cs new file mode 100644 index 0000000000..f107e162da --- /dev/null +++ b/src/IKVM.Java.Extensions/java/lang/DelegateRunnable.cs @@ -0,0 +1,29 @@ +using System; + +using java.lang; + +namespace IKVM.Java.Extensions.java.lang +{ + public class DelegateRunnable : Runnable + { + + readonly Action onRun; + + /// + /// Initializes a new instance. + /// + /// + /// + public DelegateRunnable(Action onRun) + { + this.onRun = onRun ?? throw new ArgumentNullException(nameof(onRun)); + } + + public void run() + { + onRun(); + } + + } + +} diff --git a/src/IKVM.Java.Extensions/java/nio/channels/AwaitableCompletionHandler.cs b/src/IKVM.Java.Extensions/java/nio/channels/AwaitableCompletionHandler.cs new file mode 100644 index 0000000000..0ea7387550 --- /dev/null +++ b/src/IKVM.Java.Extensions/java/nio/channels/AwaitableCompletionHandler.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace java.nio.channels +{ + + /// + /// An awaitable implementation of a . + /// + public class AwaitableCompletionHandler : AwaitableCompletionHandler + { + + /// + /// Gets the task that can be awaited. + /// + public new Task Task => base.Task; + + /// + /// Gets an awaiter used to await this completion handler. + /// + public new TaskAwaiter GetAwaiter() => Task.GetAwaiter(); + + } + + /// + /// An awaitable implementation of a . + /// + /// + public class AwaitableCompletionHandler : AwaitableCompletionHandler + { + + + + } + + /// + /// An awaitable implementation of a . + /// + /// + public class AwaitableCompletionHandler : CompletionHandler + { + + readonly TaskCompletionSource source = new TaskCompletionSource(); + A attachment; + + /// + /// Gets the attachment which is set after the handler completes. + /// + public A Attachment => attachment; + + /// + /// Gets the task that can be awaited. + /// + public Task Task => source.Task; + + /// + /// Gets an awaiter used to await this completion handler. + /// + public TaskAwaiter GetAwaiter() => Task.GetAwaiter(); + + /// + /// Invoked when the handler is signaled completion. + /// + /// + /// + void CompletionHandler.completed(object result, object attachment) + { + this.attachment = (A)attachment; + source.SetResult((V)result); + } + + /// + /// Invoked when the handler is signaled failure. + /// + /// + /// + void CompletionHandler.failed(Exception exc, object attachment) + { + this.attachment = (A)attachment; + source.SetException(exc); + } + + } + +} diff --git a/src/IKVM.Java.Extensions/java/util/function/BaseStreamExtensions.cs b/src/IKVM.Java.Extensions/java/util/function/BaseStreamExtensions.cs new file mode 100644 index 0000000000..33d7aa3885 --- /dev/null +++ b/src/IKVM.Java.Extensions/java/util/function/BaseStreamExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +using java.util.stream; + +namespace java.util.function +{ + + public static class BaseStreamExtensions + { + + public static IEnumerable AsEnumerable(this Stream stream) + { + return new StreamWrapper(stream); + } + + } + +} diff --git a/src/IKVM.Java.Extensions/java/util/function/DelegateConsumer.cs b/src/IKVM.Java.Extensions/java/util/function/DelegateConsumer.cs index b27ad39b00..34a38cc1c2 100644 --- a/src/IKVM.Java.Extensions/java/util/function/DelegateConsumer.cs +++ b/src/IKVM.Java.Extensions/java/util/function/DelegateConsumer.cs @@ -8,7 +8,7 @@ namespace java.util.function /// Implementation of that sends output to a delegate. /// /// - class DelegateConsumer : Consumer + public class DelegateConsumer : Consumer { /// diff --git a/src/IKVM.Java.Extensions/java/util/function/DelegateFunction.cs b/src/IKVM.Java.Extensions/java/util/function/DelegateFunction.cs new file mode 100644 index 0000000000..8b1a2177da --- /dev/null +++ b/src/IKVM.Java.Extensions/java/util/function/DelegateFunction.cs @@ -0,0 +1,38 @@ +using System; + +namespace java.util.function +{ + + public class DelegateFunction : Function + { + + readonly Func func; + + /// + /// Initializes a new instance. + /// + /// + /// + public DelegateFunction(Func func) + { + this.func = func ?? throw new ArgumentNullException(nameof(func)); + } + + public Function andThen(Function after) + { + return Function.__DefaultMethods.andThen(this, after); + } + + public Function compose(Function before) + { + return Function.__DefaultMethods.compose(this, before); + } + + public object apply(object t) + { + return func((TArg)t); + } + + } + +} diff --git a/src/IKVM.Java.Extensions/java/util/function/DelegatePredicate.cs b/src/IKVM.Java.Extensions/java/util/function/DelegatePredicate.cs new file mode 100644 index 0000000000..a408c4e4da --- /dev/null +++ b/src/IKVM.Java.Extensions/java/util/function/DelegatePredicate.cs @@ -0,0 +1,46 @@ +using System; + +namespace java.util.function +{ + + /// + /// Implements the interface against a delegate. + /// + /// + public class DelegatePredicate : Predicate + { + + readonly Func func; + + /// + /// Initializes a new instance. + /// + /// + public DelegatePredicate(Func func) + { + this.func = func ?? throw new ArgumentNullException(nameof(func)); + } + + public Predicate and(Predicate other) + { + return Predicate.__DefaultMethods.and(this, other); + } + + public Predicate negate() + { + return Predicate.__DefaultMethods.negate(this); + } + + public Predicate or(Predicate other) + { + return Predicate.__DefaultMethods.or(this, other); + } + + public bool test(object t) + { + return func((TArg)t); + } + + } + +} diff --git a/src/IKVM.Java.Extensions/java/util/function/StreamWrapper.cs b/src/IKVM.Java.Extensions/java/util/function/StreamWrapper.cs new file mode 100644 index 0000000000..f319fd83f8 --- /dev/null +++ b/src/IKVM.Java.Extensions/java/util/function/StreamWrapper.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using java.util.stream; + +namespace java.util.function +{ + + /// + /// Wraps a to produce an . + /// + /// + class StreamWrapper : IEnumerable + { + + readonly Stream stream; + + /// + /// Initializes a new instance. + /// + /// + /// + public StreamWrapper(Stream stream) + { + this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); + } + + /// + /// Gets an enumerator for the stream. + /// + /// + public IEnumerator GetEnumerator() + { + return stream.iterator().AsEnumerator(); + } + + /// + /// Gets an enumerator for the stream. + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Java/IKVM.Java.msbuildproj b/src/IKVM.Java/IKVM.Java.msbuildproj index f6e1ceb1b3..0ee321b7e7 100644 --- a/src/IKVM.Java/IKVM.Java.msbuildproj +++ b/src/IKVM.Java/IKVM.Java.msbuildproj @@ -2,7 +2,7 @@ - + net461;netcoreapp3.1 @@ -15,7 +15,7 @@ - + @@ -39,21 +39,18 @@ - - - openjdk\$([MSBuild]::MakeRelative('$(OpenJdkDir)', '%(RootDir)%(Directory)')) - - - + + + + + - - @@ -153,4 +150,4 @@ - \ No newline at end of file + diff --git a/src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/Handler.java b/src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/Handler.java deleted file mode 100644 index bcdb0ac368..0000000000 --- a/src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/Handler.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - Copyright (C) 2002, 2003, 2004, 2005, 2006 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -package gnu.java.net.protocol.ikvmres; - -import cli.System.Reflection.Assembly; -import java.net.*; -import java.io.*; - -public class Handler extends URLStreamHandler -{ - - private static final String RFC2396_DIGIT = "0123456789"; - private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz"; - private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - private static final String RFC2396_ALPHA = RFC2396_LOWALPHA + RFC2396_UPALPHA; - private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA; - private static final String RFC2396_MARK = "-_.!~*'()"; - private static final String RFC2396_UNRESERVED = RFC2396_ALPHANUM + RFC2396_MARK; - private static final String RFC2396_REG_NAME = RFC2396_UNRESERVED + "$,;:@&=+"; - private static final String RFC2396_PCHAR = RFC2396_UNRESERVED + ":@&=+$,"; - private static final String RFC2396_SEGMENT = RFC2396_PCHAR + ";"; - private static final String RFC2396_PATH_SEGMENTS = RFC2396_SEGMENT + "/"; - - static InputStream readResourceFromAssembly(String assembly, int port, String resource) - throws cli.System.IO.FileNotFoundException, - cli.System.BadImageFormatException, - cli.System.Security.SecurityException, - IOException - { - if (assembly.equals("gen") && port != -1 && resource.endsWith(".class") && resource.indexOf('.') == resource.length() - 6) - { - ClassLoader loader = GetGenericClassLoaderById(port); - try - { - Class c = Class.forName(resource.substring(1, resource.length() - 6).replace('/', '.'), false, loader); - return new ByteArrayInputStream(GenerateStub(c)); - } - catch(ClassNotFoundException _) - { - } - catch(LinkageError _) - { - } - } - return readResourceFromAssembly(LoadAssembly(assembly), resource); - } - - public static InputStream readResourceFromAssembly(Assembly asm, String resource) - throws IOException - { - try - { - if(false) throw new cli.System.Security.SecurityException(); - if(false) throw new cli.System.IO.FileNotFoundException(); - if(false) throw new cli.System.IO.IOException(); - return new ikvm.io.InputStreamWrapper(ReadResourceFromAssemblyImpl(asm, resource)); - } - catch (cli.System.Security.SecurityException x) - { - throw (IOException)new IOException().initCause(x); - } - catch (cli.System.IO.FileNotFoundException x) - { - if(resource.endsWith(".class") && resource.indexOf('.') == resource.length() - 6) - { - Class c = LoadClassFromAssembly(asm, resource.substring(1, resource.length() - 6).replace('/', '.')); - if(c != null) - { - return new ByteArrayInputStream(GenerateStub(c)); - } - } - throw (FileNotFoundException)new FileNotFoundException().initCause(x); - } - catch(cli.System.IO.IOException x) - { - throw (IOException)new IOException().initCause(x); - } - } - - private static native byte[] GenerateStub(Class c); - private static native cli.System.IO.Stream ReadResourceFromAssemblyImpl(Assembly asm, String resource); - private static native Class LoadClassFromAssembly(Assembly asm, String className); - private static native Assembly LoadAssembly(String name) throws cli.System.IO.FileNotFoundException, cli.System.BadImageFormatException, cli.System.Security.SecurityException; - private static native ClassLoader GetGenericClassLoaderById(int id); - - protected URLConnection openConnection(URL url) throws IOException - { - return new IkvmresURLConnection(url); - } - - protected void parseURL(URL url, String url_string, int start, int end) - { - try - { - // NOTE originally I wanted to use java.net.URI to handling parsing and constructing of these things, - // but it turns out that URI uses regex and that depends on resource loading... - url_string = url_string.substring(start, end); - if(url_string.startsWith("//")) - { - int slash = url_string.indexOf('/', 2); - if (slash == -1) - { - throw new RuntimeException("ikvmres: URLs must contain path"); - } - String assembly = unquote(url_string.substring(2, slash)); - String file = unquote(url_string.substring(slash)); - setURL(url, "ikvmres", assembly, -1, file, null); - } - else if (url_string.startsWith("/")) - { - setURL(url, "ikvmres", url.getHost(), -1, url_string, null); - } - else - { - String[] baseparts = ((cli.System.String)(Object)url.getFile()).Split(new char[] { '/' }); - String[] relparts = ((cli.System.String)(Object)url_string).Split(new char[] { '/' }); - String[] target = new String[baseparts.length + relparts.length - 1]; - for(int i = 1; i < baseparts.length; i++) - { - target[i - 1] = baseparts[i]; - } - int p = baseparts.length - 2; - for(int i = 0; i < relparts.length; i++) - { - if(relparts[i].equals(".")) - { - - } - else if (relparts[i].equals("..")) - { - p = Math.max(0, p - 1); - } - else - { - target[p++] = relparts[i]; - } - } - StringBuffer file = new StringBuffer(); - for (int i = 0; i < p; i++) - { - file.append('/').append(target[i]); - } - setURL(url, "ikvmres", url.getHost(), -1, file.toString(), null); - } - } - catch(URISyntaxException x) - { - throw new RuntimeException(x.getMessage()); - } - } - - protected String toExternalForm(URL url) - { - // NOTE originally I wanted to use java.net.URI to handle parsing and constructing of these things, - // but it turns out that URI uses regex and that depends on resource loading... - return "ikvmres://" + quote(url.getHost(), RFC2396_REG_NAME) + quote(url.getFile(), RFC2396_PATH_SEGMENTS); - } - - protected InetAddress getHostAddress(URL url) - { - return null; - } - - private static String quote (String str, String legalCharacters) - { - StringBuffer sb = new StringBuffer(str.length()); - for (int i = 0; i < str.length(); i++) - { - char c = str.charAt(i); - if (legalCharacters.indexOf(c) == -1) - { - String hex = "0123456789ABCDEF"; - if (c <= 127) - { - sb.append('%') - .append(hex.charAt(c / 16)) - .append(hex.charAt(c % 16)); - } - else - { - try - { - // this is far from optimal, but it works - byte[] utf8 = str.substring(i, i + 1).getBytes("utf-8"); - for (int j = 0; j < utf8.length; j++) - { - sb.append('%') - .append(hex.charAt((utf8[j] & 0xff) / 16)) - .append(hex.charAt((utf8[j] & 0xff) % 16)); - } - } - catch (java.io.UnsupportedEncodingException x) - { - throw (Error)new InternalError().initCause(x); - } - } - } - else - { - sb.append(c); - } - } - return sb.toString(); - } - - private static String unquote (String str) - throws URISyntaxException - { - if (str == null) - return null; - byte[] buf = new byte[str.length()]; - int pos = 0; - for (int i = 0; i < str.length(); i++) - { - char c = str.charAt(i); - if (c > 127) - throw new URISyntaxException(str, "Invalid character"); - if (c == '%') - { - if (i + 2 >= str.length()) - throw new URISyntaxException(str, "Invalid quoted character"); - String hex = "0123456789ABCDEF"; - int hi = hex.indexOf(str.charAt(++i)); - int lo = hex.indexOf(str.charAt(++i)); - if (lo < 0 || hi < 0) - throw new URISyntaxException(str, "Invalid quoted character"); - buf[pos++] = (byte)(hi * 16 + lo); - } - else - { - buf[pos++] = (byte)c; - } - } - try - { - return new String(buf, 0, pos, "utf-8"); - } - catch (java.io.UnsupportedEncodingException x2) - { - throw (Error)new InternalError().initCause(x2); - } - - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicBoolean.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicBoolean.java deleted file mode 100644 index 6c1f1b1fac..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicBoolean.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; - -/** - * A {@code boolean} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicBoolean} is used in applications such as atomically - * updated flags, and cannot be used as a replacement for a - * {@link java.lang.Boolean}. - * - * @since 1.5 - * @author Doug Lea - */ -public class AtomicBoolean implements java.io.Serializable { - private static final long serialVersionUID = 4654671469794556979L; - - private volatile int value; - - /** - * Creates a new {@code AtomicBoolean} with the given initial value. - * - * @param initialValue the initial value - */ - public AtomicBoolean(boolean initialValue) { - value = initialValue ? 1 : 0; - } - - /** - * Creates a new {@code AtomicBoolean} with initial value {@code false}. - */ - public AtomicBoolean() { - } - - /** - * Returns the current value. - * - * @return the current value - */ - public final boolean get() { - return value != 0; - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final boolean compareAndSet(boolean expect, boolean update) { - int e = expect ? 1 : 0; - int u = update ? 1 : 0; - return compareAndSwapInt(e, u); - } - // implemented in map.xml - private native boolean compareAndSwapInt(int expected, int update); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public boolean weakCompareAndSet(boolean expect, boolean update) { - return compareAndSet(expect, update); - } - - /** - * Unconditionally sets to the given value. - * - * @param newValue the new value - */ - public final void set(boolean newValue) { - value = newValue ? 1 : 0; - } - - /** - * Eventually sets to the given value. - * - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(boolean newValue) { - value = newValue ? 1 : 0; - } - - /** - * Atomically sets to the given value and returns the previous value. - * - * @param newValue the new value - * @return the previous value - */ - public final boolean getAndSet(boolean newValue) { - boolean prev; - do { - prev = get(); - } while (!compareAndSet(prev, newValue)); - return prev; - } - - /** - * Returns the String representation of the current value. - * @return the String representation of the current value - */ - public String toString() { - return Boolean.toString(get()); - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicInteger.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicInteger.java deleted file mode 100644 index 11753d4ef2..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicInteger.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.IntUnaryOperator; -import java.util.function.IntBinaryOperator; - -/** - * An {@code int} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicInteger} is used in applications such as atomically - * incremented counters, and cannot be used as a replacement for an - * {@link java.lang.Integer}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. - * - * @since 1.5 - * @author Doug Lea -*/ -public class AtomicInteger extends Number implements java.io.Serializable { - private static final long serialVersionUID = 6214790243416807050L; - - private volatile int value; - - /** - * Creates a new AtomicInteger with the given initial value. - * - * @param initialValue the initial value - */ - public AtomicInteger(int initialValue) { - value = initialValue; - } - - /** - * Creates a new AtomicInteger with initial value {@code 0}. - */ - public AtomicInteger() { - } - - /** - * Gets the current value. - * - * @return the current value - */ - public final int get() { - return value; - } - - /** - * Sets to the given value. - * - * @param newValue the new value - */ - public final void set(int newValue) { - value = newValue; - } - - /** - * Eventually sets to the given value. - * - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(int newValue) { - value = newValue; - } - - /** - * Atomically sets to the given value and returns the old value. - * - * @param newValue the new value - * @return the previous value - */ - public final native int getAndSet(int newValue); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(int expect, int update); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(int expect, int update) { - return compareAndSet(expect, update); - } - - /** - * Atomically increments by one the current value. - * - * @return the previous value - */ - public final int getAndIncrement() { - return incrementAndGet() - 1; - } - - /** - * Atomically decrements by one the current value. - * - * @return the previous value - */ - public final int getAndDecrement() { - return decrementAndGet() + 1; - } - - /** - * Atomically adds the given value to the current value. - * - * @param delta the value to add - * @return the previous value - */ - public final int getAndAdd(int delta) { - return addAndGet(delta) - delta; - } - - /** - * Atomically increments by one the current value. - * - * @return the updated value - */ - public final native int incrementAndGet(); - - /** - * Atomically decrements by one the current value. - * - * @return the updated value - */ - public final native int decrementAndGet(); - - /** - * Atomically adds the given value to the current value. - * - * @param delta the value to add - * @return the updated value - */ - public final native int addAndGet(int delta); - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final int getAndUpdate(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final int updateAndGet(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the previous value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final int getAndAccumulate(int x, - IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the updated value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final int accumulateAndGet(int x, - IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Returns the String representation of the current value. - * @return the String representation of the current value - */ - public String toString() { - return Integer.toString(get()); - } - - /** - * Returns the value of this {@code AtomicInteger} as an {@code int}. - */ - public int intValue() { - return get(); - } - - /** - * Returns the value of this {@code AtomicInteger} as a {@code long} - * after a widening primitive conversion. - * @jls 5.1.2 Widening Primitive Conversions - */ - public long longValue() { - return (long)get(); - } - - /** - * Returns the value of this {@code AtomicInteger} as a {@code float} - * after a widening primitive conversion. - * @jls 5.1.2 Widening Primitive Conversions - */ - public float floatValue() { - return (float)get(); - } - - /** - * Returns the value of this {@code AtomicInteger} as a {@code double} - * after a widening primitive conversion. - * @jls 5.1.2 Widening Primitive Conversions - */ - public double doubleValue() { - return (double)get(); - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicIntegerArray.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicIntegerArray.java deleted file mode 100644 index 893ac07139..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicIntegerArray.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.IntUnaryOperator; -import java.util.function.IntBinaryOperator; - -/** - * An {@code int} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. - * @since 1.5 - * @author Doug Lea - */ -public class AtomicIntegerArray implements java.io.Serializable { - private static final long serialVersionUID = 2862133569453604235L; - - private final int[] array; - - /** - * Creates a new AtomicIntegerArray of the given length, with all - * elements initially zero. - * - * @param length the length of the array - */ - public AtomicIntegerArray(int length) { - array = new int[length]; - } - - /** - * Creates a new AtomicIntegerArray with the same length as, and - * all elements copied from, the given array. - * - * @param array the array to copy elements from - * @throws NullPointerException if array is null - */ - public AtomicIntegerArray(int[] array) { - // Visibility guaranteed by final field guarantees - this.array = array.clone(); - } - - /** - * Returns the length of the array. - * - * @return the length of the array - */ - public final int length() { - return array.length; - } - - /** - * Gets the current value at position {@code i}. - * - * @param i the index - * @return the current value - */ - public final native int get(int i); - - /** - * Sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - */ - public final native void set(int i, int newValue); - - /** - * Eventually sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(int i, int newValue) { - set(i, newValue); - } - - /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. - * - * @param i the index - * @param newValue the new value - * @return the previous value - */ - public final native int getAndSet(int i, int newValue); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(int i, int expect, int update); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(int i, int expect, int update) { - return compareAndSet(i, expect, update); - } - - /** - * Atomically increments by one the element at index {@code i}. - * - * @param i the index - * @return the previous value - */ - public final int getAndIncrement(int i) { - return incrementAndGet(i) - 1; - } - - /** - * Atomically decrements by one the element at index {@code i}. - * - * @param i the index - * @return the previous value - */ - public final int getAndDecrement(int i) { - return decrementAndGet(i) + 1; - } - - /** - * Atomically adds the given value to the element at index {@code i}. - * - * @param i the index - * @param delta the value to add - * @return the previous value - */ - public final int getAndAdd(int i, int delta) { - return addAndGet(i, delta) - delta; - } - - /** - * Atomically increments by one the element at index {@code i}. - * - * @param i the index - * @return the updated value - */ - public final native int incrementAndGet(int i); - - /** - * Atomically decrements by one the element at index {@code i}. - * - * @param i the index - * @return the updated value - */ - public final native int decrementAndGet(int i); - - /** - * Atomically adds the given value to the element at index {@code i}. - * - * @param i the index - * @param delta the value to add - * @return the updated value - */ - public final native int addAndGet(int i, int delta); - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(i); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final int updateAndGet(int i, IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(i); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final int getAndAccumulate(int i, int x, - IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(i); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final int accumulateAndGet(int i, int x, - IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(i); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Returns the String representation of the current values of array. - * @return the String representation of the current values of array. - */ - public String toString() { - int iMax = array.length - 1; - if (iMax == -1) - return "[]"; - - StringBuilder b = new StringBuilder(); - b.append('['); - for (int i = 0; ; i++) { - b.append(get(i)); - if (i == iMax) - return b.append(']').toString(); - b.append(',').append(' '); - } - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLong.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLong.java deleted file mode 100644 index 0d07fb10d6..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLong.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.LongUnaryOperator; -import java.util.function.LongBinaryOperator; - -/** - * A {@code long} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicLong} is used in applications such as atomically - * incremented sequence numbers, and cannot be used as a replacement - * for a {@link java.lang.Long}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. - * - * @since 1.5 - * @author Doug Lea - */ -public class AtomicLong extends Number implements java.io.Serializable { - private static final long serialVersionUID = 1927816293512124184L; - - /** - * Records whether the underlying JVM supports lockless - * compareAndSwap for longs. While the Unsafe.compareAndSwapLong - * method works in either case, some constructions should be - * handled at Java level to avoid locking user-visible locks. - */ - static final boolean VM_SUPPORTS_LONG_CAS = false; - - private volatile long value; - - /** - * Creates a new AtomicLong with the given initial value. - * - * @param initialValue the initial value - */ - public AtomicLong(long initialValue) { - value = initialValue; - } - - /** - * Creates a new AtomicLong with initial value {@code 0}. - */ - public AtomicLong() { - } - - /** - * Gets the current value. - * - * @return the current value - */ - public final long get() { - return value; - } - - /** - * Sets to the given value. - * - * @param newValue the new value - */ - public final void set(long newValue) { - value = newValue; - } - - /** - * Eventually sets to the given value. - * - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(long newValue) { - value = newValue; - } - - /** - * Atomically sets to the given value and returns the old value. - * - * @param newValue the new value - * @return the previous value - */ - public final native long getAndSet(long newValue); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(long expect, long update); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(long expect, long update) { - return compareAndSet(expect, update); - } - - /** - * Atomically increments by one the current value. - * - * @return the previous value - */ - public final long getAndIncrement() { - return incrementAndGet() - 1; - } - - /** - * Atomically decrements by one the current value. - * - * @return the previous value - */ - public final long getAndDecrement() { - return decrementAndGet() + 1; - } - - /** - * Atomically adds the given value to the current value. - * - * @param delta the value to add - * @return the previous value - */ - public final long getAndAdd(long delta) { - return addAndGet(delta) - delta; - } - - /** - * Atomically increments by one the current value. - * - * @return the updated value - */ - public final native long incrementAndGet(); - - /** - * Atomically decrements by one the current value. - * - * @return the updated value - */ - public final native long decrementAndGet(); - - /** - * Atomically adds the given value to the current value. - * - * @param delta the value to add - * @return the updated value - */ - public final native long addAndGet(long delta); - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final long getAndUpdate(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final long updateAndGet(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the previous value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final long getAndAccumulate(long x, - LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the updated value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final long accumulateAndGet(long x, - LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Returns the String representation of the current value. - * @return the String representation of the current value - */ - public String toString() { - return Long.toString(get()); - } - - /** - * Returns the value of this {@code AtomicLong} as an {@code int} - * after a narrowing primitive conversion. - * @jls 5.1.3 Narrowing Primitive Conversions - */ - public int intValue() { - return (int)get(); - } - - /** - * Returns the value of this {@code AtomicLong} as a {@code long}. - */ - public long longValue() { - return get(); - } - - /** - * Returns the value of this {@code AtomicLong} as a {@code float} - * after a widening primitive conversion. - * @jls 5.1.2 Widening Primitive Conversions - */ - public float floatValue() { - return (float)get(); - } - - /** - * Returns the value of this {@code AtomicLong} as a {@code double} - * after a widening primitive conversion. - * @jls 5.1.2 Widening Primitive Conversions - */ - public double doubleValue() { - return (double)get(); - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLongArray.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLongArray.java deleted file mode 100644 index 599a2cdf58..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicLongArray.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.LongUnaryOperator; -import java.util.function.LongBinaryOperator; - -/** - * A {@code long} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package specification - * for description of the properties of atomic variables. - * @since 1.5 - * @author Doug Lea - */ -public class AtomicLongArray implements java.io.Serializable { - private static final long serialVersionUID = -2308431214976778248L; - - private final long[] array; - - /** - * Creates a new AtomicLongArray of the given length, with all - * elements initially zero. - * - * @param length the length of the array - */ - public AtomicLongArray(int length) { - array = new long[length]; - } - - /** - * Creates a new AtomicLongArray with the same length as, and - * all elements copied from, the given array. - * - * @param array the array to copy elements from - * @throws NullPointerException if array is null - */ - public AtomicLongArray(long[] array) { - // Visibility guaranteed by final field guarantees - this.array = array.clone(); - } - - /** - * Returns the length of the array. - * - * @return the length of the array - */ - public final int length() { - return array.length; - } - - /** - * Gets the current value at position {@code i}. - * - * @param i the index - * @return the current value - */ - public final native long get(int i); - - /** - * Sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - */ - public final native void set(int i, long newValue); - - /** - * Eventually sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(int i, long newValue) { - set(i, newValue); - } - - /** - * Atomically sets the element at position {@code i} to the given value - * and returns the old value. - * - * @param i the index - * @param newValue the new value - * @return the previous value - */ - public final native long getAndSet(int i, long newValue); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(int i, long expect, long update); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(int i, long expect, long update) { - return compareAndSet(i, expect, update); - } - - /** - * Atomically increments by one the element at index {@code i}. - * - * @param i the index - * @return the previous value - */ - public final long getAndIncrement(int i) { - return incrementAndGet(i) - 1; - } - - /** - * Atomically decrements by one the element at index {@code i}. - * - * @param i the index - * @return the previous value - */ - public final long getAndDecrement(int i) { - return decrementAndGet(i) + 1; - } - - /** - * Atomically adds the given value to the element at index {@code i}. - * - * @param i the index - * @param delta the value to add - * @return the previous value - */ - public final long getAndAdd(int i, long delta) { - return addAndGet(i, delta) - delta; - } - - /** - * Atomically increments by one the element at index {@code i}. - * - * @param i the index - * @return the updated value - */ - public final native long incrementAndGet(int i); - - /** - * Atomically decrements by one the element at index {@code i}. - * - * @param i the index - * @return the updated value - */ - public final native long decrementAndGet(int i); - - /** - * Atomically adds the given value to the element at index {@code i}. - * - * @param i the index - * @param delta the value to add - * @return the updated value - */ - public native long addAndGet(int i, long delta); - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final long getAndUpdate(int i, LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(i); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final long updateAndGet(int i, LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(i); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final long getAndAccumulate(int i, long x, - LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(i); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final long accumulateAndGet(int i, long x, - LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(i); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Returns the String representation of the current values of array. - * @return the String representation of the current values of array - */ - public String toString() { - int iMax = array.length - 1; - if (iMax == -1) - return "[]"; - - StringBuilder b = new StringBuilder(); - b.append('['); - for (int i = 0; ; i++) { - b.append(get(i)); - if (i == iMax) - return b.append(']').toString(); - b.append(',').append(' '); - } - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReference.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReference.java deleted file mode 100644 index f36b59d023..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReference.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.UnaryOperator; -import java.util.function.BinaryOperator; - -/** - * An object reference that may be updated atomically. See the {@link - * java.util.concurrent.atomic} package specification for description - * of the properties of atomic variables. - * @since 1.5 - * @author Doug Lea - * @param The type of object referred to by this reference - */ -public class AtomicReference implements java.io.Serializable { - private static final long serialVersionUID = -1848883965231344442L; - - private volatile V value; - - /** - * Creates a new AtomicReference with the given initial value. - * - * @param initialValue the initial value - */ - public AtomicReference(V initialValue) { - value = initialValue; - } - - /** - * Creates a new AtomicReference with null initial value. - */ - public AtomicReference() { - } - - /** - * Gets the current value. - * - * @return the current value - */ - public final V get() { - return value; - } - - /** - * Sets to the given value. - * - * @param newValue the new value - */ - public final void set(V newValue) { - value = newValue; - } - - /** - * Eventually sets to the given value. - * - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(V newValue) { - value = newValue; - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(V expect, V update); - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(V expect, V update) { - return compareAndSet(expect, update); - } - - /** - * Atomically sets to the given value and returns the old value. - * - * @param newValue the new value - * @return the previous value - */ - @SuppressWarnings("unchecked") - public final native V getAndSet(V newValue); - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final V getAndUpdate(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final V updateAndGet(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the previous value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final V getAndAccumulate(V x, - BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return prev; - } - - /** - * Atomically updates the current value with the results of - * applying the given function to the current and given values, - * returning the updated value. The function should be - * side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function - * is applied with the current value as its first argument, - * and the given update as the second argument. - * - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final V accumulateAndGet(V x, - BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return next; - } - - /** - * Returns the String representation of the current value. - * @return the String representation of the current value - */ - public String toString() { - return String.valueOf(get()); - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java deleted file mode 100644 index 1e02492e69..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/AtomicReferenceArray.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.UnaryOperator; -import java.util.function.BinaryOperator; -import java.util.Arrays; -import java.lang.reflect.Array; -import sun.misc.Unsafe; - -/** - * An array of object references in which elements may be updated - * atomically. See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. - * @since 1.5 - * @author Doug Lea - * @param The base class of elements held in this array - */ -public class AtomicReferenceArray implements java.io.Serializable { - private static final long serialVersionUID = -6209656149925076980L; - - private static final Unsafe unsafe; - private static final long arrayFieldOffset; - private final Object[] array; // must have exact type Object[] - - static { - try { - unsafe = Unsafe.getUnsafe(); - arrayFieldOffset = unsafe.objectFieldOffset - (AtomicReferenceArray.class.getDeclaredField("array")); - } catch (Exception e) { - throw new Error(e); - } - } - - /** - * Creates a new AtomicReferenceArray of the given length, with all - * elements initially null. - * - * @param length the length of the array - */ - public AtomicReferenceArray(int length) { - array = new Object[length]; - } - - /** - * Creates a new AtomicReferenceArray with the same length as, and - * all elements copied from, the given array. - * - * @param array the array to copy elements from - * @throws NullPointerException if array is null - */ - public AtomicReferenceArray(E[] array) { - // Visibility guaranteed by final field guarantees - this.array = Arrays.copyOf(array, array.length, Object[].class); - } - - /** - * Returns the length of the array. - * - * @return the length of the array - */ - public final int length() { - return array.length; - } - - /** - * Gets the current value at position {@code i}. - * - * @param i the index - * @return the current value - */ - public final native E get(int i); - - /** - * Sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - */ - public final native void set(int i, E newValue); - - /** - * Eventually sets the element at position {@code i} to the given value. - * - * @param i the index - * @param newValue the new value - * @since 1.6 - */ - public final void lazySet(int i, E newValue) { - set(i, newValue); - } - - /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. - * - * @param i the index - * @param newValue the new value - * @return the previous value - */ - @SuppressWarnings("unchecked") - public final native E getAndSet(int i, E newValue); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final native boolean compareAndSet(int i, E expect, E update); - - /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param i the index - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful - */ - public final boolean weakCompareAndSet(int i, E expect, E update) { - return compareAndSet(i, expect, update); - } - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the previous value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the previous value - * @since 1.8 - */ - public final E getAndUpdate(int i, UnaryOperator updateFunction) { - E prev, next; - do { - prev = get(i); - next = updateFunction.apply(prev); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the results - * of applying the given function, returning the updated value. The - * function should be side-effect-free, since it may be re-applied - * when attempted updates fail due to contention among threads. - * - * @param i the index - * @param updateFunction a side-effect-free function - * @return the updated value - * @since 1.8 - */ - public final E updateAndGet(int i, UnaryOperator updateFunction) { - E prev, next; - do { - prev = get(i); - next = updateFunction.apply(prev); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the previous value - * @since 1.8 - */ - public final E getAndAccumulate(int i, E x, - BinaryOperator accumulatorFunction) { - E prev, next; - do { - prev = get(i); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(i, prev, next)); - return prev; - } - - /** - * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted - * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. - * - * @param i the index - * @param x the update value - * @param accumulatorFunction a side-effect-free function of two arguments - * @return the updated value - * @since 1.8 - */ - public final E accumulateAndGet(int i, E x, - BinaryOperator accumulatorFunction) { - E prev, next; - do { - prev = get(i); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(i, prev, next)); - return next; - } - - /** - * Returns the String representation of the current values of array. - * @return the String representation of the current values of array - */ - public String toString() { - int iMax = array.length - 1; - if (iMax == -1) - return "[]"; - - StringBuilder b = new StringBuilder(); - b.append('['); - for (int i = 0; ; i++) { - b.append(get(i)); - if (i == iMax) - return b.append(']').toString(); - b.append(',').append(' '); - } - } - - /** - * Reconstitutes the instance from a stream (that is, deserializes it). - */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException, - java.io.InvalidObjectException { - // Note: This must be changed if any additional fields are defined - Object a = s.readFields().get("array", null); - if (a == null || !a.getClass().isArray()) - throw new java.io.InvalidObjectException("Not array type"); - if (a.getClass() != Object[].class) - a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class); - unsafe.putObjectVolatile(this, arrayFieldOffset, a); - } - -} diff --git a/src/IKVM.Java/classpath/java/util/concurrent/atomic/Striped64.java b/src/IKVM.Java/classpath/java/util/concurrent/atomic/Striped64.java deleted file mode 100644 index 1fea75bed6..0000000000 --- a/src/IKVM.Java/classpath/java/util/concurrent/atomic/Striped64.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package java.util.concurrent.atomic; -import java.util.function.LongBinaryOperator; -import java.util.function.DoubleBinaryOperator; -import java.util.concurrent.ThreadLocalRandom; - -/** - * A package-local class holding common representation and mechanics - * for classes supporting dynamic striping on 64bit values. The class - * extends Number so that concrete subclasses must publicly do so. - */ -@SuppressWarnings("serial") -abstract class Striped64 extends Number { - /* - * This class maintains a lazily-initialized table of atomically - * updated variables, plus an extra "base" field. The table size - * is a power of two. Indexing uses masked per-thread hash codes. - * Nearly all declarations in this class are package-private, - * accessed directly by subclasses. - * - * Table entries are of class Cell; a variant of AtomicLong padded - * (via @sun.misc.Contended) to reduce cache contention. Padding - * is overkill for most Atomics because they are usually - * irregularly scattered in memory and thus don't interfere much - * with each other. But Atomic objects residing in arrays will - * tend to be placed adjacent to each other, and so will most - * often share cache lines (with a huge negative performance - * impact) without this precaution. - * - * In part because Cells are relatively large, we avoid creating - * them until they are needed. When there is no contention, all - * updates are made to the base field. Upon first contention (a - * failed CAS on base update), the table is initialized to size 2. - * The table size is doubled upon further contention until - * reaching the nearest power of two greater than or equal to the - * number of CPUS. Table slots remain empty (null) until they are - * needed. - * - * A single spinlock ("cellsBusy") is used for initializing and - * resizing the table, as well as populating slots with new Cells. - * There is no need for a blocking lock; when the lock is not - * available, threads try other slots (or the base). During these - * retries, there is increased contention and reduced locality, - * which is still better than alternatives. - * - * The Thread probe fields maintained via ThreadLocalRandom serve - * as per-thread hash codes. We let them remain uninitialized as - * zero (if they come in this way) until they contend at slot - * 0. They are then initialized to values that typically do not - * often conflict with others. Contention and/or table collisions - * are indicated by failed CASes when performing an update - * operation. Upon a collision, if the table size is less than - * the capacity, it is doubled in size unless some other thread - * holds the lock. If a hashed slot is empty, and lock is - * available, a new Cell is created. Otherwise, if the slot - * exists, a CAS is tried. Retries proceed by "double hashing", - * using a secondary hash (Marsaglia XorShift) to try to find a - * free slot. - * - * The table size is capped because, when there are more threads - * than CPUs, supposing that each thread were bound to a CPU, - * there would exist a perfect hash function mapping threads to - * slots that eliminates collisions. When we reach capacity, we - * search for this mapping by randomly varying the hash codes of - * colliding threads. Because search is random, and collisions - * only become known via CAS failures, convergence can be slow, - * and because threads are typically not bound to CPUS forever, - * may not occur at all. However, despite these limitations, - * observed contention rates are typically low in these cases. - * - * It is possible for a Cell to become unused when threads that - * once hashed to it terminate, as well as in the case where - * doubling the table causes no thread to hash to it under - * expanded mask. We do not try to detect or remove such cells, - * under the assumption that for long-running instances, observed - * contention levels will recur, so the cells will eventually be - * needed again; and for short-lived ones, it does not matter. - */ - - /** - * Padded variant of AtomicLong supporting only raw accesses plus CAS. - * - * JVM intrinsics note: It would be possible to use a release-only - * form of CAS here, if it were provided. - */ - @sun.misc.Contended static final class Cell { - volatile long value; - Cell(long x) { value = x; } - @ikvm.internal.InterlockedCompareAndSet("value") - final native boolean cas(long cmp, long val); - } - - /** Number of CPUS, to place bound on table size */ - static final int NCPU = Runtime.getRuntime().availableProcessors(); - - /** - * Table of cells. When non-null, size is a power of 2. - */ - transient volatile Cell[] cells; - - /** - * Base value, used mainly when there is no contention, but also as - * a fallback during table initialization races. Updated via CAS. - */ - transient volatile long base; - - /** - * Spinlock (locked via CAS) used when resizing and/or creating Cells. - */ - transient volatile int cellsBusy; - - /** - * Package-private default constructor - */ - Striped64() { - } - - /** - * CASes the base field. - */ - @ikvm.internal.InterlockedCompareAndSet("base") - final native boolean casBase(long cmp, long val); - - /** - * CASes the cellsBusy field from 0 to 1 to acquire lock. - */ - final boolean casCellsBusy() { - return casCellsBusy(0, 1); - } - - @ikvm.internal.InterlockedCompareAndSet("cellsBusy") - private native boolean casCellsBusy(int cmp, int newVal); - - /** - * Returns the probe value for the current thread. - * Duplicated from ThreadLocalRandom because of packaging restrictions. - */ - static final int getProbe() { - return Thread.currentThread().threadLocalRandomProbe; - } - - /** - * Pseudo-randomly advances and records the given probe value for the - * given thread. - * Duplicated from ThreadLocalRandom because of packaging restrictions. - */ - static final int advanceProbe(int probe) { - probe ^= probe << 13; // xorshift - probe ^= probe >>> 17; - probe ^= probe << 5; - Thread.currentThread().threadLocalRandomProbe = probe; - return probe; - } - - /** - * Handles cases of updates involving initialization, resizing, - * creating new Cells, and/or contention. See above for - * explanation. This method suffers the usual non-modularity - * problems of optimistic retry code, relying on rechecked sets of - * reads. - * - * @param x the value - * @param fn the update function, or null for add (this convention - * avoids the need for an extra field or function in LongAdder). - * @param wasUncontended false if CAS failed before call - */ - final void longAccumulate(long x, LongBinaryOperator fn, - boolean wasUncontended) { - int h; - if ((h = getProbe()) == 0) { - ThreadLocalRandom.current(); // force initialization - h = getProbe(); - wasUncontended = true; - } - boolean collide = false; // True if last slot nonempty - for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { - if (cellsBusy == 0) { // Try to attach new Cell - Cell r = new Cell(x); // Optimistically create - if (cellsBusy == 0 && casCellsBusy()) { - boolean created = false; - try { // Recheck under lock - Cell[] rs; int m, j; - if ((rs = cells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { - rs[j] = r; - created = true; - } - } finally { - cellsBusy = 0; - } - if (created) - break; - continue; // Slot is now non-empty - } - } - collide = false; - } - else if (!wasUncontended) // CAS already known to fail - wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, ((fn == null) ? v + x : - fn.applyAsLong(v, x)))) - break; - else if (n >= NCPU || cells != as) - collide = false; // At max size or stale - else if (!collide) - collide = true; - else if (cellsBusy == 0 && casCellsBusy()) { - try { - if (cells == as) { // Expand table unless stale - Cell[] rs = new Cell[n << 1]; - for (int i = 0; i < n; ++i) - rs[i] = as[i]; - cells = rs; - } - } finally { - cellsBusy = 0; - } - collide = false; - continue; // Retry with expanded table - } - h = advanceProbe(h); - } - else if (cellsBusy == 0 && cells == as && casCellsBusy()) { - boolean init = false; - try { // Initialize table - if (cells == as) { - Cell[] rs = new Cell[2]; - rs[h & 1] = new Cell(x); - cells = rs; - init = true; - } - } finally { - cellsBusy = 0; - } - if (init) - break; - } - else if (casBase(v = base, ((fn == null) ? v + x : - fn.applyAsLong(v, x)))) - break; // Fall back on using base - } - } - - /** - * Same as longAccumulate, but injecting long/double conversions - * in too many places to sensibly merge with long version, given - * the low-overhead requirements of this class. So must instead be - * maintained by copy/paste/adapt. - */ - final void doubleAccumulate(double x, DoubleBinaryOperator fn, - boolean wasUncontended) { - int h; - if ((h = getProbe()) == 0) { - ThreadLocalRandom.current(); // force initialization - h = getProbe(); - wasUncontended = true; - } - boolean collide = false; // True if last slot nonempty - for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { - if (cellsBusy == 0) { // Try to attach new Cell - Cell r = new Cell(Double.doubleToRawLongBits(x)); - if (cellsBusy == 0 && casCellsBusy()) { - boolean created = false; - try { // Recheck under lock - Cell[] rs; int m, j; - if ((rs = cells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { - rs[j] = r; - created = true; - } - } finally { - cellsBusy = 0; - } - if (created) - break; - continue; // Slot is now non-empty - } - } - collide = false; - } - else if (!wasUncontended) // CAS already known to fail - wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, - ((fn == null) ? - Double.doubleToRawLongBits - (Double.longBitsToDouble(v) + x) : - Double.doubleToRawLongBits - (fn.applyAsDouble - (Double.longBitsToDouble(v), x))))) - break; - else if (n >= NCPU || cells != as) - collide = false; // At max size or stale - else if (!collide) - collide = true; - else if (cellsBusy == 0 && casCellsBusy()) { - try { - if (cells == as) { // Expand table unless stale - Cell[] rs = new Cell[n << 1]; - for (int i = 0; i < n; ++i) - rs[i] = as[i]; - cells = rs; - } - } finally { - cellsBusy = 0; - } - collide = false; - continue; // Retry with expanded table - } - h = advanceProbe(h); - } - else if (cellsBusy == 0 && cells == as && casCellsBusy()) { - boolean init = false; - try { // Initialize table - if (cells == as) { - Cell[] rs = new Cell[2]; - rs[h & 1] = new Cell(Double.doubleToRawLongBits(x)); - cells = rs; - init = true; - } - } finally { - cellsBusy = 0; - } - if (init) - break; - } - else if (casBase(v = base, - ((fn == null) ? - Double.doubleToRawLongBits - (Double.longBitsToDouble(v) + x) : - Double.doubleToRawLongBits - (fn.applyAsDouble - (Double.longBitsToDouble(v), x))))) - break; // Fall back on using base - } - } - - -} diff --git a/src/IKVM.Java/local/ikvm/internal/CallerID.java b/src/IKVM.Java/local/ikvm/internal/CallerID.java index 342e9ff8a3..a5545b3eb2 100644 --- a/src/IKVM.Java/local/ikvm/internal/CallerID.java +++ b/src/IKVM.Java/local/ikvm/internal/CallerID.java @@ -27,113 +27,109 @@ import cli.System.Diagnostics.StackFrame; import cli.System.Reflection.Assembly; -public abstract class CallerID -{ - private Class clazz; - private ClassLoader classLoader; - - private CallerID(Class clazz, ClassLoader classLoader) - { - this.clazz = clazz; - this.classLoader = classLoader; - } - - protected CallerID() { } - - @ikvm.lang.Internal - public final Class getCallerClass() - { - if (clazz == null) - { - clazz = GetClass(); +public abstract class CallerID { + private Class clazz; + private ClassLoader classLoader; + + private CallerID(Class clazz, ClassLoader classLoader) { + this.clazz = clazz; + this.classLoader = classLoader; } - return clazz; - } - - @ikvm.lang.Internal - public final ClassLoader getCallerClassLoader() - { - ClassLoader cl = classLoader; - if (cl == null) - { - cl = classLoader = GetClassLoader(); - if (cl == null) - { - cl = classLoader = ClassLoader.DUMMY; - } + + protected CallerID() { + } - return cl == ClassLoader.DUMMY ? null : cl; - } - - @ikvm.lang.Internal - public static CallerID create(cli.System.Diagnostics.StackFrame frame) - { - return create(frame.GetMethod()); - } - - @ikvm.lang.Internal - public static CallerID create(final cli.System.Reflection.MethodBase method) - { - return new CallerID() { - Class GetClass() { - if (method == null) { - // this happens if a native thread attaches and calls back into Java - return null; - } - Type type = method.get_DeclaringType(); - if (type == null) { - // TODO we probably should return a class corresponding to - throw new InternalError(); + + @ikvm.lang.Internal + public final Class getCallerClass() { + if (clazz == null) { + clazz = GetClass(); } - return ikvm.runtime.Util.getClassFromTypeHandle(type.get_TypeHandle()); - } - ClassLoader GetClassLoader() { - if (method == null) { - // this happens if a native thread attaches and calls back into Java - return null; + + return clazz; + } + + @ikvm.lang.Internal + public final ClassLoader getCallerClassLoader() { + ClassLoader cl = classLoader; + if (cl == null) { + cl = classLoader = GetClassLoader(); + if (cl == null) { + cl = classLoader = ClassLoader.DUMMY; + } } - Assembly asm = method.get_Module().get_Assembly(); - return GetAssemblyClassLoader(asm); - } - }; - } - - // this is a shortcut for use inside the core class library, it removes the need to create a nested type for every caller - @ikvm.lang.Internal - public static CallerID create(final cli.System.RuntimeTypeHandle typeHandle) - { - return new CallerID() { - Class GetClass() { - return ikvm.runtime.Util.getClassFromTypeHandle(typeHandle); - } - ClassLoader GetClassLoader() { - // since this optimization is only available inside the core class library, we know that the class loader is null - return null; - } - }; - } - - // used by the runtime for EmitHostCallerID and DynamicCallerID - static CallerID create(Class clazz, ClassLoader classLoader) - { - return new CallerID(clazz, classLoader) { - Class GetClass() { - return null; - } - ClassLoader GetClassLoader() { + + return cl == ClassLoader.DUMMY ? null : cl; + } + + @ikvm.lang.Internal + public static CallerID create(cli.System.Diagnostics.StackFrame frame) { + return create(frame.GetMethod()); + } + + @ikvm.lang.Internal + public static CallerID create(final cli.System.Reflection.MethodBase method) { + return new CallerID() { + Class GetClass() { + if (method == null) { + // this happens if a native thread attaches and calls back into Java + return null; + } + Type type = method.get_DeclaringType(); + if (type == null) { + // TODO we probably should return a class corresponding to + throw new InternalError("No declaring type for " + method.get_Name() + "."); + } + + return ikvm.runtime.Util.getClassFromTypeHandle(type.get_TypeHandle()); + } + + ClassLoader GetClassLoader() { + if (method == null) { + // this happens if a native thread attaches and calls back into Java + return null; + } + + Assembly asm = method.get_Module().get_Assembly(); + return GetAssemblyClassLoader(asm); + } + }; + } + + // this is a shortcut for use inside the core class library, it removes the need to create a nested type for every caller + @ikvm.lang.Internal + public static CallerID create(final cli.System.RuntimeTypeHandle typeHandle) { + return new CallerID() { + Class GetClass() { + return ikvm.runtime.Util.getClassFromTypeHandle(typeHandle); + } + ClassLoader GetClassLoader() { + // since this optimization is only available inside the core class library, we know that the class loader is null + return null; + } + }; + } + + // used by the runtime for EmitHostCallerID and DynamicCallerID + static CallerID create(Class clazz, ClassLoader classLoader) { + return new CallerID(clazz, classLoader) { + Class GetClass() { + return null; + } + ClassLoader GetClassLoader() { + return null; + } + }; + } + + @ikvm.lang.Internal + public static CallerID getCallerID() { + // this is a compiler intrinsic, so there is no meaningful implementation here return null; - } - }; - } - - @ikvm.lang.Internal - public static CallerID getCallerID() - { - // this is a compiler intrinsic, so there is no meaningful implementation here - return null; - } - - native Class GetClass(); - native ClassLoader GetClassLoader(); - static native ClassLoader GetAssemblyClassLoader(Assembly asm); + } + + native Class GetClass(); + native ClassLoader GetClassLoader(); + static native ClassLoader GetAssemblyClassLoader(Assembly asm); + } diff --git a/src/IKVM.Java/local/ikvm/internal/Serialization.java b/src/IKVM.Java/local/ikvm/internal/Serialization.java index 28fec14e78..ae1ce7a913 100644 --- a/src/IKVM.Java/local/ikvm/internal/Serialization.java +++ b/src/IKVM.Java/local/ikvm/internal/Serialization.java @@ -24,24 +24,21 @@ package ikvm.internal; import cli.System.Runtime.Serialization.SerializationInfo; -import cli.System.Security.Permissions.SecurityAction; -import cli.System.Security.Permissions.SecurityPermissionAttribute; import java.io.InteropObjectInputStream; import java.io.InteropObjectOutputStream; -public final class Serialization -{ - private Serialization() { } +public final class Serialization { + + private Serialization() { + + } - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.Demand, SerializationFormatter = true) - public static void writeObject(Object obj, SerializationInfo info) - { + public static void writeObject(Object obj, SerializationInfo info) { InteropObjectOutputStream.writeObject(obj, info); } - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.Demand, SerializationFormatter = true) - public static void readObject(Object obj, SerializationInfo info) - { + public static void readObject(Object obj, SerializationInfo info) { InteropObjectInputStream.readObject(obj, info); } + } diff --git a/src/IKVM.Java/local/ikvm/internal/Util.java b/src/IKVM.Java/local/ikvm/internal/Util.java index a4784c5e6a..02e667e7d0 100644 --- a/src/IKVM.Java/local/ikvm/internal/Util.java +++ b/src/IKVM.Java/local/ikvm/internal/Util.java @@ -10,11 +10,6 @@ @Internal public final class Util { - public static boolean WINDOWS = RuntimeInformation.IsOSPlatform(OSPlatform.get_Windows()); - public static boolean LINUX = RuntimeInformation.IsOSPlatform(OSPlatform.get_Linux()); - public static boolean MACOSX = RuntimeInformation.IsOSPlatform(OSPlatform.get_OSX()); - public static boolean MONO = Type.GetType("Mono.Runtime") != null; - public static boolean rangeCheck(int arrayLength, int offset, int length) { return offset >= 0 && offset <= arrayLength && length >= 0 && length <= arrayLength - offset; } diff --git a/src/IKVM.Java/local/ikvm/runtime/Launcher.java b/src/IKVM.Java/local/ikvm/runtime/Launcher.java index 6acb0f36fc..da31e65049 100644 --- a/src/IKVM.Java/local/ikvm/runtime/Launcher.java +++ b/src/IKVM.Java/local/ikvm/runtime/Launcher.java @@ -16,6 +16,7 @@ private Launcher() * Launches the given .NET type as a Java application class. This method should be invoked before any other JVM work is * conducted, ideally by generated executables. */ + @cli.IKVM.Attributes.HideFromJavaAttribute.Annotation public static native int run(Type main, String[] args, String jvmArgPrefix, Properties properties); } diff --git a/src/IKVM.Java/local/ikvm/util/EnumeratorIterator.java b/src/IKVM.Java/local/ikvm/util/EnumeratorIterator.java new file mode 100644 index 0000000000..b8dc32607c --- /dev/null +++ b/src/IKVM.Java/local/ikvm/util/EnumeratorIterator.java @@ -0,0 +1,37 @@ +package ikvm.util; + +import java.util.*; + +import cli.System.Collections.IEnumerator; + +public final class EnumeratorIterator implements Iterator { + + final IEnumerator enumerator; + boolean didMove; + boolean hasNext; + + public EnumeratorIterator(IEnumerator enumerator) { + this.enumerator = enumerator; + } + + @Override + public boolean hasNext() { + if (didMove == false) { + hasNext = enumerator.MoveNext(); + didMove = true; + } + + return hasNext; + } + + @Override + public E next() { + if (hasNext() == false) { + throw new NoSuchElementException(); + } + + didMove = false; + return (E)enumerator.get_Current(); + } + +} \ No newline at end of file diff --git a/src/IKVM.Java/local/java/awt/Font.java b/src/IKVM.Java/local/java/awt/Font.java index d99c6757c1..2e4a464919 100644 --- a/src/IKVM.Java/local/java/awt/Font.java +++ b/src/IKVM.Java/local/java/awt/Font.java @@ -942,7 +942,7 @@ public File run() throws IOException { @cli.System.Security.SecuritySafeCriticalAttribute.Annotation private static void RemoveFontResourceEx(String filename) { - if( ikvm.internal.Util.WINDOWS ) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { RemoveFontResourceEx( filename, 16, IntPtr.Zero ); } } diff --git a/src/IKVM.Java/local/java/io/DefaultFileSystem.java b/src/IKVM.Java/local/java/io/DefaultFileSystem.java index ce12131482..64e84b7964 100644 --- a/src/IKVM.Java/local/java/io/DefaultFileSystem.java +++ b/src/IKVM.Java/local/java/io/DefaultFileSystem.java @@ -27,7 +27,7 @@ class DefaultFileSystem { static FileSystem getFileSystem() { - return ikvm.internal.Util.WINDOWS ? new WinNTFileSystem() : new UnixFileSystem(); + return cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() ? new WinNTFileSystem() : new UnixFileSystem(); } } diff --git a/src/IKVM.Java/local/java/io/FileDescriptor.java b/src/IKVM.Java/local/java/io/FileDescriptor.java index 65d07f9fc6..379fc05894 100644 --- a/src/IKVM.Java/local/java/io/FileDescriptor.java +++ b/src/IKVM.Java/local/java/io/FileDescriptor.java @@ -1,41 +1,8 @@ -/* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - package java.io; import java.util.ArrayList; import java.util.List; -import cli.System.IO.FileAccess; -import cli.System.IO.FileMode; -import cli.System.IO.FileShare; -import cli.System.IO.FileStream; -import cli.System.IO.SeekOrigin; -import cli.System.Net.Sockets.SocketError; -import cli.System.Runtime.InteropServices.DllImportAttribute; - /** * Instances of the file descriptor class serve as an opaque handle * to the underlying machine-specific structure representing an @@ -48,91 +15,77 @@ * @author Pavani Diwanji * @since JDK1.0 */ -public final class FileDescriptor -{ +public final class FileDescriptor { - private volatile cli.System.IO.Stream stream; - private volatile cli.System.Net.Sockets.Socket socket; - private volatile boolean nonBlockingSocket; - private volatile cli.System.IAsyncResult asyncResult; + // set up JavaIOFileDescriptorAccess in SharedSecrets + static { + sun.misc.SharedSecrets.setJavaIOFileDescriptorAccess( + new sun.misc.JavaIOFileDescriptorAccess() { + public void set(FileDescriptor obj, int fd) { - /** - * HACK - * JRuby uses reflection to get at the handle (on Windows) and fd (on non-Windows) - * fields to convert it into a native handle and query if it is a tty. - */ - @ikvm.lang.Property(get = "get_fd") - private int fd; + } - @ikvm.lang.Property(get = "get_handle") - private long handle; + public int get(FileDescriptor obj) { + return obj.fd; + } - private Closeable parent; - private List otherParents; - private boolean closed; - - @cli.System.Security.SecurityCriticalAttribute.Annotation - private long get_handle() - { - if (ikvm.internal.Util.WINDOWS) - { - if (stream instanceof cli.System.IO.FileStream) - { - cli.System.IO.FileStream fs = (cli.System.IO.FileStream)stream; - return fs.get_Handle().ToInt64(); - } - else if (this == in) - { - return GetStdHandle(-10).ToInt64(); - } - else if (this == out) - { - return GetStdHandle(-11).ToInt64(); - } - else if (this == err) - { - return GetStdHandle(-12).ToInt64(); + public void setHandle(FileDescriptor obj, long handle) { + + } + + public long getHandle(FileDescriptor obj) { + return obj.handle; + } } - } + ); + } - return -1; + @ikvm.lang.Internal + public static FileDescriptor fromStream(cli.System.IO.Stream stream) { + FileDescriptor desc = new FileDescriptor(); + desc.stream = stream; + return desc; + } + + @ikvm.lang.Internal + public static FileDescriptor fromSocket(cli.System.Net.Sockets.Socket socket) { + FileDescriptor desc = new FileDescriptor(); + desc.socket = socket; + return desc; } + + private volatile cli.System.IO.Stream stream; - @cli.System.Security.SecurityCriticalAttribute.Annotation - private int get_fd() - { - if (!ikvm.internal.Util.WINDOWS) - { - if (stream instanceof cli.System.IO.FileStream) - { - cli.System.IO.FileStream fs = (cli.System.IO.FileStream)stream; - return fs.get_Handle().ToInt32(); - } - else if (this == in) - { - return 0; - } - else if (this == out) - { - return 1; - } - else if (this == err) - { - return 2; - } - } + @ikvm.lang.Internal + public cli.System.IO.Stream getStream() { + return stream; + } - return -1; + private volatile cli.System.Net.Sockets.Socket socket; + + @ikvm.lang.Internal + public cli.System.Net.Sockets.Socket getSocket() { + return socket; } - @DllImportAttribute.Annotation("kernel32") - private static native cli.System.IntPtr GetStdHandle(int nStdHandle); + @ikvm.lang.Property(get = "getFd") + private int fd; - /** - * Constructs an (invalid) FileDescriptor - * object. - */ - public /**/ FileDescriptor() { + private native int getFd(); + + @ikvm.lang.Property(get = "getHandle") + private long handle; + + private native long getHandle(); + + private Closeable parent; + private List otherParents; + private boolean closed; + + private cli.System.Threading.SemaphoreSlim semaphore; + private cli.System.Threading.Tasks.Task task; + + public FileDescriptor() { } @@ -201,78 +154,9 @@ public boolean valid() { * buffers have been synchronized with physical media. * @since JDK1.1 */ - public void sync() throws SyncFailedException - { - if (stream == null) - { - throw new SyncFailedException("sync failed"); - } - - if (!stream.get_CanWrite()) - { - return; - } - - try - { - if (false) throw new cli.System.IO.IOException(); - stream.Flush(); - } - catch (cli.System.IO.IOException x) - { - throw new SyncFailedException(x.getMessage()); - } - - if (stream instanceof FileStream) - { - FileStream fs = (FileStream)stream; - boolean ok = ikvm.internal.Util.WINDOWS ? flushWin32(fs) : flushPosix(fs); - if (!ok) - { - throw new SyncFailedException("sync failed"); - } - } - } - - private static native boolean flushPosix(FileStream fs); - - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - private static boolean flushWin32(FileStream fs) - { - return FlushFileBuffers(fs.get_SafeFileHandle()) != 0; - } - - @DllImportAttribute.Annotation("kernel32") - private static native int FlushFileBuffers(cli.Microsoft.Win32.SafeHandles.SafeFileHandle handle); + public native void sync() throws SyncFailedException; - private static FileDescriptor standardStream(int fd) - { - FileDescriptor desc = new FileDescriptor(); - try - { - desc.stream = getStandardStream(fd); - } - catch (cli.System.MissingMethodException _) - { - desc.stream = cli.System.IO.Stream.Null; - } - return desc; - } - - private static cli.System.IO.Stream getStandardStream(int fd) throws cli.System.MissingMethodException - { - switch (fd) - { - case 0: - return cli.System.Console.OpenStandardInput(); - case 1: - return cli.System.Console.OpenStandardOutput(); - case 2: - return cli.System.Console.OpenStandardError(); - default: - throw new Error(); - } - } + private static native FileDescriptor standardStream(int fd); /* * Package private methods to track referents. @@ -323,7 +207,7 @@ synchronized void closeAll(Closeable releaser) throws IOException { } } } - } catch(IOException ex) { + } catch (IOException ex) { /* * If releaser close() throws IOException * add other exceptions as suppressed. @@ -338,451 +222,4 @@ synchronized void closeAll(Closeable releaser) throws IOException { } } - void openReadOnly(String name) throws FileNotFoundException - { - open(name, FileMode.Open, FileAccess.Read); - } - - void openWriteOnly(String name) throws FileNotFoundException - { - open(name, FileMode.Create, FileAccess.Write); - } - - void openReadWrite(String name) throws FileNotFoundException - { - open(name, FileMode.OpenOrCreate, FileAccess.ReadWrite); - } - - void openAppend(String name) throws FileNotFoundException - { - open(name, FileMode.Append, FileAccess.Write); - } - - private static native cli.System.IO.Stream open(String name, FileMode fileMode, FileAccess fileAccess) - throws cli.System.IO.IOException, - cli.System.Security.SecurityException, - cli.System.UnauthorizedAccessException, - cli.System.ArgumentException, - cli.System.NotSupportedException; - - private void open(String name, int fileMode, int fileAccess) throws FileNotFoundException - { - try - { - stream = open(name, FileMode.wrap(fileMode), FileAccess.wrap(fileAccess)); - } - catch (cli.System.Security.SecurityException x1) - { - throw new SecurityException(x1.getMessage()); - } - catch (cli.System.IO.IOException x2) - { - throw new FileNotFoundException(x2.getMessage()); - } - catch (cli.System.UnauthorizedAccessException x3) - { - // this is caused by "name" being a directory instead of a file - // or by name being a read-only file - throw new FileNotFoundException(name + " (Access is denied)"); - } - catch (cli.System.ArgumentException x4) - { - throw new FileNotFoundException(x4.getMessage()); - } - catch (cli.System.NotSupportedException x5) - { - throw new FileNotFoundException(x5.getMessage()); - } - } - - private void checkOpen() throws IOException - { - if (stream == null) - { - throw new IOException("Stream Closed"); - } - } - - int read() throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(null); - return stream.ReadByte(); - } - catch (cli.System.NotSupportedException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public int readBytes(byte buf[], int offset, int len) throws IOException - { - // NOTE we start by dereferencing buf, to make sure you get a NullPointerException first if you pass a null reference. - int bufLen = buf.length; - if ((offset < 0) || (offset > bufLen) || (len < 0) || (len > (bufLen - offset))) - { - throw new IndexOutOfBoundsException(); - } - - if (len == 0) - { - return 0; - } - - checkOpen(); - - try - { - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(null); - int count = stream.Read(buf, offset, len); - if (count == 0) - { - count = -1; - } - return count; - } - catch (cli.System.NotSupportedException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - long skip(long n) throws IOException - { - checkOpen(); - if (!stream.get_CanSeek()) - { - // in a somewhat bizar twist, for non-seekable streams the JDK throws an exception - throw new IOException("The handle is invalid"); - } - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - long cur = stream.get_Position(); - long end = stream.Seek(n, SeekOrigin.wrap(SeekOrigin.Current)); - return end - cur; - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - // this means we have a broken Stream, because if CanSeek returns true, it must - // support Length and Position - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public int available() throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - if (stream.get_CanSeek()) - { - return (int)Math.min(Integer.MAX_VALUE, Math.max(0, stream.get_Length() - stream.get_Position())); - } - return 0; - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - // this means we have a broken Stream, because if CanSeek returns true, it must - // support Length and Position - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - void write(int b) throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(null); - stream.WriteByte((byte)b); - // NOTE FileStream buffers the output, so we have to flush explicitly - stream.Flush(); - } - catch (cli.System.NotSupportedException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public void writeBytes(byte buf[], int offset, int len) throws IOException - { - // NOTE we start by dereferencing buf, to make sure you get a NullPointerException first if you pass a null reference. - int bufLen = buf.length; - if ((offset < 0) || (offset > bufLen) || (len < 0) || (len > (bufLen - offset))) - { - throw new IndexOutOfBoundsException(); - } - - if (len == 0) - { - return; - } - - checkOpen(); - - try - { - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(null); - stream.Write(buf, offset, len); - // NOTE FileStream buffers the output, so we have to flush explicitly - stream.Flush(); - } - catch (cli.System.NotSupportedException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public long getFilePointer() throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - return stream.get_Position(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public void seek(long newPosition) throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - if (false) throw new cli.System.ArgumentOutOfRangeException(); - stream.set_Position(newPosition); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - catch (cli.System.ArgumentOutOfRangeException _) - { - throw new IOException("Negative seek offset"); - } - } - - @ikvm.lang.Internal - public long length() throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - return stream.get_Length(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public void setLength(long newLength) throws IOException - { - checkOpen(); - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.ObjectDisposedException(null); - stream.SetLength(newLength); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.NotSupportedException x1) - { - throw new IOException(x1); - } - catch (cli.System.ObjectDisposedException x) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - @ikvm.lang.Internal - public void close() throws IOException - { - cli.System.IO.Stream s = stream; - stream = null; - if (s != null) - { - s.Close(); - } - } - - @ikvm.lang.Internal - public cli.System.IO.Stream getStream() - { - return stream; - } - - @ikvm.lang.Internal - public static FileDescriptor fromStream(cli.System.IO.Stream stream) - { - FileDescriptor desc = new FileDescriptor(); - desc.stream = stream; - return desc; - } - - @ikvm.lang.Internal - public cli.System.Net.Sockets.Socket getSocket() - { - return socket; - } - - @ikvm.lang.Internal - public void setSocket(cli.System.Net.Sockets.Socket socket) - { - this.socket = socket; - } - - @ikvm.lang.Internal - public void setSocketBlocking(boolean blocking) throws IOException - { - this.nonBlockingSocket = !blocking; - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - socket.set_Blocking(blocking); - } - catch (cli.System.Net.Sockets.SocketException e) - { - // Work around for winsock issue. You can't set a socket to blocking if a connection request is pending, - // so we'll have to set the blocking again in SocketChannelImpl.checkConnect(). - if (e.get_SocketErrorCode().Value == SocketError.InvalidArgument) - return; - - throw java.net.SocketUtil.convertSocketExceptionToIOException(e); - } - catch (cli.System.ObjectDisposedException e) - { - throw new java.net.SocketException("Socket is closed."); - } - } - - @ikvm.lang.Internal - public boolean isSocketBlocking() - { - return !nonBlockingSocket; - } - - @ikvm.lang.Internal - public cli.System.IAsyncResult getAsyncResult() - { - return asyncResult; - } - - @ikvm.lang.Internal - public void setAsyncResult(cli.System.IAsyncResult asyncResult) - { - this.asyncResult = asyncResult; - } - } diff --git a/src/IKVM.Java/local/java/lang/.gitignore b/src/IKVM.Java/local/java/lang/.gitignore deleted file mode 100644 index 4fdc733f95..0000000000 --- a/src/IKVM.Java/local/java/lang/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/PropertyConstants.java diff --git a/src/IKVM.Java/local/java/lang/Class.java b/src/IKVM.Java/local/java/lang/Class.java index b11e668d45..4ca1724c28 100644 --- a/src/IKVM.Java/local/java/lang/Class.java +++ b/src/IKVM.Java/local/java/lang/Class.java @@ -144,9 +144,6 @@ public final class Class implements java.io.Serializable, * generated. */ private Class(ClassLoader loader) { - // Initialize final field for classLoader. The initialization value of non-null - // prevents future JIT optimizations from assuming this final field is null. - //classLoader = loader; throw new InternalError(); } diff --git a/src/IKVM.Java/local/java/lang/ClassLoader.java b/src/IKVM.Java/local/java/lang/ClassLoader.java index ec29bea77a..c905117bfe 100644 --- a/src/IKVM.Java/local/java/lang/ClassLoader.java +++ b/src/IKVM.Java/local/java/lang/ClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,10 +177,10 @@ */ public abstract class ClassLoader { - // If initialization succeed this is set to true and security checks will - // succeed. Otherwise the object is not initialized and the object is - // useless. - private final boolean initialized; + private static native void registerNatives(); + static { + registerNatives(); + } // The parent class loader for delegation // Note: VM hardcoded the offset of this field, thus all new fields @@ -286,14 +286,11 @@ private static Void checkCreateClassLoader() { } return null; } - + // [IKVM] this normally private constructor is also used by ikvm.runtime.AssemblyClassLoader // to construct an assembly class loader without doing a security check @ikvm.lang.Internal protected ClassLoader(Void unused, ClassLoader parent) { - if (parent != null) { - parent.check(); - } this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { parallelLockMap = new ConcurrentHashMap<>(); @@ -308,7 +305,6 @@ protected ClassLoader(Void unused, ClassLoader parent) { domains = new HashSet<>(); assertionLock = this; } - initialized = true; } /** @@ -423,6 +419,7 @@ protected Class loadClass(String name, boolean resolve) // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { + long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); @@ -437,7 +434,13 @@ protected Class loadClass(String name, boolean resolve) if (c == null) { // If still not found, then invoke findClass in order // to find the class. + long t1 = System.nanoTime(); c = findClass(name); + + // this is the defining class loader; record the stats + sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); + sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); + sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { @@ -480,7 +483,7 @@ protected Object getClassLoadingLock(String className) { } // This method is invoked by the virtual machine to load a class. - final Class loadClassInternal(String name) + private Class loadClassInternal(String name) throws ClassNotFoundException { // For backward compatibility, explicitly lock on 'this' when @@ -495,7 +498,7 @@ final Class loadClassInternal(String name) } // Invoked by the VM after loading class with this loader. - final void checkPackageAccess(Class cls, ProtectionDomain pd) { + private void checkPackageAccess(Class cls, ProtectionDomain pd) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (ReflectUtil.isNonPublicProxyClass(cls)) { @@ -766,7 +769,6 @@ protected final Class defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { - check(); protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class c = defineClass1(name, b, off, len, protectionDomain, source); @@ -840,8 +842,6 @@ protected final Class defineClass(String name, java.nio.ByteBuffer b, ProtectionDomain protectionDomain) throws ClassFormatError { - check(); - int len = b.remaining(); // Use byte[] if not a direct ByteBufer: @@ -876,7 +876,7 @@ private native Class defineClass2(String name, java.nio.ByteBuffer b, String source); // true if the name is null or has the potential to be a valid binary name - static boolean checkName(String name) { + private boolean checkName(String name) { if ((name == null) || (name.length() == 0)) return true; if ((name.indexOf('/') != -1) @@ -972,7 +972,6 @@ private boolean compareCerts(Certificate[] pcerts, * @see #defineClass(String, byte[], int, int) */ protected final void resolveClass(Class c) { - check(); resolveClass0(c); } @@ -1003,7 +1002,6 @@ protected final void resolveClass(Class c) { protected final Class findSystemClass(String name) throws ClassNotFoundException { - check(); ClassLoader system = getSystemClassLoader(); if (system == null) { if (!checkName(name)) @@ -1023,7 +1021,6 @@ protected final Class findSystemClass(String name) */ private Class findBootstrapClassOrNull(String name) { - check(); if (!checkName(name)) return null; return findBootstrapClass(name); @@ -1032,13 +1029,6 @@ private Class findBootstrapClassOrNull(String name) // return null if not found private native Class findBootstrapClass(String name); - // Check to make sure the class loader has been initialized. - private void check() { - if (!initialized) { - throw new SecurityException("ClassLoader object not initialized"); - } - } - /** * Returns the class with the given binary name if this * loader has been recorded by the Java virtual machine as an initiating @@ -1054,7 +1044,6 @@ private void check() { * @since 1.1 */ protected final Class findLoadedClass(String name) { - check(); if (!checkName(name)) return null; return findLoadedClass0(name); @@ -1075,7 +1064,6 @@ protected final Class findLoadedClass(String name) { * @since 1.1 */ protected final void setSigners(Class c, Object[] signers) { - check(); c.setSigners(signers); } @@ -1460,7 +1448,8 @@ private static synchronized void initSystemClassLoader() { Throwable oops = null; scl = l.getClassLoader(); try { - scl = AccessController.doPrivileged(new SystemClassLoaderAction(scl)); + scl = AccessController.doPrivileged( + new SystemClassLoaderAction(scl)); } catch (PrivilegedActionException pae) { oops = pae.getCause(); if (oops instanceof InvocationTargetException) { @@ -1733,7 +1722,6 @@ static class NativeLibrary { native long find(String name); native void unload(String name, boolean isBuiltin); - static native String findBuiltinLib(String name); public NativeLibrary(Class fromClass, String name, boolean isBuiltin) { this.name = name; @@ -1786,7 +1774,8 @@ static Class getFromClass() { private static String usr_paths[]; private static String sys_paths[]; - private static String[] initializePath(String ldpath) { + private static String[] initializePath(String propname) { + String ldpath = System.getProperty(propname, ""); String ps = File.pathSeparator; int ldlen = ldpath.length(); int i, j, n; @@ -1816,15 +1805,6 @@ private static String[] initializePath(String ldpath) { paths[n] = ldpath.substring(i, ldlen); return paths; } - - private static String java_library_path; - private static String sun_boot_library_path; - - static void initializeLibraryPaths(java.util.Properties props) - { - java_library_path = props.getProperty("java.library.path", ""); - sun_boot_library_path = props.getProperty("sun.boot.library.path", ""); - } // Invoked in the java.lang.Runtime class to implement load and loadLibrary. static void loadLibrary(Class fromClass, String name, @@ -1832,8 +1812,8 @@ static void loadLibrary(Class fromClass, String name, ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader(); if (sys_paths == null) { - usr_paths = initializePath(java_library_path); - sys_paths = initializePath(sun_boot_library_path); + usr_paths = initializePath("java.library.path"); + sys_paths = initializePath("sun.boot.library.path"); } if (isAbsolute) { if (loadLibrary0(fromClass, new File(name))) { @@ -1882,9 +1862,11 @@ static void loadLibrary(Class fromClass, String name, throw new UnsatisfiedLinkError("no " + name + " in java.library.path"); } + private static native String findBuiltinLib(String name); + private static boolean loadLibrary0(Class fromClass, final File file) { // Check to see if we're attempting to access a static library - String name = NativeLibrary.findBuiltinLib(file.getName()); + String name = findBuiltinLib(file.getName()); boolean isBuiltin = (name != null); if (!isBuiltin) { boolean exists = AccessController.doPrivileged( @@ -2202,16 +2184,19 @@ private void initializeJavaAssertionMaps() { // Retrieves the assertion directives from the VM. private static native AssertionStatusDirectives retrieveDirectives(); - + // [IKVM] equivalent of HotSpot's java_lang_ClassLoader::is_trusted_loader() static boolean isTrustedLoader(ClassLoader loader) { ClassLoader cl = scl; while (cl != null) { - if (cl == loader) return true; - cl = cl.parent; + if (cl == loader) + return true; + else + cl = cl.parent; } return false; } + } diff --git a/src/IKVM.Java/local/java/lang/ClassLoaderHelper.java b/src/IKVM.Java/local/java/lang/ClassLoaderHelper.java index e4dc578f51..1c08b6e50a 100644 --- a/src/IKVM.Java/local/java/lang/ClassLoaderHelper.java +++ b/src/IKVM.Java/local/java/lang/ClassLoaderHelper.java @@ -37,9 +37,10 @@ private ClassLoaderHelper() {} * For mac, this replaces the final .dylib suffix with .jnilib */ static File mapAlternativeName(File lib) { - if (!ikvm.internal.Util.MACOSX) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsOSX() == false) { return null; } + String name = lib.toString(); int index = name.lastIndexOf('.'); if (index < 0) { diff --git a/src/IKVM.Java/local/java/lang/ProcessImpl.java b/src/IKVM.Java/local/java/lang/ProcessImpl.java index 5fe3285203..ae211b1e71 100644 --- a/src/IKVM.Java/local/java/lang/ProcessImpl.java +++ b/src/IKVM.Java/local/java/lang/ProcessImpl.java @@ -1,835 +1,46 @@ -/* - * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/*IKVM*/ -/* - * Modified for IKVM by Jeroen Frijters - */ - package java.lang; -import java.io.IOException; -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileDescriptor; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.lang.ProcessBuilder.Redirect; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import ikvm.internal.io.FileStreamExtensions; - -import cli.System.AsyncCallback; -import cli.System.IAsyncResult; -import cli.System.Diagnostics.ProcessStartInfo; -import cli.System.EventArgs; -import cli.System.EventHandler; -import cli.System.IO.FileAccess; -import cli.System.IO.FileShare; -import cli.System.IO.FileMode; -import cli.System.IO.FileOptions; -import cli.System.IO.FileStream; -import cli.System.IO.Stream; -import cli.System.Security.AccessControl.FileSystemRights; - -/* This class is for the exclusive use of ProcessBuilder.start() to - * create new processes. - * - * @author Martin Buchholz - * @since 1.5 - */ +import java.io.*; +import java.util.*; final class ProcessImpl extends Process { - static class fdAccess { - static Stream getHandle(FileDescriptor fd) { - return fd.getStream(); - } - } - - /** - * Open a file for writing. If {@code append} is {@code true} then the file - * is opened for atomic append directly and a FileOutputStream constructed - * with the resulting handle. This is because a FileOutputStream created - * to append to a file does not open the file in a manner that guarantees - * that writes by the child process will be atomic. - */ - private static FileOutputStream newFileOutputStream(File f, boolean append) - throws IOException - { - if (append) { - String path = f.getPath(); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkWrite(path); - final FileDescriptor fd = openForAtomicAppend(path); - return AccessController.doPrivileged( - new PrivilegedAction() { - public FileOutputStream run() { - return new FileOutputStream(fd); - } - } - ); - } else { - return new FileOutputStream(f); - } - } - - // System-dependent portion of ProcessBuilder.start() - static Process start(String cmdarray[], - java.util.Map environment, - String dir, - ProcessBuilder.Redirect[] redirects, - boolean redirectErrorStream) - throws IOException - { - FileInputStream f0 = null; - FileOutputStream f1 = null; - FileOutputStream f2 = null; - - try { - Stream[] stdHandles; - if (redirects == null) { - stdHandles = new Stream[3]; - } else { - stdHandles = new Stream[3]; - - if (redirects[0] == Redirect.PIPE) - stdHandles[0] = null; - else if (redirects[0] == Redirect.INHERIT) - stdHandles[0] = fdAccess.getHandle(FileDescriptor.in); - else { - f0 = new FileInputStream(redirects[0].file()); - stdHandles[0] = fdAccess.getHandle(f0.getFD()); - } - - if (redirects[1] == Redirect.PIPE) - stdHandles[1] = null; - else if (redirects[1] == Redirect.INHERIT) - stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); - else { - f1 = newFileOutputStream(redirects[1].file(), - redirects[1].append()); - stdHandles[1] = fdAccess.getHandle(f1.getFD()); - } - - if (redirects[2] == Redirect.PIPE) - stdHandles[2] = null; - else if (redirects[2] == Redirect.INHERIT) - stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); - else { - f2 = newFileOutputStream(redirects[2].file(), - redirects[2].append()); - stdHandles[2] = fdAccess.getHandle(f2.getFD()); - } - } - - return new ProcessImpl(cmdarray, environment, dir, - stdHandles, redirectErrorStream); - } catch (Throwable t) { - if (f0 != null) - f0.close(); - if (f1 != null) - f1.close(); - if (f2 != null) - f2.close(); - throw t; - } finally { - // HACK prevent the File[In|Out]putStream objects from closing the streams - // (the System.IO.FileStream will eventually be closed explicitly or by its finalizer) - if (f0 != null) - cli.System.GC.SuppressFinalize(f0); - if (f1 != null) - cli.System.GC.SuppressFinalize(f1); - if (f2 != null) - cli.System.GC.SuppressFinalize(f2); - } - - } - - private static class LazyPattern { - // Escape-support version: - // "(\")((?:\\\\\\1|.)+?)\\1|([^\\s\"]+)"; - private static final Pattern PATTERN = - Pattern.compile("[^\\s\"]+|\"[^\"]*\""); - }; - - /* Parses the command string parameter into the executable name and - * program arguments. - * - * The command string is broken into tokens. The token separator is a space - * or quota character. The space inside quotation is not a token separator. - * There are no escape sequences. - */ - private static String[] getTokensFromCommand(String command) { - ArrayList matchList = new ArrayList<>(8); - Matcher regexMatcher = LazyPattern.PATTERN.matcher(command); - while (regexMatcher.find()) - matchList.add(regexMatcher.group()); - return matchList.toArray(new String[matchList.size()]); - } - - private static final int VERIFICATION_CMD_BAT = 0; - private static final int VERIFICATION_WIN32 = 1; - private static final int VERIFICATION_LEGACY = 2; - private static final char ESCAPE_VERIFICATION[][] = { - // We guarantee the only command file execution for implicit [cmd.exe] run. - // http://technet.microsoft.com/en-us/library/bb490954.aspx - {' ', '\t', '<', '>', '&', '|', '^'}, - - {' ', '\t', '<', '>'}, - {' ', '\t'} - }; - - private static String createCommandLine(int verificationType, - final String executablePath, - final String cmd[]) - { - StringBuilder cmdbuf = new StringBuilder(80); - - cmdbuf.append(executablePath); - - for (int i = 1; i < cmd.length; ++i) { - cmdbuf.append(' '); - String s = cmd[i]; - if (needsEscaping(verificationType, s)) { - cmdbuf.append('"').append(s); - - // The code protects the [java.exe] and console command line - // parser, that interprets the [\"] combination as an escape - // sequence for the ["] char. - // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - // - // If the argument is an FS path, doubling of the tail [\] - // char is not a problem for non-console applications. - // - // The [\"] sequence is not an escape sequence for the [cmd.exe] - // command line parser. The case of the [""] tail escape - // sequence could not be realized due to the argument validation - // procedure. - if ((verificationType != VERIFICATION_CMD_BAT) && s.endsWith("\\")) { - cmdbuf.append('\\'); - } - cmdbuf.append('"'); - } else { - cmdbuf.append(s); - } - } - return cmdbuf.toString(); - } - - private static boolean isQuoted(boolean noQuotesInside, String arg, - String errorMessage) { - int lastPos = arg.length() - 1; - if (lastPos >=1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') { - // The argument has already been quoted. - if (noQuotesInside) { - if (arg.indexOf('"', 1) != lastPos) { - // There is ["] inside. - throw new IllegalArgumentException(errorMessage); - } - } - return true; - } - if (noQuotesInside) { - if (arg.indexOf('"') >= 0) { - // There is ["] inside. - throw new IllegalArgumentException(errorMessage); - } - } - return false; - } - - private static boolean needsEscaping(int verificationType, String arg) { - // Switch off MS heuristic for internal ["]. - // Please, use the explicit [cmd.exe] call - // if you need the internal ["]. - // Example: "cmd.exe", "/C", "Extended_MS_Syntax" - - // For [.exe] or [.com] file the unpaired/internal ["] - // in the argument is not a problem. - boolean argIsQuoted = isQuoted( - (verificationType == VERIFICATION_CMD_BAT), - arg, "Argument has embedded quote, use the explicit CMD.EXE call."); - - if (!argIsQuoted) { - char testEscape[] = ESCAPE_VERIFICATION[verificationType]; - for (int i = 0; i < testEscape.length; ++i) { - if (arg.indexOf(testEscape[i]) >= 0) { - return true; - } - } - } - return false; - } - - private static String getExecutablePath(String path) - throws IOException - { - boolean pathIsQuoted = isQuoted(true, path, - "Executable name has embedded quote, split the arguments"); - - // Win32 CreateProcess requires path to be normalized - File fileToRun = new File(pathIsQuoted - ? path.substring(1, path.length() - 1) - : path); - - // From the [CreateProcess] function documentation: - // - // "If the file name does not contain an extension, .exe is appended. - // Therefore, if the file name extension is .com, this parameter - // must include the .com extension. If the file name ends in - // a period (.) with no extension, or if the file name contains a path, - // .exe is not appended." - // - // "If the file name !does not contain a directory path!, - // the system searches for the executable file in the following - // sequence:..." - // - // In practice ANY non-existent path is extended by [.exe] extension - // in the [CreateProcess] funcion with the only exception: - // the path ends by (.) - - return fileToRun.getPath(); - } - - - private boolean isShellFile(String executablePath) { - String upPath = executablePath.toUpperCase(); - return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); - } - - private String quoteString(String arg) { - StringBuilder argbuf = new StringBuilder(arg.length() + 2); - return argbuf.append('"').append(arg).append('"').toString(); - } - - - private cli.System.Diagnostics.Process handle; - private OutputStream stdin_stream; - private InputStream stdout_stream; - private InputStream stderr_stream; - - private ProcessImpl(String cmd[], - final java.util.Map envblock, - final String path, - final Stream[] stdHandles, - final boolean redirectErrorStream) - throws IOException - { - String cmdstr; - SecurityManager security = System.getSecurityManager(); - boolean allowAmbiguousCommands = false; - if (security == null) { - allowAmbiguousCommands = true; - String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands"); - if (value != null) - allowAmbiguousCommands = !"false".equalsIgnoreCase(value); - } - if (allowAmbiguousCommands) { - // Legacy mode. - - // Normalize path if possible. - String executablePath = new File(cmd[0]).getPath(); - - // No worry about internal, unpaired ["], and redirection/piping. - if (needsEscaping(VERIFICATION_LEGACY, executablePath) ) - executablePath = quoteString(executablePath); - - cmdstr = createCommandLine( - //legacy mode doesn't worry about extended verification - VERIFICATION_LEGACY, - executablePath, - cmd); - } else { - String executablePath; - try { - executablePath = getExecutablePath(cmd[0]); - } catch (IllegalArgumentException e) { - // Workaround for the calls like - // Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar") - - // No chance to avoid CMD/BAT injection, except to do the work - // right from the beginning. Otherwise we have too many corner - // cases from - // Runtime.getRuntime().exec(String[] cmd [, ...]) - // calls with internal ["] and escape sequences. - - // Restore original command line. - StringBuilder join = new StringBuilder(); - // terminal space in command line is ok - for (String s : cmd) - join.append(s).append(' '); - - // Parse the command line again. - cmd = getTokensFromCommand(join.toString()); - executablePath = getExecutablePath(cmd[0]); - - // Check new executable name once more - if (security != null) - security.checkExec(executablePath); - } - - // Quotation protects from interpretation of the [path] argument as - // start of longer path with spaces. Quotation has no influence to - // [.exe] extension heuristic. - cmdstr = createCommandLine( - // We need the extended verification procedure for CMD files. - isShellFile(executablePath) - ? VERIFICATION_CMD_BAT - : VERIFICATION_WIN32, - quoteString(executablePath), - cmd); - } - - handle = create(cmdstr, envblock, path, - stdHandles, redirectErrorStream); - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Void run() { - if (stdHandles[0] == null) - stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE; - else { - FileDescriptor stdin_fd = FileDescriptor.fromStream(stdHandles[0]); - stdin_stream = new BufferedOutputStream( - new FileOutputStream(stdin_fd)); - } + cli.System.Diagnostics.Process process; + OutputStream outputStream; + InputStream inputStream; + InputStream errorStream; - if (stdHandles[1] == null) - stdout_stream = ProcessBuilder.NullInputStream.INSTANCE; - else { - FileDescriptor stdout_fd = FileDescriptor.fromStream(stdHandles[1]); - stdout_stream = new BufferedInputStream( - new FileInputStream(stdout_fd)); - } + static native Process start(String cmdarray[], java.util.Map environment, String dir, ProcessBuilder.Redirect[] redirects, boolean redirectErrorStream) throws IOException; - if (stdHandles[2] == null) - stderr_stream = ProcessBuilder.NullInputStream.INSTANCE; - else { - FileDescriptor stderr_fd = FileDescriptor.fromStream(stdHandles[2]); - stderr_stream = new FileInputStream(stderr_fd); - } - - return null; }}); + private ProcessImpl(cli.System.Diagnostics.Process process, OutputStream outputStream, InputStream inputStream, InputStream errorStream) { + this.process = process; + this.outputStream = outputStream; + this.inputStream = inputStream; + this.errorStream = errorStream; } + @Override public OutputStream getOutputStream() { - return stdin_stream; + return outputStream; } + @Override public InputStream getInputStream() { - return stdout_stream; + return inputStream; } - + + @Override public InputStream getErrorStream() { - return stderr_stream; - } - - public int exitValue() { - if (!handle.get_HasExited()) - throw new IllegalThreadStateException("process has not exited"); - return handle.get_ExitCode(); + return errorStream; } - - public int waitFor() throws InterruptedException { - waitForInterruptibly(handle); - if (Thread.interrupted()) - throw new InterruptedException(); - return exitValue(); - } - - private static void waitForInterruptibly(cli.System.Diagnostics.Process handle) throws InterruptedException { - // to be interruptable we have to use polling - // (on .NET 2.0 WaitForExit is actually interruptible, but this isn't documented) - Thread current = Thread.currentThread(); - while (!current.isInterrupted() && !handle.WaitForExit(100)) - ; - } - + @Override - public boolean waitFor(long timeout, TimeUnit unit) - throws InterruptedException - { - if (handle.get_HasExited()) return true; - if (timeout <= 0) return false; - - long msTimeout = unit.toMillis(timeout); - - waitForTimeoutInterruptibly(handle, msTimeout); - if (Thread.interrupted()) - throw new InterruptedException(); - return handle.get_HasExited(); - } - - private static void waitForTimeoutInterruptibly( - cli.System.Diagnostics.Process handle, long timeout) { - long now = System.currentTimeMillis(); - long exp = now + timeout; - if (exp < now) { - // if we overflowed, just wait for a really long time - exp = Long.MAX_VALUE; - } - Thread current = Thread.currentThread(); - for (;;) { - if (current.isInterrupted()) { - return; - } - // wait for a maximum of 100 ms to be interruptible - if (handle.WaitForExit((int)Math.min(100, exp - now))) { - return; - } - now = System.currentTimeMillis(); - if (now >= exp) { - return; - } - } - } - - public void destroy() { terminateProcess(handle); } - + public native int waitFor() throws InterruptedException; + @Override - public Process destroyForcibly() { - destroy(); - return this; - } - - private static void terminateProcess(cli.System.Diagnostics.Process handle) { - try { - if (false) throw new cli.System.ComponentModel.Win32Exception(); - if (false) throw new cli.System.InvalidOperationException(); - handle.Kill(); - } catch (cli.System.ComponentModel.Win32Exception _) { - } catch (cli.System.InvalidOperationException _) { - } - } - + public native int exitValue(); + @Override - public boolean isAlive() { - return isProcessAlive(handle); - } - - private static boolean isProcessAlive(cli.System.Diagnostics.Process handle) { - return !handle.get_HasExited(); - } - - /** - * Create a process using the win32 function CreateProcess. - * The method is synchronized due to MS kb315939 problem. - * All native handles should restore the inherit flag at the end of call. - * - * @param cmdstr the Windows command line - * @param envblock NUL-separated, double-NUL-terminated list of - * environment strings in VAR=VALUE form - * @param dir the working directory of the process, or null if - * inheriting the current directory from the parent process - * @param stdHandles array of windows HANDLEs. Indexes 0, 1, and - * 2 correspond to standard input, standard output and - * standard error, respectively. On input, a value of -1 - * means to create a pipe to connect child and parent - * processes. On output, a value which is not -1 is the - * parent pipe handle corresponding to the pipe which has - * been created. An element of this array is -1 on input - * if and only if it is not -1 on output. - * @param redirectErrorStream redirectErrorStream attribute - * @return the native subprocess HANDLE returned by CreateProcess - */ - private static cli.System.Diagnostics.Process create(String cmdstr, - java.util.Map envblock, - String dir, - Stream[] stdHandles, - boolean redirectErrorStream) - throws IOException { + public native void destroy(); - int programEnd = parseCommandString(cmdstr); - int argumentsStart = programEnd; - if (cmdstr.length() > argumentsStart && cmdstr.charAt(argumentsStart) == ' ') { - argumentsStart++; - } - - String fileName = cmdstr.substring(0, programEnd); - ProcessStartInfo si = new ProcessStartInfo(fileName, cmdstr.substring(argumentsStart)); - si.set_UseShellExecute(false); - si.set_RedirectStandardError(true); - si.set_RedirectStandardOutput(true); - si.set_RedirectStandardInput(true); - si.set_CreateNoWindow(true); - if (dir != null) { - si.set_WorkingDirectory(dir); - } - if (envblock != null) { - si.get_EnvironmentVariables().Clear(); - for (String key : envblock.keySet()) { - si.get_EnvironmentVariables().set_Item(key, envblock.get(key)); - } - } - - cli.System.Diagnostics.Process proc; - try { - if (false) throw new cli.System.ComponentModel.Win32Exception(); - if (false) throw new cli.System.InvalidOperationException(); - proc = cli.System.Diagnostics.Process.Start(si); - } catch (cli.System.ComponentModel.Win32Exception x1) { - throw new IOException(x1.getMessage()); - } catch (cli.System.InvalidOperationException x2) { - throw new IOException(x2.getMessage()); - } - - // if any of the handles is redirected to/from a file, - // we need to close the files as soon as the process exits - if (stdHandles[0] instanceof FileStream - || stdHandles[1] instanceof FileStream - || stdHandles[2] instanceof FileStream) { - final Stream s0 = stdHandles[0]; - final Stream s1 = stdHandles[1]; - final Stream s2 = stdHandles[2]; - proc.set_EnableRaisingEvents(true); - proc.add_Exited(new EventHandler(new EventHandler.Method() { - public void Invoke(Object sender, EventArgs e) { - if (s0 instanceof FileStream) - s0.Close(); - if (s1 instanceof FileStream) - s1.Close(); - if (s2 instanceof FileStream) - s2.Close(); - } - })); - } - - Stream stdin = proc.get_StandardInput().get_BaseStream(); - Stream stdout = proc.get_StandardOutput().get_BaseStream(); - Stream stderr = proc.get_StandardError().get_BaseStream(); - - if (stdHandles[0] != null) { - connectPipe(stdHandles[0], stdin); - stdHandles[0] = null; - } else { - stdHandles[0] = stdin; - } - - Stream stdoutDrain = null; - if (stdHandles[1] != null) { - stdoutDrain = stdHandles[1]; - connectPipe(stdout, stdoutDrain); - stdHandles[1] = null; - } else if (redirectErrorStream) { - PipeStream pipe = new PipeStream(); - connectPipe(stdout, pipe); - connectPipe(stderr, pipe); - stdHandles[1] = pipe; - } else { - stdHandles[1] = stdout; - } - - if (redirectErrorStream) { - if (stdoutDrain != null) { - connectPipe(stderr, stdoutDrain); - } - stdHandles[2] = null; - } else if (stdHandles[2] != null) { - connectPipe(stderr, stdHandles[2]); - stdHandles[2] = null; - } else { - stdHandles[2] = stderr; - } - - return proc; - } - - private static final class PipeStream extends Stream - { - private final byte[] buf = new byte[4096]; - private int pos; - private int users = 2; - - @Override - public synchronized int Read(byte[] buffer, int offset, int count) - { - if (count == 0) - { - return 0; - } - while (pos == 0) - { - try - { - wait(); - } - catch (InterruptedException _) { } - } - if (pos == -1) - { - return 0; - } - count = Math.min(count, pos); - System.arraycopy(buf, 0, buffer, offset, count); - pos -= count; - System.arraycopy(buf, count, buf, 0, pos); - notifyAll(); - return count; - } - - @Override - public synchronized void Write(byte[] buffer, int offset, int count) - { - while (buf.length - pos < count) - { - try - { - wait(); - } - catch (InterruptedException _) { } - } - System.arraycopy(buffer, offset, buf, pos, count); - pos += count; - notifyAll(); - } - - @Override - public synchronized void Close() - { - if (--users == 0) - { - pos = -1; - notifyAll(); - } - } - - @Override - public boolean get_CanRead() - { - return true; - } - - @Override - public boolean get_CanSeek() - { - return false; - } - - @Override - public boolean get_CanWrite() - { - return true; - } - - @Override - public void Flush() - { - } - - @Override - public long get_Length() - { - ikvm.runtime.Util.throwException(new cli.System.NotSupportedException()); - return 0; - } - - @Override - public long get_Position() - { - ikvm.runtime.Util.throwException(new cli.System.NotSupportedException()); - return 0; - } - - @Override - public long Seek(long offset, cli.System.IO.SeekOrigin origin) - { - ikvm.runtime.Util.throwException(new cli.System.NotSupportedException()); - return 0; - } - - @Override - public void SetLength(long value) - { - ikvm.runtime.Util.throwException(new cli.System.NotSupportedException()); - } - - @Override - public void set_Position(long position) - { - ikvm.runtime.Util.throwException(new cli.System.NotSupportedException()); - } - } - - private static native int parseCommandString(String cmdstr); - - /** - * Opens a file for atomic append. The file is created if it doesn't - * already exist. - * - * @param file the file to open or create - * @return the native HANDLE - */ - private static FileDescriptor openForAtomicAppend(String path) - throws IOException { - try { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - return FileDescriptor.fromStream(FileStreamExtensions.create(path, FileMode.wrap(FileMode.Append), FileSystemRights.wrap(FileSystemRights.AppendData), FileShare.wrap(FileShare.ReadWrite), 1, FileOptions.wrap(FileOptions.None))); - } catch (cli.System.ArgumentException x) { - throw new IOException(x.getMessage()); - } catch (cli.System.IO.IOException x) { - throw new IOException(x.getMessage()); - } catch (cli.System.Security.SecurityException x) { - throw new IOException(x.getMessage()); - } catch (cli.System.UnauthorizedAccessException x) { - throw new IOException(x.getMessage()); - } - } - - private static void connectPipe(final Stream in, final Stream out) { - final byte[] buf = new byte[4096]; - final AsyncCallback[] callback = new AsyncCallback[1]; - callback[0] = new AsyncCallback(new AsyncCallback.Method() { - public void Invoke(IAsyncResult ar) { - try { - int count = in.EndRead(ar); - if (count > 0) { - out.Write(buf, 0, count); - out.Flush(); - in.BeginRead(buf, 0, buf.length, callback[0], null); - } else { - out.Close(); - } - } catch (Throwable _) { - } - } - }); - try { - in.BeginRead(buf, 0, buf.length, callback[0], null); - } catch (Throwable _) { - } - } } diff --git a/src/IKVM.Java/local/java/lang/PropertyConstants.java.tt b/src/IKVM.Java/local/java/lang/PropertyConstants.java.tt deleted file mode 100644 index 398bad86f9..0000000000 --- a/src/IKVM.Java/local/java/lang/PropertyConstants.java.tt +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright (C) 2007, 2009 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -package java.lang; - -interface PropertyConstants { - - String java_runtime_name = "OpenJDK Runtime Environment"; - String java_runtime_version = "#OpenJdkFullVersion#"; - - String java_vm_name = "#Name#"; - String java_vm_version = "#Version#"; - String java_vm_vendor = "#Name#"; - - String java_vm_specification_version = "#OpenJdkSpecificationVersion#"; - String java_vm_specification_vendor = "#OpenJdkSpecificationVendor#"; - - String java_version = "#OpenJdkImplementationVersion#"; - String java_vendor = "#Name#"; - String java_vendor_url = "#VendorUrl#"; - String java_vendor_url_bug = "#VendorUrlBug#"; - - String java_specification_version = "#OpenJdkSpecificationVersion#"; - String java_specification_vendor = "#OpenJdkSpecificationVendor#"; - - String openjdk_version = "#OpenJdkVersion#"; - String openjdk_vendor = "#OpenJdkVendor#"; - -} diff --git a/src/IKVM.Java/local/java/lang/Shutdown.java b/src/IKVM.Java/local/java/lang/Shutdown.java index 8f1a5959fd..b6d525fe65 100644 --- a/src/IKVM.Java/local/java/lang/Shutdown.java +++ b/src/IKVM.Java/local/java/lang/Shutdown.java @@ -82,7 +82,7 @@ private static void registerProcessExit() { // MONOBUG Mono doesn't support starting a new thread during ProcessExit // (and application shutdown hooks are based on threads) // see https://bugzilla.xamarin.com/show_bug.cgi?id=5650 - if (!ikvm.internal.Util.MONO) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsMono() == false) { // AppDomain.ProcessExit has a LinkDemand, so we have to have a separate method registerShutdownHook(); if (false) throw new cli.System.Security.SecurityException(); diff --git a/src/IKVM.Java/local/java/lang/System.java b/src/IKVM.Java/local/java/lang/System.java index 796d99dcb2..e39390c5e7 100644 --- a/src/IKVM.Java/local/java/lang/System.java +++ b/src/IKVM.Java/local/java/lang/System.java @@ -43,39 +43,6 @@ import sun.security.util.SecurityConstants; import sun.reflect.annotation.AnnotationType; -final class StdIO -{ - private StdIO() { } - static InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); - static PrintStream out = System.newPrintStream(new FileOutputStream(FileDescriptor.out), Props.props.getProperty("sun.stdout.encoding")); - static PrintStream err = System.newPrintStream(new FileOutputStream(FileDescriptor.err), Props.props.getProperty("sun.stderr.encoding")); -} - -final class Props -{ - private Props() { } - - static Properties props; - static String lineSeparator; - - static - { - props = new Properties(); - VMSystemProperties.initProperties(props); - lineSeparator = props.getProperty("line.separator"); - - // after we've initialized the system properties, we need to fixate certain - // results that depend on system properties, because we don't want Java code to - // be able to change the behavior by setting these system properties. - ClassLoader.initializeLibraryPaths(props); - sun.misc.VM.saveAndRemoveProperties(props); - - // now that we've initialized the system properties (which are our only - // notion of "booting" the VM) we set the booted flag. - sun.misc.VM.booted(); - } -} - /** * The System class contains several useful class fields * and methods. It cannot be instantiated. @@ -100,7 +67,7 @@ public final class System { */ private static native void registerNatives(); static { - //registerNatives(); + registerNatives(); } /** Don't let anyone instantiate this class */ @@ -114,11 +81,12 @@ private System() { * the host environment or user. */ @ikvm.lang.Property(get="get_in") - public final static InputStream in = null; - + public static InputStream in; + private static InputStream _in; + private static InputStream get_in() { - return StdIO.in; + return _in; } /** @@ -147,11 +115,12 @@ private static InputStream get_in() * @see java.io.PrintStream#println(java.lang.String) */ @ikvm.lang.Property(get="get_out") - public final static PrintStream out = null; - + public static PrintStream out; + private static PrintStream _out; + private static PrintStream get_out() { - return StdIO.out; + return _out; } /** @@ -167,11 +136,12 @@ private static PrintStream get_out() * destination that is typically not continuously monitored. */ @ikvm.lang.Property(get="get_err") - public final static PrintStream err = null; - + public static PrintStream err; + private static PrintStream _err; + private static PrintStream get_err() { - return StdIO.err; + return _err; } /* The security manager for the system. @@ -344,9 +314,6 @@ void setSecurityManager(final SecurityManager s) { private static synchronized void setSecurityManager0(final SecurityManager s) { - // [IKVM] force sun.misc.Launcher to initialize, because it assumes that it runs without a SecurityManager - sun.misc.Launcher.getLauncher(); - SecurityManager sm = getSecurityManager(); if (sm != null) { // ask the currently installed security manager if we @@ -404,10 +371,7 @@ public static SecurityManager getSecurityManager() { * the current time and midnight, January 1, 1970 UTC. * @see java.util.Date */ - public static long currentTimeMillis() { - long january_1st_1970 = 62135596800000L; - return cli.System.DateTime.get_UtcNow().get_Ticks() / 10000L - january_1st_1970; - } + public static native long currentTimeMillis(); /** * Returns the current value of the running Java Virtual Machine's @@ -453,12 +417,7 @@ public static long currentTimeMillis() { * high-resolution time source, in nanoseconds * @since 1.5 */ - public static long nanoTime() { - long NANOS_PER_SEC = 1000000000; - double current = cli.System.Diagnostics.Stopwatch.GetTimestamp(); - double freq = cli.System.Diagnostics.Stopwatch.Frequency; - return (long)((current / freq) * NANOS_PER_SEC); - } + public static native long nanoTime(); /** * Copies an array from the specified source array, beginning at the @@ -567,9 +526,7 @@ public static native void arraycopy(Object src, int srcPos, * @return the hashCode * @since JDK1.1 */ - public static int identityHashCode(Object x) { - return cli.System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(x); - } + public static native int identityHashCode(Object x); /** * System properties. The following properties are guaranteed to be defined: @@ -592,13 +549,9 @@ public static int identityHashCode(Object x) { * */ - @ikvm.lang.Property(get="get_props", set="set_props") private static Properties props; private static native Properties initProperties(Properties props); - private static Properties get_props() { return Props.props; } - private static void set_props(Properties value) { Props.props = value; } - /** * Determines the current system properties. *

@@ -716,12 +669,8 @@ public static String lineSeparator() { return lineSeparator; } - @ikvm.lang.Property(get="get_lineSeparator", set="set_lineSeparator") private static String lineSeparator; - private static String get_lineSeparator() { return Props.lineSeparator; } - private static void set_lineSeparator(String value) { Props.lineSeparator = value; } - /** * Sets the system properties to the Properties * argument. @@ -777,7 +726,7 @@ public static void setProperties(Properties props) { * @exception NullPointerException if key is * null. * @exception IllegalArgumentException if key is empty. - * @see #setProperty + * @see #setPropertycheckKey * @see java.lang.SecurityException * @see java.lang.SecurityManager#checkPropertyAccess(java.lang.String) * @see java.lang.System#getProperties() @@ -1211,7 +1160,7 @@ public static void loadLibrary(String libname) { /** * Create PrintStream for stdout/err based on encoding. */ - /*private*/ static PrintStream newPrintStream(FileOutputStream fos, String enc) { + private static PrintStream newPrintStream(FileOutputStream fos, String enc) { if (enc != null) { try { return new PrintStream(new BufferedOutputStream(fos, 128), true, enc); @@ -1219,4 +1168,128 @@ public static void loadLibrary(String libname) { } return new PrintStream(new BufferedOutputStream(fos, 128), true); } + + + /** + * Initialize the system class. Called after thread initialization. + */ + private static void initializeSystemClass() { + + // VM might invoke JNU_NewStringPlatform() to set those encoding + // sensitive properties (user.home, user.name, boot.class.path, etc.) + // during "props" initialization, in which it may need access, via + // System.getProperty(), to the related system encoding property that + // have been initialized (put into "props") at early stage of the + // initialization. So make sure the "props" is available at the + // very beginning of the initialization and all system properties to + // be put into it directly. + props = new Properties(); + initProperties(props); // initialized by the VM + + // There are certain system configurations that may be controlled by + // VM options such as the maximum amount of direct memory and + // Integer cache size used to support the object identity semantics + // of autoboxing. Typically, the library will obtain these values + // from the properties set by the VM. If the properties are for + // internal implementation use only, these properties should be + // removed from the system properties. + // + // See java.lang.Integer.IntegerCache and the + // sun.misc.VM.saveAndRemoveProperties method for example. + // + // Save a private copy of the system properties object that + // can only be accessed by the internal implementation. Remove + // certain system properties that are not intended for public access. + sun.misc.VM.saveAndRemoveProperties(props); + + + lineSeparator = props.getProperty("line.separator"); + sun.misc.Version.init(); + + FileInputStream fdIn = new FileInputStream(FileDescriptor.in); + FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out); + FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err); + setIn0(new BufferedInputStream(fdIn)); + setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding"))); + setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding"))); + + // Load the zip library now in order to keep java.util.zip.ZipFile + // from trying to use itself to load this library later. + // loadLibrary("zip"); + + // Setup Java signal handlers for HUP, TERM, and INT (where available). + Terminator.setup(); + + // Initialize any miscellenous operating system settings that need to be + // set for the class libraries. Currently this is no-op everywhere except + // for Windows where the process-wide error mode is set before the java.io + // classes are used. + sun.misc.VM.initializeOSEnvironment(); + + // The main thread is not added to its thread group in the same + // way as other threads; we must do it ourselves here. + Thread current = Thread.currentThread(); + current.getThreadGroup().add(current); + + // register shared secrets + setJavaLangAccess(); + + // Subsystems that are invoked during initialization can invoke + // sun.misc.VM.isBooted() in order to avoid doing things that should + // wait until the application class loader has been set up. + // IMPORTANT: Ensure that this remains the last initialization action! + sun.misc.VM.booted(); + } + + private static void setJavaLangAccess() { + // Allow privileged classes outside of java.lang + sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){ + public sun.reflect.ConstantPool getConstantPool(Class klass) { + return klass.getConstantPool(); + } + public boolean casAnnotationType(Class klass, AnnotationType oldType, AnnotationType newType) { + return klass.casAnnotationType(oldType, newType); + } + public AnnotationType getAnnotationType(Class klass) { + return klass.getAnnotationType(); + } + public Map, Annotation> getDeclaredAnnotationMap(Class klass) { + return klass.getDeclaredAnnotationMap(); + } + public byte[] getRawClassAnnotations(Class klass) { + throw new Error("IKVM: disabled"); + } + public byte[] getRawClassTypeAnnotations(Class klass) { + return klass.getRawTypeAnnotations(); + } + public byte[] getRawExecutableTypeAnnotations(Executable executable) { + return Class.getExecutableTypeAnnotationBytes(executable); + } + public > + E[] getEnumConstantsShared(Class klass) { + return klass.getEnumConstantsShared(); + } + public void blockedOn(Thread t, Interruptible b) { + t.blockedOn(b); + } + public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) { + Shutdown.add(slot, registerShutdownInProgress, hook); + } + public int getStackTraceDepth(Throwable t) { + return t.getStackTraceDepth(); + } + public StackTraceElement getStackTraceElement(Throwable t, int i) { + return t.getStackTraceElement(i); + } + public String newStringUnsafe(char[] chars) { + return new String(chars, true); + } + public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) { + return new Thread(target, acc); + } + public void invokeFinalize(Object o) throws Throwable { + o.finalize(); + } + }); + } } diff --git a/src/IKVM.Java/local/java/lang/Thread.java b/src/IKVM.Java/local/java/lang/Thread.java index 979ca1ea7e..dabf278766 100644 --- a/src/IKVM.Java/local/java/lang/Thread.java +++ b/src/IKVM.Java/local/java/lang/Thread.java @@ -921,7 +921,15 @@ public void Invoke() { nativeThread.set_Name(getName()); nativeThread.set_IsBackground(daemon); nativeThread.set_Priority(cli.System.Threading.ThreadPriority.wrap(mapJavaPriorityToClr(priority))); - String apartment = Props.props.getProperty("ikvm.apartmentstate", "").toLowerCase(); + + String apartment = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("ikvm.apartmentstate", "").toLowerCase(); + } + } + ); + if ("mta".equals(apartment)) { nativeThread.SetApartmentState(cli.System.Threading.ApartmentState.wrap(cli.System.Threading.ApartmentState.MTA)); } diff --git a/src/IKVM.Java/local/java/lang/VMSystemProperties.java b/src/IKVM.Java/local/java/lang/VMSystemProperties.java deleted file mode 100644 index f5bd632774..0000000000 --- a/src/IKVM.Java/local/java/lang/VMSystemProperties.java +++ /dev/null @@ -1,511 +0,0 @@ -package java.lang; - -import java.lang.PropertyConstants; -import java.io.StringReader; -import java.util.Properties; - -import ikvm.io.InputStreamWrapper; - -import cli.System.Environment; -import cli.System.Diagnostics.FileVersionInfo; -import cli.System.IO.Path; -import cli.System.Runtime.InteropServices.RuntimeInformation; -import cli.System.Runtime.InteropServices.OSPlatform; - -import static ikvm.internal.Util.SafeGetEnvironmentVariable; - -final class VMSystemProperties -{ - - private static final byte VER_NT_DOMAIN_CONTROLLER = 0x0000002; - private static final byte VER_NT_SERVER = 0x0000003; - private static final byte VER_NT_WORKSTATION = 0x0000001; - - private static final String SPEC_TITLE = "Java Platform API Specification"; - private static final String SPEC_VERSION = PropertyConstants.java_specification_version; - private static final String SPEC_VENDOR = PropertyConstants.java_specification_vendor; - - private static String getLibraryPath() - { - String libraryPath = null; - if (RuntimeInformation.IsOSPlatform(OSPlatform.get_Windows())) { - // see /hotspot/src/os/windows/vm/os_windows.cpp for the comment that describes how we build the path - String windir = SafeGetEnvironmentVariable("SystemRoot"); - if (windir != null) { - libraryPath = Path.PathSeparator + windir + "\\Sun\\Java\\bin"; - } - - try { - if (false) throw new cli.System.Security.SecurityException(); - if (libraryPath == null) { - libraryPath = Environment.get_SystemDirectory(); - } else { - libraryPath += Path.PathSeparator + Environment.get_SystemDirectory(); - } - } catch (cli.System.Security.SecurityException _) { - - } - - if (windir != null) { - libraryPath += Path.PathSeparator + windir; - } - - String path = SafeGetEnvironmentVariable("PATH"); - if (path != null) { - libraryPath += Path.PathSeparator + path; - } - } else if (RuntimeInformation.IsOSPlatform(OSPlatform.get_Linux())) { - // on Linux we have some hardcoded paths (from /hotspot/src/os/linux/vm/os_linux.cpp) - // and we can only guess the cpu arch based on bitness (that means only x86 and x64) - String cpu_arch = cli.System.IntPtr.get_Size() == 4 ? "i386" : "amd64"; - libraryPath = "/usr/java/packages/lib/" + cpu_arch + ":/lib:/usr/lib"; - String ld_library_path = SafeGetEnvironmentVariable("LD_LIBRARY_PATH"); - if (ld_library_path != null) { - libraryPath = ld_library_path + ":" + libraryPath; - } - } else if (RuntimeInformation.IsOSPlatform(OSPlatform.get_OSX())) { - libraryPath = "."; - } - - try { - libraryPath = Path.GetDirectoryName(getRuntimeAssembly().get_Location()) + Path.PathSeparator + libraryPath; - } catch (Throwable _) { - // ignore - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.get_Windows())) { - libraryPath += Path.PathSeparator + "."; - } - - return libraryPath; - } - - public static void initProperties(Properties p) { - initCommonProperties(p); - initI18NProperties(p); - - // the runtime assembly will set the root of various relative paths - String runtimePath = Path.GetDirectoryName(getRuntimeAssembly().get_Location()); - - try { - String ikvmPropertiesPath = Path.Combine(runtimePath, "ikvm.properties"); - if (cli.System.IO.File.Exists(ikvmPropertiesPath)) { - try (StringReader ikvmPropertiesReader = new StringReader(cli.System.IO.File.ReadAllText(ikvmPropertiesPath))) { - p.load(ikvmPropertiesReader); - } - } - } catch (Exception _) { - - } - - // lookup existing ikvm.home property - String ikvmHome = p.getProperty("ikvm.home"); - - // calculate ikvm.home from ikvm.home.root - if (ikvmHome == null) { - String ikvmHomeRoot = p.getProperty("ikvm.home.root"); - if (ikvmHomeRoot != null) { - String ikvmHomeRootPath = Path.Combine(runtimePath, ikvmHomeRoot); - if (cli.System.IO.Directory.Exists(ikvmHomeRootPath)) { - String[] ikvmHomeArchs = getIkvmHomeArchs(); - if (ikvmHomeArchs != null) { - for (String ikvmHomeArch : ikvmHomeArchs) { - String ikvmHomePath = Path.Combine(ikvmHomeRootPath, ikvmHomeArch); - if (cli.System.IO.Directory.Exists(ikvmHomePath)) { - ikvmHome = ikvmHomePath; - break; - } - } - } - } - } - } - - // default value for ikvm.home - if (ikvmHome == null) { - ikvmHome = "ikvm"; - } - - try { - // ensure ikvm.home is absolute - if (Path.IsPathRooted(ikvmHome) == false) { - ikvmHome = Path.GetFullPath(Path.Combine(runtimePath, ikvmHome)); - } - - // adjust ikvm.home and java.home to match - p.setProperty("ikvm.home", ikvmHome); - p.setProperty("java.home", ikvmHome); - } catch (Exception _) { - - } - - p.setProperty("openjdk.version", PropertyConstants.openjdk_version); - p.setProperty("java.endorsed.dirs", Path.Combine(ikvmHome, "lib", "endorsed")); - p.setProperty("sun.boot.library.path", Path.Combine(ikvmHome, "bin")); - p.setProperty("sun.boot.class.path", getBootClassPath()); - p.setProperty("file.encoding.pkg", "sun.io"); - p.setProperty("java.vm.info", "compiled mode"); - p.setProperty("java.awt.headless", "true"); - p.setProperty("user.timezone", ""); - p.setProperty("sun.cpu.endian", cli.System.BitConverter.IsLittleEndian ? "little" : "big"); - p.setProperty("sun.nio.MaxDirectMemorySize", "-1"); - p.setProperty("sun.os.patch.level", cli.System.Environment.get_OSVersion().get_ServicePack()); - - String stdoutEncoding = getStdoutEncoding(); - if (stdoutEncoding != null) { - p.setProperty("sun.stdout.encoding", stdoutEncoding); - } - - String stderrEncoding = getStderrEncoding(); - if (stderrEncoding != null) { - p.setProperty("sun.stderr.encoding", stderrEncoding); - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.get_OSX())) { - p.setProperty("sun.jnu.encoding", "UTF-8"); - } else { - p.setProperty("sun.jnu.encoding", cli.System.Text.Encoding.get_Default().get_WebName()); - } - - // serve cacerts out of the VFS by default - String vfsroot = getVirtualFileSystemRoot(); - p.setProperty("javax.net.ssl.trustStore", Path.Combine(vfsroot, "cacerts")); - - // TODO - // sun.cpu.isalist:=pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86 - // sun.desktop:=windows - // sun.io.unicode.encoding:=UnicodeLittle - // sun.management.compiler:=HotSpot Client Compiler - - // read properties from app.config - try { - if (false) throw new cli.System.Configuration.ConfigurationException(); - cli.System.Collections.Specialized.NameValueCollection appSettings = cli.System.Configuration.ConfigurationSettings.get_AppSettings(); - cli.System.Collections.IEnumerator keys = appSettings.GetEnumerator(); - while (keys.MoveNext()) { - String key = (String)keys.get_Current(); - if (key.startsWith("ikvm:")) { - p.setProperty(key.substring(5), appSettings.get_Item(key)); - } - } - } catch (cli.System.Configuration.ConfigurationException _) { - // app.config is invalid, ignore - } - - // set the properties that were specified with IKVM.Runtime.Launcher.SetProperties() - cli.System.Collections.IDictionary importProperties = get_ImportProperties(); - if (importProperties != null) { - cli.System.Collections.IDictionaryEnumerator entries = importProperties.GetEnumerator(); - while (entries.MoveNext()) { - p.setProperty((String)entries.get_Key(), (String)entries.get_Value()); - } - } - } - - private static void initCommonProperties(Properties p) - { - p.setProperty("java.version", PropertyConstants.java_version); - p.setProperty("java.vendor", PropertyConstants.java_vendor); - p.setProperty("java.vendor.url", PropertyConstants.java_vendor_url); - p.setProperty("java.vendor.url.bug", PropertyConstants.java_vendor_url_bug); - p.setProperty("java.vm.name", PropertyConstants.java_vm_name); - p.setProperty("java.vm.version", PropertyConstants.java_vm_version); - p.setProperty("java.vm.vendor", PropertyConstants.java_vm_vendor); - p.setProperty("java.vm.specification.name", "Java Virtual Machine Specification"); - p.setProperty("java.vm.specification.version", PropertyConstants.java_vm_specification_version); - p.setProperty("java.vm.specification.vendor", PropertyConstants.java_vm_specification_vendor); - p.setProperty("java.runtime.name", PropertyConstants.java_runtime_name); - p.setProperty("java.runtime.version", PropertyConstants.java_runtime_version); - p.setProperty("java.specification.name", "Java Platform API Specification"); - p.setProperty("java.specification.version", PropertyConstants.java_specification_version); - p.setProperty("java.specification.vendor", PropertyConstants.java_specification_vendor); - p.setProperty("java.class.version", "52.0"); - p.setProperty("java.class.path", ""); - p.setProperty("java.library.path", getLibraryPath()); - - try { - if (false) throw new cli.System.Security.SecurityException(); - p.setProperty("java.io.tmpdir", Path.GetTempPath()); - } catch (cli.System.Security.SecurityException _) { - p.setProperty("java.io.tmpdir", "."); - } - p.setProperty("java.ext.dirs", ""); - - // NOTE os.name *must* contain "Windows" when running on Windows, because Classpath tests on that - String osname = null; - String osversion = null; - if (RuntimeInformation.IsOSPlatform(OSPlatform.get_Windows())) { - cli.System.OperatingSystem os = cli.System.Environment.get_OSVersion(); - int major = os.get_Version().get_Major(); - int minor = os.get_Version().get_Minor(); - int build = os.get_Version().get_Build(); - - switch (os.get_Platform().Value) { - case cli.System.PlatformID.Win32Windows: - if (major == 4) { - switch (minor) { - case 0: - osname = "Windows 95"; - break; - case 10: - osname = "Windows 98"; - break; - case 90: - osname = "Windows Me"; - break; - default: - osname = "Windows 9X (unknown)"; - } - } else { - osname = "Windows 9X (unknown)"; - } - break; - case cli.System.PlatformID.Win32NT: - FileVersionInfo kernel32 = getKernel32FileVersionInfo(); - if (kernel32 != null) { - major = kernel32.get_ProductMajorPart(); - minor = kernel32.get_ProductMinorPart(); - build = kernel32.get_ProductBuildPart(); - } - - byte productType = getWindowsProductType(); - if (productType < 0) { - // error - } - - osname = "Windows NT (unknown)"; - switch (major) { - case 3: - case 4: - osname = "Windows NT"; - break; - case 5: - switch (minor) { - case 0: - osname = "Windows 2000"; - break; - case 1: - osname = "Windows XP"; - break; - case 2: - if (productType == VER_NT_WORKSTATION && Environment.get_Is64BitOperatingSystem()) { - osname = "Windows XP"; - } else { - osname = "Windows 2003"; - } - break; - default: - osname = "Windows NT (unknown)"; - break; - } - break; - case 6: - if (productType == VER_NT_WORKSTATION) { - switch (minor) { - case 0: - osname = "Windows Vista"; - break; - case 1: - osname = "Windows 7"; - break; - case 2: - osname = "Windows 8"; - break; - case 3: - osname = "Windows 8.1"; - break; - default: - osname = "Windows NT (unknown)"; - break; - } - } else { - switch (minor) { - case 0: - osname = "Windows Server 2008"; - break; - case 1: - osname = "Windows Server 2008 R2"; - break; - case 2: - osname = "Windows Server 2012"; - break; - case 3: - osname = "Windows Server 2012 R2"; - break; - default: - osname = "Windows NT (unknown)"; - break; - } - } - break; - case 10: - if (productType == VER_NT_WORKSTATION) { - switch (minor) { - case 0: - if (build >= 22000) { - osname = "Windows 11"; - } else { - osname = "Windows 10"; - } - break; - default: - osname = "Windows NT (unknown)"; - break; - } - } else { - switch (minor) { - case 0: - if (build > 20347) { - osname = "Windows Server 2022"; - } else if (build > 17676) { - osname = "Windows Server 2019"; - } else { - osname = "Windows Server 2016"; - } - break; - default: - osname = "Windows NT (unknown)"; - break; - } - } - break; - default: - osname = "Windows (unknown)"; - break; - } - break; - } - - osversion = major + "." + minor; - } else if (RuntimeInformation.IsOSPlatform(OSPlatform.get_Linux())) { - String[] sysname = getLinuxSysnameAndRelease(); - osname = sysname[0]; - osversion = sysname[1]; - } else if (RuntimeInformation.IsOSPlatform(OSPlatform.get_OSX())) { - // openjdk calls Foundation libraries here - } - - if (osname == null) { - osname = cli.System.Environment.get_OSVersion().ToString(); - } - - p.setProperty("os.name", osname); - p.setProperty("os.version", osversion); - - String arch = SafeGetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); - if (arch == null) - { - // we don't know, so we make a guess - if (cli.System.IntPtr.get_Size() == 4) { - arch = RuntimeInformation.IsOSPlatform(OSPlatform.get_Windows()) ? "x86" : "i386"; - } else { - arch = "amd64"; - } - } - - if (arch.equals("AMD64")) { - arch = "amd64"; - } - - p.setProperty("os.arch", arch); - p.setProperty("sun.arch.data.model", String.valueOf(cli.System.IntPtr.get_Size() * 8)); - p.setProperty("file.separator", cli.System.Char.ToString(Path.DirectorySeparatorChar)); - p.setProperty("file.encoding", cli.System.Text.Encoding.get_Default().get_WebName()); - p.setProperty("path.separator", String.valueOf(Path.PathSeparator)); - p.setProperty("line.separator", cli.System.Environment.get_NewLine()); - - try { - if (false) throw new cli.System.Security.SecurityException(); - p.setProperty("user.name", cli.System.Environment.get_UserName()); - } catch(cli.System.Security.SecurityException _) { - p.setProperty("user.name", "(unknown)"); - } - - String home = SafeGetEnvironmentVariable("USERPROFILE"); - if (home == null) { - // maybe we're on *nix - home = SafeGetEnvironmentVariable("HOME"); - if (home == null) - { - // TODO maybe there is a better way - // NOTE on MS .NET this doesn't return the correct path - // (it returns "C:\\Documents and Settings\\username\\My Documents", but we really need - // "C:\\Documents and Settings\\username" to be compatible with Sun, that's why we use %USERPROFILE% if it exists) - try { - if (false) throw new cli.System.Security.SecurityException(); - home = cli.System.Environment.GetFolderPath(cli.System.Environment.SpecialFolder.wrap(cli.System.Environment.SpecialFolder.Personal)); - } catch (cli.System.Security.SecurityException _) { - home = "."; - } - } - } - p.setProperty("user.home", home); - - try { - if (false) throw new cli.System.Security.SecurityException(); - p.setProperty("user.dir", cli.System.Environment.get_CurrentDirectory()); - } catch (cli.System.Security.SecurityException _) { - p.setProperty("user.dir", "."); - } - } - - private static void initI18NProperties(Properties p) { - String[] culture = ((cli.System.String)(Object)cli.System.Globalization.CultureInfo.get_CurrentCulture().get_Name()).Split(new char[] { '-' }); - String language; - String script; - String region; - String variant; - - if (culture.length == 2) { - language = culture[0]; - if (culture[1].length() == 4) { - script = culture[1]; - region = ""; - } else { - script = ""; - region = culture[1]; - } - } else if (culture.length == 3) { - language = culture[0]; - script = culture[1]; - region = culture[2]; - } else { - language = "en"; - script = ""; - region = "US"; - } - - // Norwegian - if (language.equals("nb")) - { - language = "no"; - region = "NO"; - variant = ""; - } else if (language.equals("nn")) { - language = "no"; - region = "NO"; - variant = "NY"; - } else { - variant = ""; - } - - p.setProperty("user.language", language); - p.setProperty("user.country", region); - p.setProperty("user.variant", variant); - p.setProperty("user.script", script); - } - - private static native cli.System.Collections.IDictionary get_ImportProperties(); - private static native cli.System.Reflection.Assembly getRuntimeAssembly(); - private static native String[] getIkvmHomeArchs(); - private static native String getVirtualFileSystemRoot(); - private static native String getBootClassPath(); - private static native String getStdoutEncoding(); - private static native String getStderrEncoding(); - private static native FileVersionInfo getKernel32FileVersionInfo(); - private static native byte getWindowsProductType(); - private static native String[] getLinuxSysnameAndRelease(); - - private VMSystemProperties() { } - -} diff --git a/src/IKVM.Java/local/java/lang/invoke/DirectMethodHandle.java b/src/IKVM.Java/local/java/lang/invoke/DirectMethodHandle.java new file mode 100644 index 0000000000..7a125e506e --- /dev/null +++ b/src/IKVM.Java/local/java/lang/invoke/DirectMethodHandle.java @@ -0,0 +1,695 @@ +/* + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import sun.misc.Unsafe; +import java.lang.reflect.Method; +import java.util.Arrays; +import sun.invoke.util.VerifyAccess; +import static java.lang.invoke.MethodHandleNatives.Constants.*; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.MethodTypeForm.*; +import static java.lang.invoke.MethodHandleStatics.*; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import sun.invoke.util.ValueConversions; +import sun.invoke.util.VerifyType; +import sun.invoke.util.Wrapper; + +/** + * The flavor of method handle which implements a constant reference + * to a class member. + * @author jrose + */ +class DirectMethodHandle extends MethodHandle { + final MemberName member; + + // Constructors and factory methods in this class *must* be package scoped or private. + private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) { + super(mtype, form); + if (!member.isResolved()) throw new InternalError(); + + if (member.getDeclaringClass().isInterface() && + member.isMethod() && !member.isAbstract()) { + // Check for corner case: invokeinterface of Object method + MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind()); + m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null); + if (m != null && m.isPublic()) { + assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong + member = m; + } + } + + this.member = member; + } + + // Factory methods: + static DirectMethodHandle make(byte refKind, Class receiver, MemberName member) { + MethodType mtype = member.getMethodOrFieldType(); + if (!member.isStatic()) { + if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor()) + throw new InternalError(member.toString()); + mtype = mtype.insertParameterTypes(0, receiver); + } + if (!member.isField()) { + if (refKind == REF_invokeSpecial) { + member = member.asSpecial(); + LambdaForm lform = preparedLambdaForm(member); + return new Special(mtype, lform, member); + } else { + LambdaForm lform = preparedLambdaForm(member); + return new DirectMethodHandle(mtype, lform, member); + } + } else { + LambdaForm lform = preparedFieldLambdaForm(member); + if (member.isStatic()) { + long offset = MethodHandleNatives.staticFieldOffset(member); + Object base = MethodHandleNatives.staticFieldBase(member); + return new StaticAccessor(mtype, lform, member, base, offset); + } else { + long offset = MethodHandleNatives.objectFieldOffset(member); + assert(offset == offset); + return new Accessor(mtype, lform, member, offset); + } + } + } + static DirectMethodHandle make(Class receiver, MemberName member) { + byte refKind = member.getReferenceKind(); + if (refKind == REF_invokeSpecial) + refKind = REF_invokeVirtual; + return make(refKind, receiver, member); + } + static DirectMethodHandle make(MemberName member) { + if (member.isConstructor()) + return makeAllocator(member); + return make(member.getDeclaringClass(), member); + } + static DirectMethodHandle make(Method method) { + return make(method.getDeclaringClass(), new MemberName(method)); + } + static DirectMethodHandle make(Field field) { + return make(field.getDeclaringClass(), new MemberName(field)); + } + private static DirectMethodHandle makeAllocator(MemberName ctor) { + assert(ctor.isConstructor() && ctor.getName().equals("")); + Class instanceClass = ctor.getDeclaringClass(); + ctor = ctor.asConstructor(); + assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor; + MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass); + LambdaForm lform = preparedLambdaForm(ctor); + MemberName init = ctor.asSpecial(); + assert(init.getMethodType().returnType() == void.class); + return new Constructor(mtype, lform, ctor, init, instanceClass); + } + + @Override + BoundMethodHandle rebind() { + return BoundMethodHandle.makeReinvoker(this); + } + + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses + return new DirectMethodHandle(mt, lf, member); + } + + @Override + String internalProperties() { + return "\n& DMH.MN="+internalMemberName(); + } + + //// Implementation methods. + @Override + @ForceInline + MemberName internalMemberName() { + return member; + } + + private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); + + /** + * Create a LF which can invoke the given method. + * Cache and share this structure among all methods with + * the same basicType and refKind. + */ + private static LambdaForm preparedLambdaForm(MemberName m) { + assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead + MethodType mtype = m.getInvocationType().basicType(); + assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m; + int which; + switch (m.getReferenceKind()) { + case REF_invokeVirtual: which = LF_INVVIRTUAL; break; + case REF_invokeStatic: which = LF_INVSTATIC; break; + case REF_invokeSpecial: which = LF_INVSPECIAL; break; + case REF_invokeInterface: which = LF_INVINTERFACE; break; + case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break; + default: throw new InternalError(m.toString()); + } + if (which == LF_INVSTATIC && shouldBeInitialized(m)) { + // precompute the barrier-free version: + preparedLambdaForm(mtype, which); + which = LF_INVSTATIC_INIT; + } + LambdaForm lform = preparedLambdaForm(mtype, which); + maybeCompile(lform, m); + assert(lform.methodType().dropParameterTypes(0, 1) + .equals(m.getInvocationType().basicType())) + : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); + return lform; + } + + private static LambdaForm preparedLambdaForm(MethodType mtype, int which) { + LambdaForm lform = mtype.form().cachedLambdaForm(which); + if (lform != null) return lform; + lform = makePreparedLambdaForm(mtype, which); + return mtype.form().setCachedLambdaForm(which, lform); + } + + private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { + boolean needsInit = (which == LF_INVSTATIC_INIT); + boolean doesAlloc = (which == LF_NEWINVSPECIAL); + String linkerName, lambdaName; + switch (which) { + case LF_INVVIRTUAL: linkerName = "linkToVirtual"; lambdaName = "DMH.invokeVirtual"; break; + case LF_INVSTATIC: linkerName = "linkToStatic"; lambdaName = "DMH.invokeStatic"; break; + case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; lambdaName = "DMH.invokeStaticInit"; break; + case LF_INVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.invokeSpecial"; break; + case LF_INVINTERFACE: linkerName = "linkToInterface"; lambdaName = "DMH.invokeInterface"; break; + case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.newInvokeSpecial"; break; + default: throw new InternalError("which="+which); + } + MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class); + if (doesAlloc) + mtypeWithArg = mtypeWithArg + .insertParameterTypes(0, Object.class) // insert newly allocated obj + .changeReturnType(void.class); // returns void + MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic); + try { + linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + final int DMH_THIS = 0; + final int ARG_BASE = 1; + final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); + int nameCursor = ARG_LIMIT; + final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1); + final int GET_MEMBER = nameCursor++; + final int LINKER_CALL = nameCursor++; + Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + assert(names.length == nameCursor); + if (doesAlloc) { + // names = { argx,y,z,... new C, init method } + names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]); + names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]); + } else if (needsInit) { + names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]); + } else { + names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]); + } + assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); + Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); + assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args! + int result = LAST_RESULT; + if (doesAlloc) { + assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one + System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2); + outArgs[0] = names[NEW_OBJ]; + result = NEW_OBJ; + } + names[LINKER_CALL] = new Name(linker, outArgs); + lambdaName += "_" + shortenSignature(basicTypeSignature(mtype)); + LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result); + // This is a tricky bit of code. Don't send it through the LF interpreter. + lform.compileToBytecode(); + return lform; + } + + static Object findDirectMethodHandle(Name name) { + if (name.function == Lazy.NF_internalMemberName || + name.function == Lazy.NF_internalMemberNameEnsureInit || + name.function == Lazy.NF_constructorMethod) { + assert(name.arguments.length == 1); + return name.arguments[0]; + } + return null; + } + + private static void maybeCompile(LambdaForm lform, MemberName m) { + if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class)) + // Help along bootstrapping... + lform.compileToBytecode(); + } + + /** Static wrapper for DirectMethodHandle.internalMemberName. */ + @ForceInline + /*non-public*/ static Object internalMemberName(Object mh) { + return ((DirectMethodHandle)mh).member; + } + + /** Static wrapper for DirectMethodHandle.internalMemberName. + * This one also forces initialization. + */ + /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) { + DirectMethodHandle dmh = (DirectMethodHandle)mh; + dmh.ensureInitialized(); + return dmh.member; + } + + /*non-public*/ static + boolean shouldBeInitialized(MemberName member) { + switch (member.getReferenceKind()) { + case REF_invokeStatic: + case REF_getStatic: + case REF_putStatic: + case REF_newInvokeSpecial: + break; + default: + // No need to initialize the class on this kind of member. + return false; + } + Class cls = member.getDeclaringClass(); + if (cls == ValueConversions.class || + cls == MethodHandleImpl.class || + cls == Invokers.class) { + // These guys have lots of DMH creation but we know + // the MHs will not be used until the system is booted. + return false; + } + if (VerifyAccess.isSamePackage(MethodHandle.class, cls) || + VerifyAccess.isSamePackage(ValueConversions.class, cls)) { + // It is a system class. It is probably in the process of + // being initialized, but we will help it along just to be safe. + if (UNSAFE.shouldBeInitialized(cls)) { + UNSAFE.ensureClassInitialized(cls); + } + return false; + } + return UNSAFE.shouldBeInitialized(cls); + } + + private static class EnsureInitialized extends ClassValue> { + @Override + protected WeakReference computeValue(Class type) { + UNSAFE.ensureClassInitialized(type); + if (UNSAFE.shouldBeInitialized(type)) + // If the previous call didn't block, this can happen. + // We are executing inside . + return new WeakReference<>(Thread.currentThread()); + return null; + } + static final EnsureInitialized INSTANCE = new EnsureInitialized(); + } + + private void ensureInitialized() { + if (checkInitialized(member)) { + // The coast is clear. Delete the barrier. + if (member.isField()) + updateForm(preparedFieldLambdaForm(member)); + else + updateForm(preparedLambdaForm(member)); + } + } + private static boolean checkInitialized(MemberName member) { + Class defc = member.getDeclaringClass(); + WeakReference ref = EnsureInitialized.INSTANCE.get(defc); + if (ref == null) { + return true; // the final state + } + Thread clinitThread = ref.get(); + // Somebody may still be running defc.. + if (clinitThread == Thread.currentThread()) { + // If anybody is running defc., it is this thread. + if (UNSAFE.shouldBeInitialized(defc)) + // Yes, we are running it; keep the barrier for now. + return false; + } else { + // We are in a random thread. Block. + UNSAFE.ensureClassInitialized(defc); + } + assert(!UNSAFE.shouldBeInitialized(defc)); + // put it into the final state + EnsureInitialized.INSTANCE.remove(defc); + return true; + } + + /*non-public*/ static void ensureInitialized(Object mh) { + ((DirectMethodHandle)mh).ensureInitialized(); + } + + /** This subclass represents invokespecial instructions. */ + static class Special extends DirectMethodHandle { + private Special(MethodType mtype, LambdaForm form, MemberName member) { + super(mtype, form, member); + } + @Override + boolean isInvokeSpecial() { + return true; + } + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Special(mt, lf, member); + } + } + + /** This subclass handles constructor references. */ + static class Constructor extends DirectMethodHandle { + final MemberName initMethod; + final Class instanceClass; + + private Constructor(MethodType mtype, LambdaForm form, MemberName constructor, + MemberName initMethod, Class instanceClass) { + super(mtype, form, constructor); + this.initMethod = initMethod; + this.instanceClass = instanceClass; + assert(initMethod.isResolved()); + } + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Constructor(mt, lf, member, initMethod, instanceClass); + } + } + + /*non-public*/ static Object constructorMethod(Object mh) { + Constructor dmh = (Constructor)mh; + return dmh.initMethod; + } + + /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException { + Constructor dmh = (Constructor)mh; + return UNSAFE.allocateInstance(dmh.instanceClass); + } + + /** This subclass handles non-static field references. */ + static class Accessor extends DirectMethodHandle { + final Class fieldType; + final long fieldOffset; + private Accessor(MethodType mtype, LambdaForm form, MemberName member, + long fieldOffset) { + super(mtype, form, member); + this.fieldType = member.getFieldType(); + this.fieldOffset = fieldOffset; + } + + @Override Object checkCast(Object obj) { + return fieldType.cast(obj); + } + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Accessor(mt, lf, member, fieldOffset); + } + } + + @ForceInline + /*non-public*/ static long fieldOffset(Object accessorObj) { + // Note: We return a long because that is what Unsafe.getObject likes. + // We store a plain int because it is more compact. + return ((Accessor)accessorObj).fieldOffset; + } + + @ForceInline + /*non-public*/ static Object checkBase(Object obj) { + // Note that the object's class has already been verified, + // since the parameter type of the Accessor method handle + // is either member.getDeclaringClass or a subclass. + // This was verified in DirectMethodHandle.make. + // Therefore, the only remaining check is for null. + // Since this check is *not* guaranteed by Unsafe.getInt + // and its siblings, we need to make an explicit one here. + obj.getClass(); // maybe throw NPE + return obj; + } + + /** This subclass handles static field references. */ + static class StaticAccessor extends DirectMethodHandle { + final private Class fieldType; + final private Object staticBase; + final private long staticOffset; + + private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member, + Object staticBase, long staticOffset) { + super(mtype, form, member); + this.fieldType = member.getFieldType(); + this.staticBase = staticBase; + this.staticOffset = staticOffset; + } + + @Override Object checkCast(Object obj) { + return fieldType.cast(obj); + } + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new StaticAccessor(mt, lf, member, staticBase, staticOffset); + } + } + + @ForceInline + /*non-public*/ static Object nullCheck(Object obj) { + obj.getClass(); + return obj; + } + + @ForceInline + /*non-public*/ static Object staticBase(Object accessorObj) { + return ((StaticAccessor)accessorObj).staticBase; + } + + @ForceInline + /*non-public*/ static long staticOffset(Object accessorObj) { + return ((StaticAccessor)accessorObj).staticOffset; + } + + @ForceInline + /*non-public*/ static Object checkCast(Object mh, Object obj) { + return ((DirectMethodHandle) mh).checkCast(obj); + } + + Object checkCast(Object obj) { + return member.getReturnType().cast(obj); + } + + // Caching machinery for field accessors: + private static byte + AF_GETFIELD = 0, + AF_PUTFIELD = 1, + AF_GETSTATIC = 2, + AF_PUTSTATIC = 3, + AF_GETSTATIC_INIT = 4, + AF_PUTSTATIC_INIT = 5, + AF_LIMIT = 6; + // Enumerate the different field kinds using Wrapper, + // with an extra case added for checked references. + private static int + FT_LAST_WRAPPER = Wrapper.values().length-1, + FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(), + FT_CHECKED_REF = FT_LAST_WRAPPER+1, + FT_LIMIT = FT_LAST_WRAPPER+2; + private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) { + return ((formOp * FT_LIMIT * 2) + + (isVolatile ? FT_LIMIT : 0) + + ftypeKind); + } + private static final LambdaForm[] ACCESSOR_FORMS + = new LambdaForm[afIndex(AF_LIMIT, false, 0)]; + private static int ftypeKind(Class ftype) { + if (ftype.isPrimitive()) + return Wrapper.forPrimitiveType(ftype).ordinal(); + else if (VerifyType.isNullReferenceConversion(Object.class, ftype)) + return FT_UNCHECKED_REF; + else + return FT_CHECKED_REF; + } + + /** + * Create a LF which can access the given field. + * Cache and share this structure among all fields with + * the same basicType and refKind. + */ + private static LambdaForm preparedFieldLambdaForm(MemberName m) { + Class ftype = m.getFieldType(); + boolean isVolatile = m.isVolatile(); + byte formOp; + switch (m.getReferenceKind()) { + case REF_getField: formOp = AF_GETFIELD; break; + case REF_putField: formOp = AF_PUTFIELD; break; + case REF_getStatic: formOp = AF_GETSTATIC; break; + case REF_putStatic: formOp = AF_PUTSTATIC; break; + default: throw new InternalError(m.toString()); + } + if (shouldBeInitialized(m)) { + // precompute the barrier-free version: + preparedFieldLambdaForm(formOp, isVolatile, ftype); + assert((AF_GETSTATIC_INIT - AF_GETSTATIC) == + (AF_PUTSTATIC_INIT - AF_PUTSTATIC)); + formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC); + } + LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype); + maybeCompile(lform, m); + assert(lform.methodType().dropParameterTypes(0, 1) + .equals(m.getInvocationType().basicType())) + : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); + return lform; + } + private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class ftype) { + int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype)); + LambdaForm lform = ACCESSOR_FORMS[afIndex]; + if (lform != null) return lform; + lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype)); + ACCESSOR_FORMS[afIndex] = lform; // don't bother with a CAS + return lform; + } + + private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) { + boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1); + boolean isStatic = (formOp >= AF_GETSTATIC); + boolean needsInit = (formOp >= AF_GETSTATIC_INIT); + boolean needsCast = (ftypeKind == FT_CHECKED_REF); + Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]); + Class ft = fw.primitiveType(); + assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind); + String tname = fw.primitiveSimpleName(); + String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1); + if (isVolatile) ctname += "Volatile"; + String getOrPut = (isGetter ? "get" : "put"); + String linkerName = (getOrPut + ctname); // getObject, putIntVolatile, etc. + MethodType linkerType; + if (isGetter) + linkerType = MethodType.methodType(ft, Object.class, long.class); + else + linkerType = MethodType.methodType(void.class, Object.class, long.class, ft); + MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual); + try { + linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + + // What is the external type of the lambda form? + MethodType mtype; + if (isGetter) + mtype = MethodType.methodType(ft); + else + mtype = MethodType.methodType(void.class, ft); + mtype = mtype.basicType(); // erase short to int, etc. + if (!isStatic) + mtype = mtype.insertParameterTypes(0, Object.class); + final int DMH_THIS = 0; + final int ARG_BASE = 1; + final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); + // if this is for non-static access, the base pointer is stored at this index: + final int OBJ_BASE = isStatic ? -1 : ARG_BASE; + // if this is for write access, the value to be written is stored at this index: + final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1; + int nameCursor = ARG_LIMIT; + final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any + final int F_OFFSET = nameCursor++; // Either static offset or field offset. + final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1); + final int INIT_BAR = (needsInit ? nameCursor++ : -1); + final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1); + final int LINKER_CALL = nameCursor++; + final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1); + final int RESULT = nameCursor-1; // either the call or the cast + Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + if (needsInit) + names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]); + if (needsCast && !isGetter) + names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]); + Object[] outArgs = new Object[1 + linkerType.parameterCount()]; + assert(outArgs.length == (isGetter ? 3 : 4)); + outArgs[0] = UNSAFE; + if (isStatic) { + outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]); + outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]); + } else { + outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]); + outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]); + } + if (!isGetter) { + outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]); + } + for (Object a : outArgs) assert(a != null); + names[LINKER_CALL] = new Name(linker, outArgs); + if (needsCast && isGetter) + names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); + for (Name n : names) assert(n != null); + String fieldOrStatic = (isStatic ? "Static" : "Field"); + String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging + if (needsCast) lambdaName += "Cast"; + if (needsInit) lambdaName += "Init"; + return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT); + } + + /** + * Pre-initialized NamedFunctions for bootstrapping purposes. + * Factored in an inner class to delay initialization until first usage. + */ + private static class Lazy { + static final NamedFunction + NF_internalMemberName, + NF_internalMemberNameEnsureInit, + NF_ensureInitialized, + NF_fieldOffset, + NF_checkBase, + NF_staticBase, + NF_staticOffset, + NF_checkCast, + NF_allocateInstance, + NF_constructorMethod; + static { + try { + NamedFunction nfs[] = { + NF_internalMemberName = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberName", Object.class)), + NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), + NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("ensureInitialized", Object.class)), + NF_fieldOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("fieldOffset", Object.class)), + NF_checkBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkBase", Object.class)), + NF_staticBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticBase", Object.class)), + NF_staticOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticOffset", Object.class)), + NF_checkCast = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkCast", Object.class, Object.class)), + NF_allocateInstance = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("allocateInstance", Object.class)), + NF_constructorMethod = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("constructorMethod", Object.class)) + }; + for (NamedFunction nf : nfs) { + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; + nf.resolve(); + } + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + } +} diff --git a/src/IKVM.Java/local/java/nio/Bits.java b/src/IKVM.Java/local/java/nio/Bits.java deleted file mode 100644 index cea374fcf1..0000000000 --- a/src/IKVM.Java/local/java/nio/Bits.java +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.nio; - -import java.security.AccessController; -import sun.misc.Unsafe; -import sun.misc.VM; - -/** - * Access to bits, native and otherwise. - */ - -class Bits { // package-private - - private Bits() { } - - - // -- Swapping -- - - static short swap(short x) { - return Short.reverseBytes(x); - } - - static char swap(char x) { - return Character.reverseBytes(x); - } - - static int swap(int x) { - return Integer.reverseBytes(x); - } - - static long swap(long x) { - return Long.reverseBytes(x); - } - - - // -- get/put char -- - - static private char makeChar(byte b1, byte b0) { - return (char)((b1 << 8) | (b0 & 0xff)); - } - - static char getCharL(ByteBuffer bb, int bi) { - return makeChar(bb._get(bi + 1), - bb._get(bi )); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static char getCharL(long a) { - return makeChar(_get(a + 1), - _get(a )); - } - - static char getCharB(ByteBuffer bb, int bi) { - return makeChar(bb._get(bi ), - bb._get(bi + 1)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static char getCharB(long a) { - return makeChar(_get(a ), - _get(a + 1)); - } - - static char getChar(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getCharB(bb, bi) : getCharL(bb, bi); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static char getChar(long a, boolean bigEndian) { - return bigEndian ? getCharB(a) : getCharL(a); - } - - private static byte char1(char x) { return (byte)(x >> 8); } - private static byte char0(char x) { return (byte)(x ); } - - static void putCharL(ByteBuffer bb, int bi, char x) { - bb._put(bi , char0(x)); - bb._put(bi + 1, char1(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putCharL(long a, char x) { - _put(a , char0(x)); - _put(a + 1, char1(x)); - } - - static void putCharB(ByteBuffer bb, int bi, char x) { - bb._put(bi , char1(x)); - bb._put(bi + 1, char0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putCharB(long a, char x) { - _put(a , char1(x)); - _put(a + 1, char0(x)); - } - - static void putChar(ByteBuffer bb, int bi, char x, boolean bigEndian) { - if (bigEndian) - putCharB(bb, bi, x); - else - putCharL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putChar(long a, char x, boolean bigEndian) { - if (bigEndian) - putCharB(a, x); - else - putCharL(a, x); - } - - - // -- get/put short -- - - static private short makeShort(byte b1, byte b0) { - return (short)((b1 << 8) | (b0 & 0xff)); - } - - static short getShortL(ByteBuffer bb, int bi) { - return makeShort(bb._get(bi + 1), - bb._get(bi )); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static short getShortL(long a) { - return makeShort(_get(a + 1), - _get(a )); - } - - static short getShortB(ByteBuffer bb, int bi) { - return makeShort(bb._get(bi ), - bb._get(bi + 1)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static short getShortB(long a) { - return makeShort(_get(a ), - _get(a + 1)); - } - - static short getShort(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getShortB(bb, bi) : getShortL(bb, bi); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static short getShort(long a, boolean bigEndian) { - return bigEndian ? getShortB(a) : getShortL(a); - } - - private static byte short1(short x) { return (byte)(x >> 8); } - private static byte short0(short x) { return (byte)(x ); } - - static void putShortL(ByteBuffer bb, int bi, short x) { - bb._put(bi , short0(x)); - bb._put(bi + 1, short1(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putShortL(long a, short x) { - _put(a , short0(x)); - _put(a + 1, short1(x)); - } - - static void putShortB(ByteBuffer bb, int bi, short x) { - bb._put(bi , short1(x)); - bb._put(bi + 1, short0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putShortB(long a, short x) { - _put(a , short1(x)); - _put(a + 1, short0(x)); - } - - static void putShort(ByteBuffer bb, int bi, short x, boolean bigEndian) { - if (bigEndian) - putShortB(bb, bi, x); - else - putShortL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putShort(long a, short x, boolean bigEndian) { - if (bigEndian) - putShortB(a, x); - else - putShortL(a, x); - } - - - // -- get/put int -- - - static private int makeInt(byte b3, byte b2, byte b1, byte b0) { - return (((b3 ) << 24) | - ((b2 & 0xff) << 16) | - ((b1 & 0xff) << 8) | - ((b0 & 0xff) )); - } - - static int getIntL(ByteBuffer bb, int bi) { - return makeInt(bb._get(bi + 3), - bb._get(bi + 2), - bb._get(bi + 1), - bb._get(bi )); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static int getIntL(long a) { - return makeInt(_get(a + 3), - _get(a + 2), - _get(a + 1), - _get(a )); - } - - static int getIntB(ByteBuffer bb, int bi) { - return makeInt(bb._get(bi ), - bb._get(bi + 1), - bb._get(bi + 2), - bb._get(bi + 3)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static int getIntB(long a) { - return makeInt(_get(a ), - _get(a + 1), - _get(a + 2), - _get(a + 3)); - } - - static int getInt(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getIntB(bb, bi) : getIntL(bb, bi) ; - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static int getInt(long a, boolean bigEndian) { - return bigEndian ? getIntB(a) : getIntL(a) ; - } - - private static byte int3(int x) { return (byte)(x >> 24); } - private static byte int2(int x) { return (byte)(x >> 16); } - private static byte int1(int x) { return (byte)(x >> 8); } - private static byte int0(int x) { return (byte)(x ); } - - static void putIntL(ByteBuffer bb, int bi, int x) { - bb._put(bi + 3, int3(x)); - bb._put(bi + 2, int2(x)); - bb._put(bi + 1, int1(x)); - bb._put(bi , int0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putIntL(long a, int x) { - _put(a + 3, int3(x)); - _put(a + 2, int2(x)); - _put(a + 1, int1(x)); - _put(a , int0(x)); - } - - static void putIntB(ByteBuffer bb, int bi, int x) { - bb._put(bi , int3(x)); - bb._put(bi + 1, int2(x)); - bb._put(bi + 2, int1(x)); - bb._put(bi + 3, int0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putIntB(long a, int x) { - _put(a , int3(x)); - _put(a + 1, int2(x)); - _put(a + 2, int1(x)); - _put(a + 3, int0(x)); - } - - static void putInt(ByteBuffer bb, int bi, int x, boolean bigEndian) { - if (bigEndian) - putIntB(bb, bi, x); - else - putIntL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putInt(long a, int x, boolean bigEndian) { - if (bigEndian) - putIntB(a, x); - else - putIntL(a, x); - } - - - // -- get/put long -- - - static private long makeLong(byte b7, byte b6, byte b5, byte b4, - byte b3, byte b2, byte b1, byte b0) - { - return ((((long)b7 ) << 56) | - (((long)b6 & 0xff) << 48) | - (((long)b5 & 0xff) << 40) | - (((long)b4 & 0xff) << 32) | - (((long)b3 & 0xff) << 24) | - (((long)b2 & 0xff) << 16) | - (((long)b1 & 0xff) << 8) | - (((long)b0 & 0xff) )); - } - - static long getLongL(ByteBuffer bb, int bi) { - return makeLong(bb._get(bi + 7), - bb._get(bi + 6), - bb._get(bi + 5), - bb._get(bi + 4), - bb._get(bi + 3), - bb._get(bi + 2), - bb._get(bi + 1), - bb._get(bi )); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static long getLongL(long a) { - return makeLong(_get(a + 7), - _get(a + 6), - _get(a + 5), - _get(a + 4), - _get(a + 3), - _get(a + 2), - _get(a + 1), - _get(a )); - } - - static long getLongB(ByteBuffer bb, int bi) { - return makeLong(bb._get(bi ), - bb._get(bi + 1), - bb._get(bi + 2), - bb._get(bi + 3), - bb._get(bi + 4), - bb._get(bi + 5), - bb._get(bi + 6), - bb._get(bi + 7)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static long getLongB(long a) { - return makeLong(_get(a ), - _get(a + 1), - _get(a + 2), - _get(a + 3), - _get(a + 4), - _get(a + 5), - _get(a + 6), - _get(a + 7)); - } - - static long getLong(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getLongB(bb, bi) : getLongL(bb, bi); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static long getLong(long a, boolean bigEndian) { - return bigEndian ? getLongB(a) : getLongL(a); - } - - private static byte long7(long x) { return (byte)(x >> 56); } - private static byte long6(long x) { return (byte)(x >> 48); } - private static byte long5(long x) { return (byte)(x >> 40); } - private static byte long4(long x) { return (byte)(x >> 32); } - private static byte long3(long x) { return (byte)(x >> 24); } - private static byte long2(long x) { return (byte)(x >> 16); } - private static byte long1(long x) { return (byte)(x >> 8); } - private static byte long0(long x) { return (byte)(x ); } - - static void putLongL(ByteBuffer bb, int bi, long x) { - bb._put(bi + 7, long7(x)); - bb._put(bi + 6, long6(x)); - bb._put(bi + 5, long5(x)); - bb._put(bi + 4, long4(x)); - bb._put(bi + 3, long3(x)); - bb._put(bi + 2, long2(x)); - bb._put(bi + 1, long1(x)); - bb._put(bi , long0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putLongL(long a, long x) { - _put(a + 7, long7(x)); - _put(a + 6, long6(x)); - _put(a + 5, long5(x)); - _put(a + 4, long4(x)); - _put(a + 3, long3(x)); - _put(a + 2, long2(x)); - _put(a + 1, long1(x)); - _put(a , long0(x)); - } - - static void putLongB(ByteBuffer bb, int bi, long x) { - bb._put(bi , long7(x)); - bb._put(bi + 1, long6(x)); - bb._put(bi + 2, long5(x)); - bb._put(bi + 3, long4(x)); - bb._put(bi + 4, long3(x)); - bb._put(bi + 5, long2(x)); - bb._put(bi + 6, long1(x)); - bb._put(bi + 7, long0(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putLongB(long a, long x) { - _put(a , long7(x)); - _put(a + 1, long6(x)); - _put(a + 2, long5(x)); - _put(a + 3, long4(x)); - _put(a + 4, long3(x)); - _put(a + 5, long2(x)); - _put(a + 6, long1(x)); - _put(a + 7, long0(x)); - } - - static void putLong(ByteBuffer bb, int bi, long x, boolean bigEndian) { - if (bigEndian) - putLongB(bb, bi, x); - else - putLongL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putLong(long a, long x, boolean bigEndian) { - if (bigEndian) - putLongB(a, x); - else - putLongL(a, x); - } - - - // -- get/put float -- - - static float getFloatL(ByteBuffer bb, int bi) { - return Float.intBitsToFloat(getIntL(bb, bi)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static float getFloatL(long a) { - return Float.intBitsToFloat(getIntL(a)); - } - - static float getFloatB(ByteBuffer bb, int bi) { - return Float.intBitsToFloat(getIntB(bb, bi)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static float getFloatB(long a) { - return Float.intBitsToFloat(getIntB(a)); - } - - static float getFloat(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getFloatB(bb, bi) : getFloatL(bb, bi); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static float getFloat(long a, boolean bigEndian) { - return bigEndian ? getFloatB(a) : getFloatL(a); - } - - static void putFloatL(ByteBuffer bb, int bi, float x) { - putIntL(bb, bi, Float.floatToRawIntBits(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putFloatL(long a, float x) { - putIntL(a, Float.floatToRawIntBits(x)); - } - - static void putFloatB(ByteBuffer bb, int bi, float x) { - putIntB(bb, bi, Float.floatToRawIntBits(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putFloatB(long a, float x) { - putIntB(a, Float.floatToRawIntBits(x)); - } - - static void putFloat(ByteBuffer bb, int bi, float x, boolean bigEndian) { - if (bigEndian) - putFloatB(bb, bi, x); - else - putFloatL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putFloat(long a, float x, boolean bigEndian) { - if (bigEndian) - putFloatB(a, x); - else - putFloatL(a, x); - } - - - // -- get/put double -- - - static double getDoubleL(ByteBuffer bb, int bi) { - return Double.longBitsToDouble(getLongL(bb, bi)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static double getDoubleL(long a) { - return Double.longBitsToDouble(getLongL(a)); - } - - static double getDoubleB(ByteBuffer bb, int bi) { - return Double.longBitsToDouble(getLongB(bb, bi)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static double getDoubleB(long a) { - return Double.longBitsToDouble(getLongB(a)); - } - - static double getDouble(ByteBuffer bb, int bi, boolean bigEndian) { - return bigEndian ? getDoubleB(bb, bi) : getDoubleL(bb, bi); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static double getDouble(long a, boolean bigEndian) { - return bigEndian ? getDoubleB(a) : getDoubleL(a); - } - - static void putDoubleL(ByteBuffer bb, int bi, double x) { - putLongL(bb, bi, Double.doubleToRawLongBits(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putDoubleL(long a, double x) { - putLongL(a, Double.doubleToRawLongBits(x)); - } - - static void putDoubleB(ByteBuffer bb, int bi, double x) { - putLongB(bb, bi, Double.doubleToRawLongBits(x)); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putDoubleB(long a, double x) { - putLongB(a, Double.doubleToRawLongBits(x)); - } - - static void putDouble(ByteBuffer bb, int bi, double x, boolean bigEndian) { - if (bigEndian) - putDoubleB(bb, bi, x); - else - putDoubleL(bb, bi, x); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void putDouble(long a, double x, boolean bigEndian) { - if (bigEndian) - putDoubleB(a, x); - else - putDoubleL(a, x); - } - - - // -- Unsafe access -- - - private static final Unsafe unsafe = Unsafe.getUnsafe(); - - @cli.System.Security.SecurityCriticalAttribute.Annotation - private static byte _get(long a) { - return unsafe.getByte(a); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - private static void _put(long a, byte b) { - unsafe.putByte(a, b); - } - - static Unsafe unsafe() { - return unsafe; - } - - - // -- Processor and memory-system properties -- - - private static final ByteOrder byteOrder; - - static ByteOrder byteOrder() { - if (byteOrder == null) - throw new Error("Unknown byte order"); - return byteOrder; - } - - static { - if (cli.System.BitConverter.IsLittleEndian) { - byteOrder = ByteOrder.LITTLE_ENDIAN; - } else { - byteOrder = ByteOrder.BIG_ENDIAN; - } - } - - - private static int pageSize = -1; - - static int pageSize() { - if (pageSize == -1) - pageSize = unsafe().pageSize(); - return pageSize; - } - - static int pageCount(long size) { - return (int)(size + (long)pageSize() - 1L) / pageSize(); - } - - private static boolean unaligned; - private static boolean unalignedKnown = false; - - static boolean unaligned() { - if (unalignedKnown) - return unaligned; - String arch = AccessController.doPrivileged( - new sun.security.action.GetPropertyAction("os.arch")); - unaligned = arch.equals("i386") || arch.equals("x86") - || arch.equals("amd64") || arch.equals("x86_64"); - unalignedKnown = true; - return unaligned; - } - - - // -- Direct memory management -- - - // A user-settable upper limit on the maximum amount of allocatable - // direct buffer memory. This value may be changed during VM - // initialization if it is launched with "-XX:MaxDirectMemorySize=". - private static volatile long maxMemory = VM.maxDirectMemory(); - private static volatile long reservedMemory; - private static volatile long totalCapacity; - private static volatile long count; - private static boolean memoryLimitSet = false; - - // These methods should be called whenever direct memory is allocated or - // freed. They allow the user to control the amount of direct memory - // which a process may access. All sizes are specified in bytes. - static void reserveMemory(long size, int cap) { - synchronized (Bits.class) { - if (!memoryLimitSet && VM.isBooted()) { - maxMemory = VM.maxDirectMemory(); - memoryLimitSet = true; - } - // -XX:MaxDirectMemorySize limits the total capacity rather than the - // actual memory usage, which will differ when buffers are page - // aligned. - if (cap <= maxMemory - totalCapacity) { - reservedMemory += size; - totalCapacity += cap; - count++; - return; - } - } - - System.gc(); - try { - Thread.sleep(100); - } catch (InterruptedException x) { - // Restore interrupt status - Thread.currentThread().interrupt(); - } - synchronized (Bits.class) { - if (totalCapacity + cap > maxMemory) - throw new OutOfMemoryError("Direct buffer memory"); - reservedMemory += size; - totalCapacity += cap; - count++; - } - - } - - static synchronized void unreserveMemory(long size, int cap) { - if (reservedMemory > 0) { - reservedMemory -= size; - totalCapacity -= cap; - count--; - assert (reservedMemory > -1); - } - } - - // -- Monitoring of direct buffer usage -- - - static { - // setup access to this package in SharedSecrets - sun.misc.SharedSecrets.setJavaNioAccess( - new sun.misc.JavaNioAccess() { - @Override - public sun.misc.JavaNioAccess.BufferPool getDirectBufferPool() { - return new sun.misc.JavaNioAccess.BufferPool() { - @Override - public String getName() { - return "direct"; - } - @Override - public long getCount() { - return Bits.count; - } - @Override - public long getTotalCapacity() { - return Bits.totalCapacity; - } - @Override - public long getMemoryUsed() { - return Bits.reservedMemory; - } - }; - } - @Override - public ByteBuffer newDirectByteBuffer(long addr, int cap, Object ob) { - return new DirectByteBuffer(addr, cap, ob); - } - @Override - public void truncate(Buffer buf) { - buf.truncate(); - } - }); - } - - // -- Bulk get/put acceleration -- - - // These numbers represent the point at which we have empirically - // determined that the average cost of a JNI call exceeds the expense - // of an element by element copy. These numbers may change over time. - static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; - static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; - - // This number limits the number of bytes to copy per call to Unsafe's - // copyMemory method. A limit is imposed to allow for safepoint polling - // during a large copy - static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; - - // These methods do no bounds checking. Verification that the copy will not - // result in memory corruption should be done prior to invocation. - // All positions and lengths are specified in bytes. - - /** - * Copy from given source array to destination address. - * - * @param src - * source array - * @param srcBaseOffset - * offset of first element of storage in source array - * @param srcPos - * offset within source array of the first element to read - * @param dstAddr - * destination address - * @param length - * number of bytes to copy - */ - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void copyFromArray(Object src, long srcBaseOffset, long srcPos, - long dstAddr, long length) - { - long offset = srcBaseOffset + srcPos; - while (length > 0) { - long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length; - unsafe.copyMemory(src, offset, null, dstAddr, size); - length -= size; - offset += size; - dstAddr += size; - } - } - - /** - * Copy from source address into given destination array. - * - * @param srcAddr - * source address - * @param dst - * destination array - * @param dstBaseOffset - * offset of first element of storage in destination array - * @param dstPos - * offset within destination array of the first element to write - * @param length - * number of bytes to copy - */ - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void copyToArray(long srcAddr, Object dst, long dstBaseOffset, long dstPos, - long length) - { - long offset = dstBaseOffset + dstPos; - while (length > 0) { - long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length; - unsafe.copyMemory(null, srcAddr, dst, offset, size); - length -= size; - srcAddr += size; - offset += size; - } - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void copyFromCharArray(Object src, long srcPos, long dstAddr, - long length) - { - copyFromShortArray(src, srcPos, dstAddr, length); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static void copyToCharArray(long srcAddr, Object dst, long dstPos, - long length) - { - copyToShortArray(srcAddr, dst, dstPos, length); - } - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyFromShortArray(Object src, long srcPos, long dstAddr, - long length); - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyToShortArray(long srcAddr, Object dst, long dstPos, - long length); - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyFromIntArray(Object src, long srcPos, long dstAddr, - long length); - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyToIntArray(long srcAddr, Object dst, long dstPos, - long length); - - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyFromLongArray(Object src, long srcPos, long dstAddr, - long length); - @cli.System.Security.SecurityCriticalAttribute.Annotation - static native void copyToLongArray(long srcAddr, Object dst, long dstPos, - long length); - -} diff --git a/src/IKVM.Java/local/java/security/ProtectionDomain.java b/src/IKVM.Java/local/java/security/ProtectionDomain.java index 7e05249fdc..920ac3d633 100644 --- a/src/IKVM.Java/local/java/security/ProtectionDomain.java +++ b/src/IKVM.Java/local/java/security/ProtectionDomain.java @@ -59,35 +59,44 @@ */ public class ProtectionDomain { + private static class JavaSecurityAccessImpl implements JavaSecurityAccess { - static { - // Set up JavaSecurityAccess in SharedSecrets - SharedSecrets.setJavaSecurityAccess( - new JavaSecurityAccess() { - public T doIntersectionPrivilege( - PrivilegedAction action, - final AccessControlContext stack, - final AccessControlContext context) - { - if (action == null) { - throw new NullPointerException(); - } - return AccessController.doPrivileged( - action, - new AccessControlContext( - stack.getContext(), context).optimize() - ); - } + private JavaSecurityAccessImpl() { + } - public T doIntersectionPrivilege( - PrivilegedAction action, - AccessControlContext context) - { - return doIntersectionPrivilege(action, - AccessController.getContext(), context); - } + @Override + public T doIntersectionPrivilege( + PrivilegedAction action, + final AccessControlContext stack, + final AccessControlContext context) { + if (action == null) { + throw new NullPointerException(); } - ); + + return AccessController.doPrivileged( + action, + getCombinedACC(context, stack) + ); + } + + @Override + public T doIntersectionPrivilege( + PrivilegedAction action, + AccessControlContext context) { + return doIntersectionPrivilege(action, + AccessController.getContext(), context); + } + + private static AccessControlContext getCombinedACC(AccessControlContext context, AccessControlContext stack) { + AccessControlContext acc = new AccessControlContext(context, stack.getCombiner(), true); + + return new AccessControlContext(stack.getContext(), acc).optimize(); + } + } + + static { + // Set up JavaSecurityAccess in SharedSecrets + SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); } /* CodeSource */ @@ -117,6 +126,8 @@ or dynamic (via a policy refresh) */ */ final Key key = new Key(); + private static final Debug debug = Debug.getInstance("domain"); + /* [IKVM] constructor for use by the runtime (AssemblyClassLoader) */ ProtectionDomain(cli.System.Reflection.Assembly assembly) { this.assembly = assembly; @@ -395,7 +406,6 @@ private static boolean seeAllp() { if (sm == null) { return true; } else { - Debug debug = Debug.getInstance("domain"); if (debug != null) { if (sm.getClass().getClassLoader() == null && Policy.getPolicyNoCheck().getClass().getClassLoader() diff --git a/src/IKVM.Java/local/java/util/zip/ZipEntry.java b/src/IKVM.Java/local/java/util/zip/ZipEntry.java index 56760975bc..fdef61f262 100644 --- a/src/IKVM.Java/local/java/util/zip/ZipEntry.java +++ b/src/IKVM.Java/local/java/util/zip/ZipEntry.java @@ -41,7 +41,9 @@ class ZipEntry implements ZipConstants, Cloneable { String name; // entry name - long time = -1; // last modification time + long xdostime = -1; // last modification time (in extended DOS time, + // where milliseconds lost in conversion might + // be encoded into the upper half) FileTime mtime; // last modification time, from extra field data FileTime atime; // last access time, from extra field data FileTime ctime; // creation time, from extra field data @@ -53,7 +55,6 @@ class ZipEntry implements ZipConstants, Cloneable { byte[] extra; // optional extra field data for entry String comment; // optional comment string for entry long offset; // [IKVM] used by ZipFile - volatile long dostime = -1; // [IKVM] undecoded DOS time (to avoid bootstrap issue, because java.util.Date needs to read from VFS zip file) /** * Compression method for uncompressed entries. @@ -65,6 +66,28 @@ class ZipEntry implements ZipConstants, Cloneable { */ public static final int DEFLATED = 8; + /** + * DOS time constant for representing timestamps before 1980. + */ + static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16); + + /** + * Approximately 128 years, in milliseconds (ignoring leap years etc). + * + * This establish an approximate high-bound value for DOS times in + * milliseconds since epoch, used to enable an efficient but + * sufficient bounds check to avoid generating extended last modified + * time entries. + * + * Calculating the exact number is locale dependent, would require loading + * TimeZone data eagerly, and would make little practical sense. Since DOS + * times theoretically go to 2107 - with compatibility not guaranteed + * after 2099 - setting this to a time that is before but near 2099 + * should be sufficient. + */ + private static final long UPPER_DOSTIME_BOUND = + 128L * 365 * 24 * 60 * 60 * 1000; + /** * Creates a new zip entry with the specified name. * @@ -95,7 +118,7 @@ public ZipEntry(String name) { public ZipEntry(ZipEntry e) { Objects.requireNonNull(e, "entry"); name = e.name; - time = e.time; + xdostime = e.xdostime; mtime = e.mtime; atime = e.atime; ctime = e.ctime; @@ -106,7 +129,6 @@ public ZipEntry(ZipEntry e) { flag = e.flag; extra = e.extra; comment = e.comment; - dostime = e.dostime; } /** @@ -140,9 +162,14 @@ public String getName() { * @see #getLastModifiedTime() */ public void setTime(long time) { - this.time = time; - this.dostime = -1; - this.mtime = null; + this.xdostime = javaToExtendedDosTime(time); + // Avoid setting the mtime field if time is in the valid + // range for a DOS time + if (xdostime != DOSTIME_BEFORE_1980 && time <= UPPER_DOSTIME_BOUND) { + this.mtime = null; + } else { + this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS); + } } /** @@ -162,16 +189,10 @@ public void setTime(long time) { * @see #setLastModifiedTime(FileTime) */ public long getTime() { - return getTimeImpl(); - } - - // [IKVM] we store the DOS time to avoid a bootstrap issue - private long getTimeImpl() { - long tmp = dostime; - if (tmp != -1) { - return ZipUtils.dosToJavaTime(tmp); + if (mtime != null) { + return mtime.toMillis(); } - return time; + return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1; } /** @@ -193,10 +214,9 @@ private long getTimeImpl() { * @since 1.8 */ public ZipEntry setLastModifiedTime(FileTime time) { - Objects.requireNonNull(name, "time"); - this.mtime = time; - this.time = time.to(TimeUnit.MILLISECONDS); - this.dostime = -1; + this.mtime = Objects.requireNonNull(time, "lastModifiedTime"); + this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS)); + return this; } @@ -219,10 +239,9 @@ public ZipEntry setLastModifiedTime(FileTime time) { public FileTime getLastModifiedTime() { if (mtime != null) return mtime; - long time = getTimeImpl(); - if (time == -1) + if (xdostime == -1) return null; - return FileTime.from(time, TimeUnit.MILLISECONDS); + return FileTime.from(getTime(), TimeUnit.MILLISECONDS); } /** @@ -242,8 +261,7 @@ public FileTime getLastModifiedTime() { * @since 1.8 */ public ZipEntry setLastAccessTime(FileTime time) { - Objects.requireNonNull(name, "time"); - this.atime = time; + this.atime = Objects.requireNonNull(time, "lastAccessTime"); return this; } @@ -280,8 +298,7 @@ public FileTime getLastAccessTime() { * @since 1.8 */ public ZipEntry setCreationTime(FileTime time) { - Objects.requireNonNull(name, "time"); - this.ctime = time; + this.ctime = Objects.requireNonNull(time, "creationTime"); return this; } @@ -466,6 +483,8 @@ void setExtra0(byte[] extra, boolean doZIP64) { } break; case EXTID_NTFS: + if (sz < 32) // reserved 4 bytes + tag 2 bytes + size 2 bytes + break; // m[a|c]time 24 bytes int pos = off + 4; // reserved 4 bytes if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24) break; diff --git a/src/IKVM.Java/local/java/util/zip/ZipFile.java b/src/IKVM.Java/local/java/util/zip/ZipFile.java index 6aa5c4423d..48097b2d3d 100644 --- a/src/IKVM.Java/local/java/util/zip/ZipFile.java +++ b/src/IKVM.Java/local/java/util/zip/ZipFile.java @@ -290,7 +290,7 @@ private void readEntries() throws IOException ZipEntry entry = new ZipEntry(); entry.flag = flags; entry.method = method; - entry.dostime = inp.readLeUnsignedInt(); + entry.xdostime = inp.readLeUnsignedInt(); entry.crc = inp.readLeUnsignedInt(); entry.csize = inp.readLeUnsignedInt(); entry.size = inp.readLeUnsignedInt(); diff --git a/src/IKVM.Java/local/sun/management/ManagementFactoryHelper.java b/src/IKVM.Java/local/sun/management/ManagementFactoryHelper.java index b5fd8d7831..68f9724611 100644 --- a/src/IKVM.Java/local/sun/management/ManagementFactoryHelper.java +++ b/src/IKVM.Java/local/sun/management/ManagementFactoryHelper.java @@ -293,9 +293,33 @@ public static HashMap getPlatformDynamicMBeans() { } static void registerInternalMBeans(MBeanServer mbs) { + + } + + private static void unregisterMBean(MBeanServer mbs, String mbeanName) { + try { + final ObjectName objName = Util.newObjectName(mbeanName); + + // inner class requires these fields to be final + final MBeanServer mbs0 = mbs; + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Void run() throws MBeanRegistrationException, + RuntimeOperationsException { + try { + mbs0.unregisterMBean(objName); + } catch (InstanceNotFoundException e) { + // ignore exception if not found + } + return null; + } + }); + } catch (PrivilegedActionException e) { + throw Util.newException(e.getException()); + } } static void unregisterInternalMBeans(MBeanServer mbs) { + } static { diff --git a/src/IKVM.Java/local/sun/misc/FileURLMapper.java b/src/IKVM.Java/local/sun/misc/FileURLMapper.java index 3fd43e4f7a..94caff650e 100644 --- a/src/IKVM.Java/local/sun/misc/FileURLMapper.java +++ b/src/IKVM.Java/local/sun/misc/FileURLMapper.java @@ -1,72 +1,35 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/*IKVM*/ -/* - * Modified for IKVM by Jeroen Frijters on May 22, 2007. - * * This is a merged version of the Windows & Solaris platform specific versions. * Since the IKVM class library binary can be used both on Windows and on *nix, * I've merged the platform specific classes into a generic class that at * runtime determines if it runs on Windows or not. - * -/*IKVM*/ + */ package sun.misc; import java.net.URL; import java.io.File; -import sun.net.www.ParseUtil; -/** - * Platform specific handling for file: URLs . In particular deals - * with network paths mapping them to UNCs. - * - * @author Michael McMahon - * @version 1.10, 07/05/05 - */ +import sun.net.www.ParseUtil; public class FileURLMapper { - private static final boolean runningOnWindows = cli.System.Environment.get_OSVersion().ToString().indexOf("Unix") == -1; + URL url; String file; - public FileURLMapper (URL url) { + public FileURLMapper(URL url) { this.url = url; } /** - * @returns the platform specific path corresponding to the URL, and in particular - * returns a UNC when the authority contains a hostname + * @returns the platform specific path corresponding to the URL, and in particular returns a UNC when the authority contains a hostname */ - public String getPath () { if (file != null) { return file; } - if (runningOnWindows) { + + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { String host = url.getHost(); if (host != null && !host.equals("") && !"localhost".equalsIgnoreCase(host)) { @@ -87,13 +50,15 @@ public String getPath () { return file; } } - + public boolean exists() { - String path = getPath(); - if (path == null) { + String s = getPath(); + if (s == null) { return false; + } else { + File f = new File(s); + return f.exists(); } - File f = new File (path); - return f.exists(); } + } diff --git a/src/IKVM.Java/classpath/sun/misc/Ref.java b/src/IKVM.Java/local/sun/misc/Ref.java similarity index 100% rename from src/IKVM.Java/classpath/sun/misc/Ref.java rename to src/IKVM.Java/local/sun/misc/Ref.java diff --git a/src/IKVM.Java/local/sun/misc/SharedSecrets.java b/src/IKVM.Java/local/sun/misc/SharedSecrets.java index f680fe4cb0..03f4a72a49 100644 --- a/src/IKVM.Java/local/sun/misc/SharedSecrets.java +++ b/src/IKVM.Java/local/sun/misc/SharedSecrets.java @@ -27,6 +27,7 @@ import java.util.jar.JarFile; import java.io.Console; +import java.io.FileDescriptor; import java.security.ProtectionDomain; import java.security.AccessController; @@ -43,11 +44,12 @@ interface and provides the ability to call package-private methods public class SharedSecrets { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static JavaUtilJarAccess javaUtilJarAccess; - private static JavaLangAccess javaLangAccess = LangHelper.getJavaLangAccess(); + private static JavaLangAccess javaLangAccess; private static JavaIOAccess javaIOAccess; private static JavaNetAccess javaNetAccess; private static JavaNetHttpCookieAccess javaNetHttpCookieAccess; private static JavaNioAccess javaNioAccess; + private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess; private static JavaSecurityAccess javaSecurityAccess; private static JavaUtilZipFileAccess javaUtilZipFileAccess; @@ -66,6 +68,10 @@ public static void setJavaUtilJarAccess(JavaUtilJarAccess access) { javaUtilJarAccess = access; } + public static void setJavaLangAccess(JavaLangAccess jla) { + javaLangAccess = jla; + } + public static JavaLangAccess getJavaLangAccess() { return javaLangAccess; } @@ -94,7 +100,10 @@ public static void setJavaNioAccess(JavaNioAccess jna) { public static JavaNioAccess getJavaNioAccess() { if (javaNioAccess == null) { - // [IKVM] OpenJDK initializes java.nio.ByteOrder here, but that doesn't work + // Ensure java.nio.ByteOrder is initialized; we know that + // this class initializes java.nio.Bits that provides the + // shared secret. + unsafe.ensureClassInitialized(java.nio.ByteOrder.class); java.nio.ByteOrder.nativeOrder(); } return javaNioAccess; @@ -111,6 +120,17 @@ public static JavaIOAccess getJavaIOAccess() { return javaIOAccess; } + public static void setJavaIOFileDescriptorAccess(JavaIOFileDescriptorAccess jiofda) { + javaIOFileDescriptorAccess = jiofda; + } + + public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() { + if (javaIOFileDescriptorAccess == null) + unsafe.ensureClassInitialized(FileDescriptor.class); + + return javaIOFileDescriptorAccess; + } + public static void setJavaSecurityProtectionDomainAccess (JavaSecurityProtectionDomainAccess jspda) { javaSecurityProtectionDomainAccess = jspda; @@ -129,8 +149,7 @@ public static void setJavaSecurityAccess(JavaSecurityAccess jsa) { public static JavaSecurityAccess getJavaSecurityAccess() { if (javaSecurityAccess == null) { - // [IKVM] OpenJDK initializes AccessController here, but that's a bug - unsafe.ensureClassInitialized(ProtectionDomain.class); + unsafe.ensureClassInitialized(AccessController.class); } return javaSecurityAccess; } diff --git a/src/IKVM.Java/local/sun/misc/Unsafe.java b/src/IKVM.Java/local/sun/misc/Unsafe.java deleted file mode 100644 index a228f432f9..0000000000 --- a/src/IKVM.Java/local/sun/misc/Unsafe.java +++ /dev/null @@ -1,1300 +0,0 @@ -/* - Copyright (C) 2006-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -package sun.misc; - -import cli.System.Buffer; -import cli.System.IntPtr; -import cli.System.Runtime.InteropServices.Marshal; -import cli.System.Security.Permissions.SecurityAction; -import cli.System.Security.Permissions.SecurityPermissionAttribute; -import ikvm.lang.Internal; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.security.ProtectionDomain; -import java.util.ArrayList; - -public final class Unsafe -{ - public static final int INVALID_FIELD_OFFSET = -1; - public static final int ARRAY_BYTE_BASE_OFFSET = 0; - // NOTE sun.corba.Bridge actually access this field directly (via reflection), - // so the name must match the JDK name. - private static final Unsafe theUnsafe = new Unsafe(); - private static final ArrayList fields = new ArrayList(); - - private Unsafe() { } - - @sun.reflect.CallerSensitive - public static Unsafe getUnsafe() - { - if(!VM.isSystemDomainLoader(ikvm.internal.CallerID.getCallerID().getCallerClassLoader())) - { - throw new SecurityException("Unsafe"); - } - return theUnsafe; - } - - private static native Field createFieldAndMakeAccessible(Class c, String field); - private static native Field copyFieldAndMakeAccessible(Field field); - - // this is the intrinsified version of objectFieldOffset(XXX.class.getDeclaredField("xxx")) - public long objectFieldOffset(Class c, String field) - { - return allocateUnsafeFieldId(createFieldAndMakeAccessible(c, field)); - } - - // NOTE we have a really lame (and slow) implementation! - public long objectFieldOffset(Field field) - { - if(Modifier.isStatic(field.getModifiers())) - { - throw new IllegalArgumentException(); - } - return allocateUnsafeFieldId(field); - } - - public long staticFieldOffset(Field field) - { - if(!Modifier.isStatic(field.getModifiers())) - { - throw new IllegalArgumentException(); - } - return allocateUnsafeFieldId(field); - } - - @Deprecated - public int fieldOffset(Field original) - { - return allocateUnsafeFieldId(original); - } - - static int allocateUnsafeFieldId(Field original) - { - Field copy = copyFieldAndMakeAccessible(original); - synchronized(fields) - { - int id = fields.size(); - fields.add(copy); - return id; - } - } - - public int arrayBaseOffset(Class c) - { - // don't change this, the Unsafe intrinsics depend on this value - return 0; - } - - public int arrayIndexScale(Class c) - { - if (c == byte[].class || c == boolean[].class) - { - return 1; - } - if (c == char[].class || c == short[].class) - { - return 2; - } - if (c == int[].class || c == float[].class || c == Object[].class) - { - return 4; - } - if (c == long[].class || c == double[].class) - { - return 8; - } - // don't change this, the Unsafe intrinsics depend on this value - return 1; - } - - static Field getField(long offset) - { - synchronized(fields) - { - return fields.get((int)offset); - } - } - - public final native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update); - - public void putObjectVolatile(Object obj, long offset, Object newValue) - { - if(obj instanceof Object[]) - { - synchronized(this) - { - ((Object[])obj)[(int)offset] = newValue; - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - field.set(obj, newValue); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - public void putOrderedObject(Object obj, long offset, Object newValue) - { - putObjectVolatile(obj, offset, newValue); - } - - public Object getObjectVolatile(Object obj, long offset) - { - if(obj instanceof Object[]) - { - synchronized(this) - { - return ((Object[])obj)[(int)offset]; - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - return field.get(obj); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - private static native short ReadInt16(Object obj, long offset); - private static native int ReadInt32(Object obj, long offset); - private static native long ReadInt64(Object obj, long offset); - private static native void WriteInt16(Object obj, long offset, short value); - private static native void WriteInt32(Object obj, long offset, int value); - private static native void WriteInt64(Object obj, long offset, long value); - - public final native boolean compareAndSwapInt(Object obj, long offset, int expect, int update); - - public void putIntVolatile(Object obj, long offset, int newValue) - { - if (obj instanceof cli.System.Array) - { - synchronized(this) - { - WriteInt32(obj, offset, newValue); - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - field.setInt(obj, newValue); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - public void putOrderedInt(Object obj, long offset, int newValue) - { - putIntVolatile(obj, offset, newValue); - } - - public int getIntVolatile(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - synchronized(this) - { - return ReadInt32(obj, offset); - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - return field.getInt(obj); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - public final native boolean compareAndSwapLong(Object obj, long offset, long expect, long update); - - public void putLongVolatile(Object obj, long offset, long newValue) - { - if (obj instanceof cli.System.Array) - { - synchronized(this) - { - WriteInt64(obj, offset, newValue); - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - field.setLong(obj, newValue); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - public void putOrderedLong(Object obj, long offset, long newValue) - { - putLongVolatile(obj, offset, newValue); - } - - public long getLongVolatile(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - synchronized(this) - { - return ReadInt64(obj, offset); - } - } - else - { - Field field = getField(offset); - synchronized(field) - { - try - { - return field.getLong(obj); - } - catch(IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - } - - public void putBoolean(Object obj, long offset, boolean newValue) - { - if (obj instanceof cli.System.Array) - { - Buffer.SetByte((cli.System.Array)obj, (int)offset, newValue ? (byte)1 : (byte)0); - } - else - { - try - { - getField(offset).setBoolean(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putBooleanVolatile(Object obj, long offset, boolean newValue) - { - putBoolean(obj, offset, newValue); - } - - public boolean getBoolean(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return Buffer.GetByte((cli.System.Array)obj, (int)offset) != 0; - } - else - { - try - { - return getField(offset).getBoolean(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public boolean getBooleanVolatile(Object obj, long offset) - { - return getBoolean(obj, offset); - } - - public void putByte(Object obj, long offset, byte newValue) - { - if (obj instanceof cli.System.Array) - { - Buffer.SetByte((cli.System.Array)obj, (int)offset, newValue); - } - else - { - try - { - getField(offset).setByte(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putByteVolatile(Object obj, long offset, byte newValue) - { - putByte(obj, offset, newValue); - } - - public byte getByte(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return Buffer.GetByte((cli.System.Array)obj, (int)offset); - } - else - { - try - { - return getField(offset).getByte(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public byte getByteVolatile(Object obj, long offset) - { - return getByte(obj, offset); - } - - public void putChar(Object obj, long offset, char newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt16(obj, offset, (short)newValue); - } - else - { - try - { - getField(offset).setChar(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putCharVolatile(Object obj, long offset, char newValue) - { - putChar(obj, offset, newValue); - } - - public char getChar(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return (char)ReadInt16(obj, offset); - } - else - { - try - { - return getField(offset).getChar(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public char getCharVolatile(Object obj, long offset) - { - return getChar(obj, offset); - } - - public void putShort(Object obj, long offset, short newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt16(obj, offset, newValue); - } - else - { - try - { - getField(offset).setShort(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putShortVolatile(Object obj, long offset, short newValue) - { - putShort(obj, offset, newValue); - } - - public short getShort(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return ReadInt16(obj, offset); - } - else - { - try - { - return getField(offset).getShort(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public short getShortVolatile(Object obj, long offset) - { - return getShort(obj, offset); - } - - public void putInt(Object obj, long offset, int newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt32(obj, offset, newValue); - } - else - { - try - { - getField(offset).setInt(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public int getInt(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return ReadInt32(obj, offset); - } - else - { - try - { - return getField(offset).getInt(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putFloat(Object obj, long offset, float newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt32(obj, offset, Float.floatToRawIntBits(newValue)); - } - else - { - try - { - getField(offset).setFloat(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putFloatVolatile(Object obj, long offset, float newValue) - { - putFloat(obj, offset, newValue); - } - - public float getFloat(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return Float.intBitsToFloat(ReadInt32(obj, offset)); - } - else - { - try - { - return getField(offset).getFloat(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public float getFloatVolatile(Object obj, long offset) - { - return getFloat(obj, offset); - } - - public void putLong(Object obj, long offset, long newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt64(obj, offset, newValue); - } - else - { - try - { - getField(offset).setLong(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public long getLong(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return ReadInt64(obj, offset); - } - else - { - try - { - return getField(offset).getLong(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putDouble(Object obj, long offset, double newValue) - { - if (obj instanceof cli.System.Array) - { - WriteInt64(obj, offset, Double.doubleToRawLongBits(newValue)); - } - else - { - try - { - getField(offset).setDouble(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public void putDoubleVolatile(Object obj, long offset, double newValue) - { - synchronized (this) - { - putDouble(obj, offset, newValue); - } - } - - public double getDouble(Object obj, long offset) - { - if (obj instanceof cli.System.Array) - { - return Double.longBitsToDouble(ReadInt64(obj, offset)); - } - else - { - try - { - return getField(offset).getDouble(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public double getDoubleVolatile(Object obj, long offset) - { - synchronized (this) - { - return getDouble(obj, offset); - } - } - - public void putObject(Object obj, long offset, Object newValue) - { - if (obj instanceof Object[]) - { - ((Object[])obj)[(int)offset] = newValue; - } - else - { - try - { - getField(offset).set(obj, newValue); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - public Object getObject(Object obj, long offset) - { - if (obj instanceof Object[]) - { - return ((Object[])obj)[(int)offset]; - } - else - { - try - { - return getField(offset).get(obj); - } - catch (IllegalAccessException x) - { - throw (InternalError)new InternalError().initCause(x); - } - } - } - - @Deprecated - public int getInt(Object o, int offset) - { - return getInt(o, (long)offset); - } - - @Deprecated - public void putInt(Object o, int offset, int x) - { - putInt(o, (long)offset, x); - } - - @Deprecated - public Object getObject(Object o, int offset) - { - return getObject(o, (long)offset); - } - - @Deprecated - public void putObject(Object o, int offset, Object x) - { - putObject(o, (long)offset, x); - } - - @Deprecated - public boolean getBoolean(Object o, int offset) - { - return getBoolean(o, (long)offset); - } - - @Deprecated - public void putBoolean(Object o, int offset, boolean x) - { - putBoolean(o, (long)offset, x); - } - - @Deprecated - public byte getByte(Object o, int offset) - { - return getByte(o, (long)offset); - } - - @Deprecated - public void putByte(Object o, int offset, byte x) - { - putByte(o, (long)offset, x); - } - - @Deprecated - public short getShort(Object o, int offset) - { - return getShort(o, (long)offset); - } - - @Deprecated - public void putShort(Object o, int offset, short x) - { - putShort(o, (long)offset, x); - } - - @Deprecated - public char getChar(Object o, int offset) - { - return getChar(o, (long)offset); - } - - @Deprecated - public void putChar(Object o, int offset, char x) - { - putChar(o, (long)offset, x); - } - - @Deprecated - public long getLong(Object o, int offset) - { - return getLong(o, (long)offset); - } - - @Deprecated - public void putLong(Object o, int offset, long x) - { - putLong(o, (long)offset, x); - } - - @Deprecated - public float getFloat(Object o, int offset) - { - return getFloat(o, (long)offset); - } - - @Deprecated - public void putFloat(Object o, int offset, float x) - { - putFloat(o, (long)offset, x); - } - - @Deprecated - public double getDouble(Object o, int offset) - { - return getDouble(o, (long)offset); - } - - @Deprecated - public void putDouble(Object o, int offset, double x) - { - putDouble(o, (long)offset, x); - } - - public native void throwException(Throwable t); - - public native void ensureClassInitialized(Class clazz); - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, SerializationFormatter = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public native Object allocateInstance(Class clazz) throws InstantiationException; - - public int addressSize() - { - return IntPtr.get_Size(); - } - - public int pageSize() - { - return 4096; - } - - // The really unsafe methods start here. They are all have a LinkDemand for unmanaged code. - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public long allocateMemory(long bytes) - { - if (bytes == 0) - { - return 0; - } - try - { - if (false) throw new cli.System.OutOfMemoryException(); - return Marshal.AllocHGlobal(IntPtr.op_Explicit(bytes)).ToInt64(); - } - catch (cli.System.OutOfMemoryException x) - { - throw new OutOfMemoryError(x.get_Message()); - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public long reallocateMemory(long address, long bytes) - { - if (bytes == 0) - { - freeMemory(address); - return 0; - } - try - { - if (false) throw new cli.System.OutOfMemoryException(); - return Marshal.ReAllocHGlobal(IntPtr.op_Explicit(address), IntPtr.op_Explicit(bytes)).ToInt64(); - } - catch (cli.System.OutOfMemoryException x) - { - throw new OutOfMemoryError(x.get_Message()); - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void freeMemory(long address) - { - Marshal.FreeHGlobal(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void setMemory(long address, long bytes, byte value) - { - while (bytes-- > 0) - { - putByte(address++, value); - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void setMemory(Object o, long offset, long bytes, byte value) - { - if (o == null) - { - setMemory(offset, bytes, value); - } - else if (o instanceof byte[]) - { - byte[] array = (byte[])o; - for (int i = 0; i < bytes; i++) - { - array[(int)(offset + i)] = value; - } - } - else if (o instanceof cli.System.Array) - { - cli.System.Array array = (cli.System.Array)o; - for (int i = 0; i < bytes; i++) - { - cli.System.Buffer.SetByte(array, (int)(offset + i), value); - } - } - else - { - throw new IllegalArgumentException(); - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void copyMemory(long srcAddress, long destAddress, long bytes) - { - while (bytes-- > 0) - { - putByte(destAddress++, getByte(srcAddress++)); - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) - { - if (srcBase == null) - { - if (destBase instanceof byte[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (byte[])destBase, (int)destOffset, (int)bytes); - } - else if (destBase instanceof boolean[]) - { - byte[] tmp = new byte[(int)bytes]; - copyMemory(srcBase, srcOffset, tmp, 0, bytes); - copyMemory(tmp, 0, destBase, destOffset, bytes); - } - else if (destBase instanceof short[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (short[])destBase, (int)(destOffset >> 1), (int)(bytes >> 1)); - } - else if (destBase instanceof char[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (char[])destBase, (int)(destOffset >> 1), (int)(bytes >> 1)); - } - else if (destBase instanceof int[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (int[])destBase, (int)(destOffset >> 2), (int)(bytes >> 2)); - } - else if (destBase instanceof float[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (float[])destBase, (int)(destOffset >> 2), (int)(bytes >> 2)); - } - else if (destBase instanceof long[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (long[])destBase, (int)(destOffset >> 3), (int)(bytes >> 3)); - } - else if (destBase instanceof double[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy(IntPtr.op_Explicit(srcOffset), (double[])destBase, (int)(destOffset >> 3), (int)(bytes >> 3)); - } - else if (destBase == null) - { - copyMemory(srcOffset, destOffset, bytes); - } - else - { - throw new IllegalArgumentException(); - } - } - else if (srcBase instanceof cli.System.Array && destBase instanceof cli.System.Array) - { - cli.System.Buffer.BlockCopy((cli.System.Array)srcBase, (int)srcOffset, (cli.System.Array)destBase, (int)destOffset, (int)bytes); - } - else - { - if (srcBase instanceof byte[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((byte[])srcBase, (int)srcOffset, IntPtr.op_Explicit(destOffset), (int)bytes); - } - else if (srcBase instanceof boolean[]) - { - byte[] tmp = new byte[(int)bytes]; - copyMemory(srcBase, srcOffset, tmp, 0, bytes); - copyMemory(tmp, 0, destBase, destOffset, bytes); - } - else if (srcBase instanceof short[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((short[])srcBase, (int)(srcOffset >> 1), IntPtr.op_Explicit(destOffset), (int)(bytes >> 1)); - } - else if (srcBase instanceof char[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((char[])srcBase, (int)(srcOffset >> 1), IntPtr.op_Explicit(destOffset), (int)(bytes >> 1)); - } - else if (srcBase instanceof int[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((int[])srcBase, (int)(srcOffset >> 2), IntPtr.op_Explicit(destOffset), (int)(bytes >> 2)); - } - else if (srcBase instanceof float[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((float[])srcBase, (int)(srcOffset >> 2), IntPtr.op_Explicit(destOffset), (int)(bytes >> 2)); - } - else if (srcBase instanceof long[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((long[])srcBase, (int)(srcOffset >> 3), IntPtr.op_Explicit(destOffset), (int)(bytes >> 3)); - } - else if (srcBase instanceof double[]) - { - cli.System.Runtime.InteropServices.Marshal.Copy((double[])srcBase, (int)(srcOffset >> 3), IntPtr.op_Explicit(destOffset), (int)(bytes >> 3)); - } - else - { - throw new IllegalArgumentException(); - } - } - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public byte getByte(long address) - { - return cli.System.Runtime.InteropServices.Marshal.ReadByte(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putByte(long address, byte x) - { - cli.System.Runtime.InteropServices.Marshal.WriteByte(IntPtr.op_Explicit(address), x); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public short getShort(long address) - { - return cli.System.Runtime.InteropServices.Marshal.ReadInt16(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putShort(long address, short x) - { - cli.System.Runtime.InteropServices.Marshal.WriteInt16(IntPtr.op_Explicit(address), x); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public char getChar(long address) - { - return (char)cli.System.Runtime.InteropServices.Marshal.ReadInt16(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putChar(long address, char x) - { - cli.System.Runtime.InteropServices.Marshal.WriteInt16(IntPtr.op_Explicit(address), (short)x); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public int getInt(long address) - { - return cli.System.Runtime.InteropServices.Marshal.ReadInt32(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putInt(long address, int x) - { - cli.System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.op_Explicit(address), x); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public long getLong(long address) - { - return cli.System.Runtime.InteropServices.Marshal.ReadInt64(IntPtr.op_Explicit(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putLong(long address, long x) - { - cli.System.Runtime.InteropServices.Marshal.WriteInt64(IntPtr.op_Explicit(address), x); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public long getAddress(long address) - { - return cli.System.Runtime.InteropServices.Marshal.ReadIntPtr(IntPtr.op_Explicit(address)).ToInt64(); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putAddress(long address, long x) - { - cli.System.Runtime.InteropServices.Marshal.WriteIntPtr(IntPtr.op_Explicit(address), IntPtr.op_Explicit(x)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public float getFloat(long address) - { - return Float.intBitsToFloat(getInt(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putFloat(long address, float x) - { - putInt(address, Float.floatToIntBits(x)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public double getDouble(long address) - { - return Double.longBitsToDouble(getLong(address)); - } - - @SecurityPermissionAttribute.Annotation(value = SecurityAction.__Enum.LinkDemand, UnmanagedCode = true) - @cli.System.Security.SecurityCriticalAttribute.Annotation - public void putDouble(long address, double x) - { - putLong(address, Double.doubleToLongBits(x)); - } - - public int getLoadAverage(double[] loadavg, int nelems) - { - return -1; - } - - public void park(boolean isAbsolute, long time) - { - if (isAbsolute) - { - java.util.concurrent.locks.LockSupport.parkUntil(time); - } - else - { - if (time == 0) - { - time = Long.MAX_VALUE; - } - java.util.concurrent.locks.LockSupport.parkNanos(time); - } - } - - public void unpark(Object thread) - { - java.util.concurrent.locks.LockSupport.unpark((Thread)thread); - } - - public Object staticFieldBase(Field f) - { - return null; - } - - @Deprecated - public Object staticFieldBase(Class c) - { - return null; - } - - public native boolean shouldBeInitialized(Class c); - - public native Class defineClass(String name, byte[] buf, int offset, int length, ClassLoader cl, ProtectionDomain pd); - - @Deprecated - @sun.reflect.CallerSensitive - public native Class defineClass(String name, byte[] b, int off, int len); - - public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); - - public void monitorEnter(Object o) - { - cli.System.Threading.Monitor.Enter(o); - } - - public void monitorExit(Object o) - { - cli.System.Threading.Monitor.Exit(o); - } - - public boolean tryMonitorEnter(Object o) - { - return cli.System.Threading.Monitor.TryEnter(o); - } - - public final int getAndAddInt(Object o, long offset, int delta) - { - for (;;) - { - int value = getIntVolatile(o, offset); - if (compareAndSwapInt(o, offset, value, value + delta)) - { - return value; - } - } - } - - public final long getAndAddLong(Object o, long offset, long delta) - { - for (;;) - { - long value = getLongVolatile(o, offset); - if (compareAndSwapLong(o, offset, value, value + delta)) - { - return value; - } - } - } - - public final int getAndSetInt(Object o, long offset, int newValue) - { - for (;;) - { - int value = getIntVolatile(o, offset); - if (compareAndSwapInt(o, offset, value, newValue)) - { - return value; - } - } - } - - public final long getAndSetLong(Object o, long offset, long newValue) - { - for (;;) - { - long value = getLongVolatile(o, offset); - if (compareAndSwapLong(o, offset, value, newValue)) - { - return value; - } - } - } - - public final Object getAndSetObject(Object o, long offset, Object newValue) - { - for (;;) - { - Object value = getObjectVolatile(o, offset); - if (compareAndSwapObject(o, offset, value, newValue)) - { - return value; - } - } - } - - public void loadFence() - { - cli.System.Threading.Thread.MemoryBarrier(); - } - - public void storeFence() - { - cli.System.Threading.Thread.MemoryBarrier(); - } - - public void fullFence() - { - cli.System.Threading.Thread.MemoryBarrier(); - } -} diff --git a/src/IKVM.Java/local/sun/misc/VM.java b/src/IKVM.Java/local/sun/misc/VM.java index 2607d95f99..4ae2664b7c 100644 --- a/src/IKVM.Java/local/sun/misc/VM.java +++ b/src/IKVM.Java/local/sun/misc/VM.java @@ -150,21 +150,11 @@ public static void asChange_otherthread(int as_old, int as_new) { } private static volatile boolean booted = false; private static final Object lock = new Object(); - static { - // [IKVM] force System properties initialization ("booting") - System.lineSeparator(); - } - // Invoked by by System.initializeSystemClass just before returning. // Subsystems that are invoked during initialization can check this // property in order to avoid doing things that should wait until the // application class loader has been set up. // - // [IKVM] The above isn't applicable. We only use the booted flag - // for the system properties (this is required because the system properties - // use java.util.Hashtable (via java.util.Properties) and it relies on - // the booted flag to determine whether it is safe to query the system - // properties). public static void booted() { synchronized (lock) { booted = true; @@ -187,13 +177,22 @@ public static void awaitBooted() throws InterruptedException { } } + // A user-settable upper limit on the maximum amount of allocatable direct + // buffer memory. This value may be changed during VM initialization if + // "java" is launched with "-XX:MaxDirectMemorySize=". + // + // The initial value of this field is arbitrary; during JRE initialization + // it will be reset to the value specified on the command line, if any, + // otherwise to Runtime.getRuntime().maxMemory(). + // + private static long directMemory = 64 * 1024 * 1024; + // Returns the maximum amount of allocatable direct buffer memory. // The directMemory variable is initialized during system initialization // in the saveAndRemoveProperties method. // public static long maxDirectMemory() { - // we don't support -XX:MaxDirectMemorySize - return Long.MAX_VALUE; + return directMemory; } // User-controllable flag that determines if direct buffers should be page @@ -254,18 +253,16 @@ public static boolean isSystemDomainLoader(ClassLoader loader) { * */ public static String getSavedProperty(String key) { - if (Lazy.savedProps.isEmpty()) + if (savedProps.isEmpty()) throw new IllegalStateException("Should be non-empty if initialized"); - return Lazy.savedProps.getProperty(key); + return savedProps.getProperty(key); } // TODO: the Property Management needs to be refactored and // the appropriate prop keys need to be accessible to the // calling classes to avoid duplication of keys. - static final class Lazy { - static final Properties savedProps = new Properties(); - } + private static final Properties savedProps = new Properties(); // Save a private copy of the system properties and remove // the system properties that are not intended for public access. @@ -275,7 +272,7 @@ public static void saveAndRemoveProperties(Properties props) { if (booted) throw new IllegalStateException("System initialization has completed"); - Lazy.savedProps.putAll(props); + savedProps.putAll(props); // Set the maximum amount of direct memory. This value is controlled // by the vm option -XX:MaxDirectMemorySize=. @@ -283,7 +280,16 @@ public static void saveAndRemoveProperties(Properties props) { // from the system property sun.nio.MaxDirectMemorySize set by the VM. // The system property will be removed. String s = (String)props.remove("sun.nio.MaxDirectMemorySize"); - // [IKVM] we don't support the -XX:MaxDirectMemorySize= option. + if (s != null) { + if (s.equals("-1")) { + // -XX:MaxDirectMemorySize not given, take default + directMemory = Runtime.getRuntime().maxMemory(); + } else { + long l = Long.parseLong(s); + if (l > -1) + directMemory = l; + } + } // Check if direct buffers should be page aligned s = (String)props.remove("sun.nio.PageAlignDirectMemory"); @@ -316,6 +322,9 @@ public static void saveAndRemoveProperties(Properties props) { // set for the class libraries. // public static void initializeOSEnvironment() { + if (!booted) { + OSEnvironment.initialize(); + } } /* Current count of objects pending for finalization */ diff --git a/src/IKVM.Java/local/sun/net/www/protocol/file/Handler.java b/src/IKVM.Java/local/sun/net/www/protocol/file/Handler.java index 88688c38dd..3ea4412558 100644 --- a/src/IKVM.Java/local/sun/net/www/protocol/file/Handler.java +++ b/src/IKVM.Java/local/sun/net/www/protocol/file/Handler.java @@ -79,7 +79,7 @@ public synchronized URLConnection openConnection(URL url, Proxy p) String file = url.getFile(); String host = url.getHost(); - if (ikvm.internal.Util.WINDOWS) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { path = ParseUtil.decode(file); path = path.replace('/', '\\'); path = path.replace('|', ':'); @@ -96,7 +96,7 @@ public synchronized URLConnection openConnection(URL url, Proxy p) /* * attempt to treat this as a UNC path. See 4180841 */ - if (ikvm.internal.Util.WINDOWS) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { path = "\\\\" + host + path; File f = new File(path); if (f.exists()) { diff --git a/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/Handler.java b/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/Handler.java index 9e4d8436b7..3f1dde8eb9 100644 --- a/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/Handler.java +++ b/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/Handler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Jeroen Frijters + Copyright (C) 2002, 2003, 2004, 2005, 2006 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,7 +24,187 @@ package sun.net.www.protocol.ikvmres; -public class Handler extends gnu.java.net.protocol.ikvmres.Handler -{ +import java.net.*; +import java.io.*; + +import cli.System.Reflection.Assembly; + +public class Handler extends URLStreamHandler { + + private static final String RFC2396_DIGIT = "0123456789"; + private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz"; + private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String RFC2396_ALPHA = RFC2396_LOWALPHA + RFC2396_UPALPHA; + private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA; + private static final String RFC2396_MARK = "-_.!~*'()"; + private static final String RFC2396_UNRESERVED = RFC2396_ALPHANUM + RFC2396_MARK; + private static final String RFC2396_REG_NAME = RFC2396_UNRESERVED + "$,;:@&=+"; + private static final String RFC2396_PCHAR = RFC2396_UNRESERVED + ":@&=+$,"; + private static final String RFC2396_SEGMENT = RFC2396_PCHAR + ";"; + private static final String RFC2396_PATH_SEGMENTS = RFC2396_SEGMENT + "/"; + + static InputStream readResourceFromAssembly(String assembly, int port, String resource) throws cli.System.IO.FileNotFoundException, cli.System.BadImageFormatException, cli.System.Security.SecurityException, IOException { + if (assembly.equals("gen") && port != -1 && resource.endsWith(".class") && resource.indexOf('.') == resource.length() - 6) { + ClassLoader loader = GetGenericClassLoaderById(port); + try { + Class c = Class.forName(resource.substring(1, resource.length() - 6).replace('/', '.'), false, loader); + return new ByteArrayInputStream(GenerateStub(c)); + } catch (ClassNotFoundException _) { + + } catch (LinkageError _) { + + } + } + + return readResourceFromAssembly(LoadAssembly(assembly), resource); + } + + public static InputStream readResourceFromAssembly(Assembly asm, String resource) throws IOException { + try { + if(false) throw new cli.System.Security.SecurityException(); + if(false) throw new cli.System.IO.FileNotFoundException(); + if(false) throw new cli.System.IO.IOException(); + return new ikvm.io.InputStreamWrapper(ReadResourceFromAssemblyImpl(asm, resource)); + } catch (cli.System.Security.SecurityException x) { + throw (IOException)new IOException().initCause(x); + } catch (cli.System.IO.FileNotFoundException x) { + if (resource.endsWith(".class") && resource.indexOf('.') == resource.length() - 6) { + Class c = LoadClassFromAssembly(asm, resource.substring(1, resource.length() - 6).replace('/', '.')); + if (c != null) { + return new ByteArrayInputStream(GenerateStub(c)); + } + } + + throw (FileNotFoundException)new FileNotFoundException().initCause(x); + } catch (cli.System.IO.IOException x) { + throw (IOException)new IOException().initCause(x); + } + } + + private static native byte[] GenerateStub(Class c); + private static native cli.System.IO.Stream ReadResourceFromAssemblyImpl(Assembly asm, String resource); + private static native Class LoadClassFromAssembly(Assembly asm, String className); + private static native Assembly LoadAssembly(String name) throws cli.System.IO.FileNotFoundException, cli.System.BadImageFormatException, cli.System.Security.SecurityException; + private static native ClassLoader GetGenericClassLoaderById(int id); + + protected URLConnection openConnection(URL url) throws IOException { + return new IkvmresURLConnection(url); + } + + protected void parseURL(URL url, String url_string, int start, int end) { + try { + // NOTE originally I wanted to use java.net.URI to handling parsing and constructing of these things, + // but it turns out that URI uses regex and that depends on resource loading... + url_string = url_string.substring(start, end); + if (url_string.startsWith("//")) { + int slash = url_string.indexOf('/', 2); + if (slash == -1) { + throw new RuntimeException("ikvmres: URLs must contain path"); + } + String assembly = unquote(url_string.substring(2, slash)); + String file = unquote(url_string.substring(slash)); + setURL(url, "ikvmres", assembly, -1, file, null); + } else if (url_string.startsWith("/")) { + setURL(url, "ikvmres", url.getHost(), -1, url_string, null); + } else { + String[] baseparts = ((cli.System.String)(Object)url.getFile()).Split(new char[] { '/' }); + String[] relparts = ((cli.System.String)(Object)url_string).Split(new char[] { '/' }); + String[] target = new String[baseparts.length + relparts.length - 1]; + for (int i = 1; i < baseparts.length; i++) { + target[i - 1] = baseparts[i]; + } + int p = baseparts.length - 2; + for (int i = 0; i < relparts.length; i++) { + if (relparts[i].equals(".")) { + + } else if (relparts[i].equals("..")) { + p = Math.max(0, p - 1); + } else { + target[p++] = relparts[i]; + } + } + StringBuffer file = new StringBuffer(); + for (int i = 0; i < p; i++) { + file.append('/').append(target[i]); + } + setURL(url, "ikvmres", url.getHost(), -1, file.toString(), null); + } + } catch (URISyntaxException x){ + throw new RuntimeException(x.getMessage()); + } + } + + protected String toExternalForm(URL url) { + // NOTE originally I wanted to use java.net.URI to handle parsing and constructing of these things, + // but it turns out that URI uses regex and that depends on resource loading... + return "ikvmres://" + quote(url.getHost(), RFC2396_REG_NAME) + quote(url.getFile(), RFC2396_PATH_SEGMENTS); + } + + protected InetAddress getHostAddress(URL url) { + return null; + } + + private static String quote(String str, String legalCharacters) { + StringBuffer sb = new StringBuffer(str.length()); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (legalCharacters.indexOf(c) == -1) { + String hex = "0123456789ABCDEF"; + if (c <= 127) { + sb.append('%') + .append(hex.charAt(c / 16)) + .append(hex.charAt(c % 16)); + } else { + try { + // this is far from optimal, but it works + byte[] utf8 = str.substring(i, i + 1).getBytes("utf-8"); + for (int j = 0; j < utf8.length; j++) { + sb.append('%') + .append(hex.charAt((utf8[j] & 0xff) / 16)) + .append(hex.charAt((utf8[j] & 0xff) % 16)); + } + } + catch (java.io.UnsupportedEncodingException x) { + throw (Error)new InternalError().initCause(x); + } + } + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + private static String unquote(String str) throws URISyntaxException { + if (str == null) + return null; + + byte[] buf = new byte[str.length()]; + int pos = 0; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 127) + throw new URISyntaxException(str, "Invalid character"); + if (c == '%') { + if (i + 2 >= str.length()) + throw new URISyntaxException(str, "Invalid quoted character"); + String hex = "0123456789ABCDEF"; + int hi = hex.indexOf(str.charAt(++i)); + int lo = hex.indexOf(str.charAt(++i)); + if (lo < 0 || hi < 0) + throw new URISyntaxException(str, "Invalid quoted character"); + buf[pos++] = (byte)(hi * 16 + lo); + } else { + buf[pos++] = (byte)c; + } + } + + try { + return new String(buf, 0, pos, "utf-8"); + } catch (java.io.UnsupportedEncodingException x2) { + throw (Error)new InternalError().initCause(x2); + } + } } diff --git a/src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/IkvmresURLConnection.java b/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/IkvmresURLConnection.java similarity index 68% rename from src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/IkvmresURLConnection.java rename to src/IKVM.Java/local/sun/net/www/protocol/ikvmres/IkvmresURLConnection.java index f192341d88..d0032e199a 100644 --- a/src/IKVM.Java/classpath/gnu/java/net/protocol/ikvmres/IkvmresURLConnection.java +++ b/src/IKVM.Java/local/sun/net/www/protocol/ikvmres/IkvmresURLConnection.java @@ -22,75 +22,59 @@ */ -package gnu.java.net.protocol.ikvmres; +package sun.net.www.protocol.ikvmres; -import cli.System.Reflection.Assembly; import java.net.*; import java.io.*; -class IkvmresURLConnection extends URLConnection -{ +import cli.System.Reflection.Assembly; + +class IkvmresURLConnection extends URLConnection { private InputStream inputStream; - IkvmresURLConnection(URL url) - { + IkvmresURLConnection(URL url) { super(url); doOutput = false; } - public void connect() throws IOException - { - if(!connected) - { + public void connect() throws IOException { + if (!connected) { String assembly = url.getHost(); String resource = url.getFile(); - if(assembly == null || resource == null || !resource.startsWith("/")) - { + if (assembly == null || resource == null || !resource.startsWith("/")) { throw new MalformedURLException(url.toString()); } - try - { + try { inputStream = Handler.readResourceFromAssembly(assembly, url.getPort(), resource); connected = true; - } - catch(cli.System.IO.FileNotFoundException x) - { + } catch (cli.System.IO.FileNotFoundException x) { throw (IOException)new FileNotFoundException(assembly).initCause(x); - } - catch(cli.System.BadImageFormatException x1) - { + } catch (cli.System.BadImageFormatException x1) { throw (IOException)new IOException().initCause(x1); - } - catch(cli.System.Security.SecurityException x2) - { + } catch (cli.System.Security.SecurityException x2) { throw (IOException)new IOException().initCause(x2); } } } - public InputStream getInputStream() throws IOException - { - if(!connected) - { + public InputStream getInputStream() throws IOException { + if (!connected) { connect(); } return inputStream; } - public OutputStream getOutputStream() throws IOException - { + public OutputStream getOutputStream() throws IOException { throw new IOException("resource URLs are read only"); } - public long getLastModified() - { + public long getLastModified() { return -1; } - public int getContentLength() - { + public int getContentLength() { return -1; } diff --git a/src/IKVM.Java/local/sun/net/www/protocol/jar/JarFileFactory.java b/src/IKVM.Java/local/sun/net/www/protocol/jar/JarFileFactory.java index e1f854110e..74398f6af5 100644 --- a/src/IKVM.Java/local/sun/net/www/protocol/jar/JarFileFactory.java +++ b/src/IKVM.Java/local/sun/net/www/protocol/jar/JarFileFactory.java @@ -83,7 +83,7 @@ public JarFile get(URL url) throws IOException { } JarFile get(URL url, boolean useCaches) throws IOException { - if (ikvm.internal.Util.WINDOWS && url.getProtocol().equalsIgnoreCase("file")) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() && url.getProtocol().equalsIgnoreCase("file")) { // Deal with UNC pathnames specially. See 4180841 String host = url.getHost(); diff --git a/src/IKVM.Java/local/sun/nio/ch/DatagramChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/DatagramChannelImpl.java deleted file mode 100644 index b2c8607b60..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/DatagramChannelImpl.java +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.nio.channels.spi.*; -import java.util.*; -import sun.net.ResourceManager; -import sun.net.ExtendedOptionsImpl; - -/** - * An implementation of DatagramChannels. - */ - -class DatagramChannelImpl - extends DatagramChannel - implements SelChImpl -{ - - // Used to make native read and write calls - private static NativeDispatcher nd = new SocketDispatcher(); - - // Our file descriptor - private final FileDescriptor fd; - - // fd value needed for dev/poll. This value will remain valid - // even after the value in the file descriptor object has been set to -1 - private final int fdVal; - - // The protocol family of the socket - private final ProtocolFamily family; - - // IDs of native threads doing reads and writes, for signalling - private volatile long readerThread = 0; - private volatile long writerThread = 0; - - // Cached InetAddress and port for unconnected DatagramChannels - // used by receive0 - private InetAddress cachedSenderInetAddress; - private int cachedSenderPort; - - // Lock held by current reading or connecting thread - private final Object readLock = new Object(); - - // Lock held by current writing or connecting thread - private final Object writeLock = new Object(); - - // Lock held by any thread that modifies the state fields declared below - // DO NOT invoke a blocking I/O operation while holding this lock! - private final Object stateLock = new Object(); - - // -- The following fields are protected by stateLock - - // State (does not necessarily increase monotonically) - private static final int ST_UNINITIALIZED = -1; - private static final int ST_UNCONNECTED = 0; - private static final int ST_CONNECTED = 1; - private static final int ST_KILLED = 2; - private int state = ST_UNINITIALIZED; - - // Binding - private InetSocketAddress localAddress; - private InetSocketAddress remoteAddress; - - // Our socket adaptor, if any - private DatagramSocket socket; - - // Multicast support - private MembershipRegistry registry; - - // set true when socket is bound and SO_REUSEADDRESS is emulated - private boolean reuseAddressEmulated; - - // set true/false when socket is already bound and SO_REUSEADDR is emulated - private boolean isReuseAddress; - - // -- End of fields protected by stateLock - - - public DatagramChannelImpl(SelectorProvider sp) - throws IOException - { - super(sp); - ResourceManager.beforeUdpCreate(); - try { - this.family = Net.isIPv6Available() ? - StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; - this.fd = Net.socket(family, false); - this.fdVal = IOUtil.fdVal(fd); - this.state = ST_UNCONNECTED; - } catch (IOException ioe) { - ResourceManager.afterUdpClose(); - throw ioe; - } - } - - public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) - throws IOException - { - super(sp); - if ((family != StandardProtocolFamily.INET) && - (family != StandardProtocolFamily.INET6)) - { - if (family == null) - throw new NullPointerException("'family' is null"); - else - throw new UnsupportedOperationException("Protocol family not supported"); - } - if (family == StandardProtocolFamily.INET6) { - if (!Net.isIPv6Available()) { - throw new UnsupportedOperationException("IPv6 not available"); - } - } - this.family = family; - this.fd = Net.socket(family, false); - this.fdVal = IOUtil.fdVal(fd); - this.state = ST_UNCONNECTED; - } - - public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) - throws IOException - { - super(sp); - this.family = Net.isIPv6Available() ? - StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; - this.fd = fd; - this.fdVal = IOUtil.fdVal(fd); - this.state = ST_UNCONNECTED; - this.localAddress = Net.localAddress(fd); - } - - public DatagramSocket socket() { - synchronized (stateLock) { - if (socket == null) - socket = DatagramSocketAdaptor.create(this); - return socket; - } - } - - @Override - public SocketAddress getLocalAddress() throws IOException { - synchronized (stateLock) { - if (!isOpen()) - throw new ClosedChannelException(); - // Perform security check before returning address - return Net.getRevealedLocalAddress(localAddress); - } - } - - @Override - public SocketAddress getRemoteAddress() throws IOException { - synchronized (stateLock) { - if (!isOpen()) - throw new ClosedChannelException(); - return remoteAddress; - } - } - - @Override - public DatagramChannel setOption(SocketOption name, T value) - throws IOException - { - if (name == null) - throw new NullPointerException(); - if (!supportedOptions().contains(name)) - throw new UnsupportedOperationException("'" + name + "' not supported"); - - synchronized (stateLock) { - ensureOpen(); - - if (name == StandardSocketOptions.IP_TOS || - name == StandardSocketOptions.IP_MULTICAST_TTL || - name == StandardSocketOptions.IP_MULTICAST_LOOP) - { - // options are protocol dependent - Net.setSocketOption(fd, family, name, value); - return this; - } - - if (name == StandardSocketOptions.IP_MULTICAST_IF) { - if (value == null) - throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); - NetworkInterface interf = (NetworkInterface)value; - if (family == StandardProtocolFamily.INET6) { - int index = interf.getIndex(); - if (index == -1) - throw new IOException("Network interface cannot be identified"); - Net.setInterface6(fd, index); - } else { - // need IPv4 address to identify interface - Inet4Address target = Net.anyInet4Address(interf); - if (target == null) - throw new IOException("Network interface not configured for IPv4"); - int targetAddress = Net.inet4AsInt(target); - Net.setInterface4(fd, targetAddress); - } - return this; - } - if (name == StandardSocketOptions.SO_REUSEADDR && - Net.useExclusiveBind() && localAddress != null) - { - reuseAddressEmulated = true; - this.isReuseAddress = (Boolean)value; - } - - // remaining options don't need any special handling - Net.setSocketOption(fd, Net.UNSPEC, name, value); - return this; - } - } - - @Override - @SuppressWarnings("unchecked") - public T getOption(SocketOption name) - throws IOException - { - if (name == null) - throw new NullPointerException(); - if (!supportedOptions().contains(name)) - throw new UnsupportedOperationException("'" + name + "' not supported"); - - synchronized (stateLock) { - ensureOpen(); - - if (name == StandardSocketOptions.IP_TOS || - name == StandardSocketOptions.IP_MULTICAST_TTL || - name == StandardSocketOptions.IP_MULTICAST_LOOP) - { - return (T) Net.getSocketOption(fd, family, name); - } - - if (name == StandardSocketOptions.IP_MULTICAST_IF) { - if (family == StandardProtocolFamily.INET) { - int address = Net.getInterface4(fd); - if (address == 0) - return null; // default interface - - InetAddress ia = Net.inet4FromInt(address); - NetworkInterface ni = NetworkInterface.getByInetAddress(ia); - if (ni == null) - throw new IOException("Unable to map address to interface"); - return (T) ni; - } else { - int index = Net.getInterface6(fd); - if (index == 0) - return null; // default interface - - NetworkInterface ni = NetworkInterface.getByIndex(index); - if (ni == null) - throw new IOException("Unable to map index to interface"); - return (T) ni; - } - } - - if (name == StandardSocketOptions.SO_REUSEADDR && - reuseAddressEmulated) - { - return (T)Boolean.valueOf(isReuseAddress); - } - - // no special handling - return (T) Net.getSocketOption(fd, Net.UNSPEC, name); - } - } - - private static class DefaultOptionsHolder { - static final Set> defaultOptions = defaultOptions(); - - private static Set> defaultOptions() { - HashSet> set = new HashSet>(8); - set.add(StandardSocketOptions.SO_SNDBUF); - set.add(StandardSocketOptions.SO_RCVBUF); - set.add(StandardSocketOptions.SO_REUSEADDR); - set.add(StandardSocketOptions.SO_BROADCAST); - set.add(StandardSocketOptions.IP_TOS); - set.add(StandardSocketOptions.IP_MULTICAST_IF); - set.add(StandardSocketOptions.IP_MULTICAST_TTL); - set.add(StandardSocketOptions.IP_MULTICAST_LOOP); - if (ExtendedOptionsImpl.flowSupported()) { - set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); - } - return Collections.unmodifiableSet(set); - } - } - - @Override - public final Set> supportedOptions() { - return DefaultOptionsHolder.defaultOptions; - } - - private void ensureOpen() throws ClosedChannelException { - if (!isOpen()) - throw new ClosedChannelException(); - } - - SocketAddress sender; // Set by receive0 (## ugh) - - public SocketAddress receive(ByteBuffer dst) throws IOException { - if (dst.isReadOnly()) - throw new IllegalArgumentException("Read-only buffer"); - if (dst == null) - throw new NullPointerException(); - synchronized (readLock) { - ensureOpen(); - // Socket was not bound before attempting receive - if (localAddress() == null) - bind(null); - int n = 0; - ByteBuffer bb = null; - try { - begin(); - if (!isOpen()) - return null; - SecurityManager security = System.getSecurityManager(); - readerThread = NativeThread.current(); - if (isConnected() || (security == null)) { - do { - n = receive(fd, dst); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - if (n == IOStatus.UNAVAILABLE) - return null; - } else { - bb = ByteBuffer.allocate(dst.remaining()); - for (;;) { - do { - n = receive(fd, bb); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - if (n == IOStatus.UNAVAILABLE) - return null; - InetSocketAddress isa = (InetSocketAddress)sender; - try { - security.checkAccept( - isa.getAddress().getHostAddress(), - isa.getPort()); - } catch (SecurityException se) { - // Ignore packet - bb.clear(); - n = 0; - continue; - } - bb.flip(); - dst.put(bb); - break; - } - } - return sender; - } finally { - readerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - private int receive(FileDescriptor fd, ByteBuffer dst) - throws IOException - { - int pos = dst.position(); - int lim = dst.limit(); - assert (pos <= lim); - int rem = (pos <= lim ? lim - pos : 0); - if (dst.hasArray() && rem > 0) - return receiveIntoManagedBuffer(fd, dst, rem, pos); - - // Substitute a managed buffer. If the supplied buffer is empty - // we must instead use a nonempty buffer, otherwise the call - // will not block waiting for a datagram on some platforms. - int newSize = Math.max(rem, 1); - ByteBuffer bb = ByteBuffer.allocate(newSize); - try { - int n = receiveIntoManagedBuffer(fd, bb, newSize, 0); - bb.flip(); - if (n > 0 && rem > 0) - dst.put(bb); - return n; - } finally { - } - } - - private int receiveIntoManagedBuffer(FileDescriptor fd, ByteBuffer bb, - int rem, int pos) - throws IOException - { - int n = receive0(fd, bb.array(), bb.arrayOffset() + pos, rem, - isConnected()); - if (n > 0) - bb.position(pos + n); - return n; - } - - public int send(ByteBuffer src, SocketAddress target) - throws IOException - { - if (src == null) - throw new NullPointerException(); - - synchronized (writeLock) { - ensureOpen(); - InetSocketAddress isa = Net.checkAddress(target); - InetAddress ia = isa.getAddress(); - if (ia == null) - throw new IOException("Target address not resolved"); - synchronized (stateLock) { - if (!isConnected()) { - if (target == null) - throw new NullPointerException(); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - if (ia.isMulticastAddress()) { - sm.checkMulticast(ia); - } else { - sm.checkConnect(ia.getHostAddress(), - isa.getPort()); - } - } - } else { // Connected case; Check address then write - if (!target.equals(remoteAddress)) { - throw new IllegalArgumentException( - "Connected address not equal to target address"); - } - return write(src); - } - } - - int n = 0; - try { - begin(); - if (!isOpen()) - return 0; - writerThread = NativeThread.current(); - do { - n = send(fd, src, isa); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - - synchronized (stateLock) { - if (isOpen() && (localAddress == null)) { - localAddress = Net.localAddress(fd); - } - } - return IOStatus.normalize(n); - } finally { - writerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target) - throws IOException - { - if (src.hasArray()) - return sendFromManagedBuffer(fd, src, target); - - // Substitute a managed buffer - int pos = src.position(); - int lim = src.limit(); - assert (pos <= lim); - int rem = (pos <= lim ? lim - pos : 0); - - ByteBuffer bb = ByteBuffer.allocate(rem); - try { - bb.put(src); - bb.flip(); - // Do not update src until we see how many bytes were written - src.position(pos); - - int n = sendFromManagedBuffer(fd, bb, target); - if (n > 0) { - // now update src - src.position(pos + n); - } - return n; - } finally { - } - } - - private int sendFromManagedBuffer(FileDescriptor fd, ByteBuffer bb, - InetSocketAddress target) - throws IOException - { - int pos = bb.position(); - int lim = bb.limit(); - assert (pos <= lim); - int rem = (pos <= lim ? lim - pos : 0); - - boolean preferIPv6 = (family != StandardProtocolFamily.INET); - int written; - try { - written = send0(preferIPv6, fd, bb.array(), bb.arrayOffset() + pos, - rem, target.getAddress(), target.getPort()); - } catch (PortUnreachableException pue) { - if (isConnected()) - throw pue; - written = rem; - } - if (written > 0) - bb.position(pos + written); - return written; - } - - public int read(ByteBuffer buf) throws IOException { - if (buf == null) - throw new NullPointerException(); - synchronized (readLock) { - synchronized (stateLock) { - ensureOpen(); - if (!isConnected()) - throw new NotYetConnectedException(); - } - int n = 0; - try { - begin(); - if (!isOpen()) - return 0; - readerThread = NativeThread.current(); - do { - n = IOUtil.read(fd, buf, -1, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - readerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) - throw new IndexOutOfBoundsException(); - synchronized (readLock) { - synchronized (stateLock) { - ensureOpen(); - if (!isConnected()) - throw new NotYetConnectedException(); - } - long n = 0; - try { - begin(); - if (!isOpen()) - return 0; - readerThread = NativeThread.current(); - do { - n = IOUtil.read(fd, dsts, offset, length, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - readerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - public int write(ByteBuffer buf) throws IOException { - if (buf == null) - throw new NullPointerException(); - synchronized (writeLock) { - synchronized (stateLock) { - ensureOpen(); - if (!isConnected()) - throw new NotYetConnectedException(); - } - int n = 0; - try { - begin(); - if (!isOpen()) - return 0; - writerThread = NativeThread.current(); - do { - n = IOUtil.write(fd, buf, -1, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - writerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) - throw new IndexOutOfBoundsException(); - synchronized (writeLock) { - synchronized (stateLock) { - ensureOpen(); - if (!isConnected()) - throw new NotYetConnectedException(); - } - long n = 0; - try { - begin(); - if (!isOpen()) - return 0; - writerThread = NativeThread.current(); - do { - n = IOUtil.write(fd, srcs, offset, length, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - writerThread = 0; - end((n > 0) || (n == IOStatus.UNAVAILABLE)); - assert IOStatus.check(n); - } - } - } - - protected void implConfigureBlocking(boolean block) throws IOException { - IOUtil.configureBlocking(fd, block); - } - - public SocketAddress localAddress() { - synchronized (stateLock) { - return localAddress; - } - } - - public SocketAddress remoteAddress() { - synchronized (stateLock) { - return remoteAddress; - } - } - - @Override - public DatagramChannel bind(SocketAddress local) throws IOException { - synchronized (readLock) { - synchronized (writeLock) { - synchronized (stateLock) { - ensureOpen(); - if (localAddress != null) - throw new AlreadyBoundException(); - InetSocketAddress isa; - if (local == null) { - // only Inet4Address allowed with IPv4 socket - if (family == StandardProtocolFamily.INET) { - isa = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0); - } else { - isa = new InetSocketAddress(0); - } - } else { - isa = Net.checkAddress(local); - - // only Inet4Address allowed with IPv4 socket - if (family == StandardProtocolFamily.INET) { - InetAddress addr = isa.getAddress(); - if (!(addr instanceof Inet4Address)) - throw new UnsupportedAddressTypeException(); - } - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkListen(isa.getPort()); - } - Net.bind(family, fd, isa.getAddress(), isa.getPort()); - localAddress = Net.localAddress(fd); - } - } - } - return this; - } - - public boolean isConnected() { - synchronized (stateLock) { - return (state == ST_CONNECTED); - } - } - - void ensureOpenAndUnconnected() throws IOException { // package-private - synchronized (stateLock) { - if (!isOpen()) - throw new ClosedChannelException(); - if (state != ST_UNCONNECTED) - throw new IllegalStateException("Connect already invoked"); - } - } - - @Override - public DatagramChannel connect(SocketAddress sa) throws IOException { - int localPort = 0; - - synchronized(readLock) { - synchronized(writeLock) { - synchronized (stateLock) { - ensureOpenAndUnconnected(); - InetSocketAddress isa = Net.checkAddress(sa); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkConnect(isa.getAddress().getHostAddress(), - isa.getPort()); - int n = Net.connect(family, - fd, - isa.getAddress(), - isa.getPort()); - if (n <= 0) - throw new Error(); // Can't happen - - // Connection succeeded; disallow further invocation - state = ST_CONNECTED; - remoteAddress = isa; - sender = isa; - cachedSenderInetAddress = isa.getAddress(); - cachedSenderPort = isa.getPort(); - - // set or refresh local address - localAddress = Net.localAddress(fd); - - // flush any packets already received. - boolean blocking = false; - synchronized (blockingLock()) { - try { - blocking = isBlocking(); - // remainder of each packet thrown away - ByteBuffer tmpBuf = ByteBuffer.allocate(1); - if (blocking) { - configureBlocking(false); - } - do { - tmpBuf.clear(); - } while (receive(tmpBuf) != null); - } finally { - if (blocking) { - configureBlocking(true); - } - } - } - } - } - } - return this; - } - - public DatagramChannel disconnect() throws IOException { - synchronized(readLock) { - synchronized(writeLock) { - synchronized (stateLock) { - if (!isConnected() || !isOpen()) - return this; - InetSocketAddress isa = remoteAddress; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkConnect(isa.getAddress().getHostAddress(), - isa.getPort()); - boolean isIPv6 = (family == StandardProtocolFamily.INET6); - disconnect0(fd, isIPv6); - remoteAddress = null; - state = ST_UNCONNECTED; - - // refresh local address - localAddress = Net.localAddress(fd); - } - } - } - return this; - } - - /** - * Joins channel's socket to the given group/interface and - * optional source address. - */ - private MembershipKey innerJoin(InetAddress group, - NetworkInterface interf, - InetAddress source) - throws IOException - { - if (!group.isMulticastAddress()) - throw new IllegalArgumentException("Group not a multicast address"); - - // check multicast address is compatible with this socket - if (group instanceof Inet4Address) { - if (family == StandardProtocolFamily.INET6 && !Net.canIPv6SocketJoinIPv4Group()) - throw new IllegalArgumentException("IPv6 socket cannot join IPv4 multicast group"); - } else if (group instanceof Inet6Address) { - if (family != StandardProtocolFamily.INET6) - throw new IllegalArgumentException("Only IPv6 sockets can join IPv6 multicast group"); - } else { - throw new IllegalArgumentException("Address type not supported"); - } - - // check source address - if (source != null) { - if (source.isAnyLocalAddress()) - throw new IllegalArgumentException("Source address is a wildcard address"); - if (source.isMulticastAddress()) - throw new IllegalArgumentException("Source address is multicast address"); - if (source.getClass() != group.getClass()) - throw new IllegalArgumentException("Source address is different type to group"); - } - - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkMulticast(group); - - synchronized (stateLock) { - if (!isOpen()) - throw new ClosedChannelException(); - - // check the registry to see if we are already a member of the group - if (registry == null) { - registry = new MembershipRegistry(); - } else { - // return existing membership key - MembershipKey key = registry.checkMembership(group, interf, source); - if (key != null) - return key; - } - - MembershipKeyImpl key; - if ((family == StandardProtocolFamily.INET6) && - ((group instanceof Inet6Address) || Net.canJoin6WithIPv4Group())) - { - int index = interf.getIndex(); - if (index == -1) - throw new IOException("Network interface cannot be identified"); - - // need multicast and source address as byte arrays - byte[] groupAddress = Net.inet6AsByteArray(group); - byte[] sourceAddress = (source == null) ? null : - Net.inet6AsByteArray(source); - - // join the group - int n = Net.join6(fd, groupAddress, index, sourceAddress); - if (n == IOStatus.UNAVAILABLE) - throw new UnsupportedOperationException(); - - key = new MembershipKeyImpl.Type6(this, group, interf, source, - groupAddress, index, sourceAddress); - - } else { - // need IPv4 address to identify interface - Inet4Address target = Net.anyInet4Address(interf); - if (target == null) - throw new IOException("Network interface not configured for IPv4"); - - int groupAddress = Net.inet4AsInt(group); - int targetAddress = Net.inet4AsInt(target); - int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source); - - // join the group - int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress); - if (n == IOStatus.UNAVAILABLE) - throw new UnsupportedOperationException(); - - key = new MembershipKeyImpl.Type4(this, group, interf, source, - groupAddress, targetAddress, sourceAddress); - } - - registry.add(key); - return key; - } - } - - @Override - public MembershipKey join(InetAddress group, - NetworkInterface interf) - throws IOException - { - return innerJoin(group, interf, null); - } - - @Override - public MembershipKey join(InetAddress group, - NetworkInterface interf, - InetAddress source) - throws IOException - { - if (source == null) - throw new NullPointerException("source address is null"); - return innerJoin(group, interf, source); - } - - // package-private - void drop(MembershipKeyImpl key) { - assert key.channel() == this; - - synchronized (stateLock) { - if (!key.isValid()) - return; - - try { - if (key instanceof MembershipKeyImpl.Type6) { - MembershipKeyImpl.Type6 key6 = - (MembershipKeyImpl.Type6)key; - Net.drop6(fd, key6.groupAddress(), key6.index(), key6.source()); - } else { - MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4)key; - Net.drop4(fd, key4.groupAddress(), key4.interfaceAddress(), - key4.source()); - } - } catch (IOException ioe) { - // should not happen - throw new AssertionError(ioe); - } - - key.invalidate(); - registry.remove(key); - } - } - - /** - * Block datagrams from given source if a memory to receive all - * datagrams. - */ - void block(MembershipKeyImpl key, InetAddress source) - throws IOException - { - assert key.channel() == this; - assert key.sourceAddress() == null; - - synchronized (stateLock) { - if (!key.isValid()) - throw new IllegalStateException("key is no longer valid"); - if (source.isAnyLocalAddress()) - throw new IllegalArgumentException("Source address is a wildcard address"); - if (source.isMulticastAddress()) - throw new IllegalArgumentException("Source address is multicast address"); - if (source.getClass() != key.group().getClass()) - throw new IllegalArgumentException("Source address is different type to group"); - - int n; - if (key instanceof MembershipKeyImpl.Type6) { - MembershipKeyImpl.Type6 key6 = - (MembershipKeyImpl.Type6)key; - n = Net.block6(fd, key6.groupAddress(), key6.index(), - Net.inet6AsByteArray(source)); - } else { - MembershipKeyImpl.Type4 key4 = - (MembershipKeyImpl.Type4)key; - n = Net.block4(fd, key4.groupAddress(), key4.interfaceAddress(), - Net.inet4AsInt(source)); - } - if (n == IOStatus.UNAVAILABLE) { - // ancient kernel - throw new UnsupportedOperationException(); - } - } - } - - /** - * Unblock given source. - */ - void unblock(MembershipKeyImpl key, InetAddress source) { - assert key.channel() == this; - assert key.sourceAddress() == null; - - synchronized (stateLock) { - if (!key.isValid()) - throw new IllegalStateException("key is no longer valid"); - - try { - if (key instanceof MembershipKeyImpl.Type6) { - MembershipKeyImpl.Type6 key6 = - (MembershipKeyImpl.Type6)key; - Net.unblock6(fd, key6.groupAddress(), key6.index(), - Net.inet6AsByteArray(source)); - } else { - MembershipKeyImpl.Type4 key4 = - (MembershipKeyImpl.Type4)key; - Net.unblock4(fd, key4.groupAddress(), key4.interfaceAddress(), - Net.inet4AsInt(source)); - } - } catch (IOException ioe) { - // should not happen - throw new AssertionError(ioe); - } - } - } - - protected void implCloseSelectableChannel() throws IOException { - synchronized (stateLock) { - if (state != ST_KILLED) - nd.preClose(fd); - ResourceManager.afterUdpClose(); - - // if member of mulitcast group then invalidate all keys - if (registry != null) - registry.invalidateAll(); - - long th; - if ((th = readerThread) != 0) - NativeThread.signal(th); - if ((th = writerThread) != 0) - NativeThread.signal(th); - if (!isRegistered()) - kill(); - } - } - - public void kill() throws IOException { - synchronized (stateLock) { - if (state == ST_KILLED) - return; - if (state == ST_UNINITIALIZED) { - state = ST_KILLED; - return; - } - assert !isOpen() && !isRegistered(); - nd.close(fd); - state = ST_KILLED; - } - } - - /** - * Translates native poll revent set into a ready operation set - */ - public boolean translateReadyOps(int ops, int initialOps, - SelectionKeyImpl sk) { - int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes - int oldOps = sk.nioReadyOps(); - int newOps = initialOps; - - if ((ops & Net.POLLNVAL) != 0) { - // This should only happen if this channel is pre-closed while a - // selection operation is in progress - // ## Throw an error if this channel has not been pre-closed - return false; - } - - if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { - newOps = intOps; - sk.nioReadyOps(newOps); - return (newOps & ~oldOps) != 0; - } - - if (((ops & Net.POLLIN) != 0) && - ((intOps & SelectionKey.OP_READ) != 0)) - newOps |= SelectionKey.OP_READ; - - if (((ops & Net.POLLOUT) != 0) && - ((intOps & SelectionKey.OP_WRITE) != 0)) - newOps |= SelectionKey.OP_WRITE; - - sk.nioReadyOps(newOps); - return (newOps & ~oldOps) != 0; - } - - public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { - return translateReadyOps(ops, sk.nioReadyOps(), sk); - } - - public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { - return translateReadyOps(ops, 0, sk); - } - - // package-private - int poll(int events, long timeout) throws IOException { - assert Thread.holdsLock(blockingLock()) && !isBlocking(); - - synchronized (readLock) { - int n = 0; - try { - begin(); - synchronized (stateLock) { - if (!isOpen()) - return 0; - readerThread = NativeThread.current(); - } - n = Net.poll(fd, events, timeout); - } finally { - readerThread = 0; - end(n > 0); - } - return n; - } - } - - /** - * Translates an interest operation set into a native poll event set - */ - public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { - int newOps = 0; - - if ((ops & SelectionKey.OP_READ) != 0) - newOps |= Net.POLLIN; - if ((ops & SelectionKey.OP_WRITE) != 0) - newOps |= Net.POLLOUT; - if ((ops & SelectionKey.OP_CONNECT) != 0) - newOps |= Net.POLLIN; - sk.selector.putEventOps(sk, newOps); - } - - public FileDescriptor getFD() { - return fd; - } - - public int getFDVal() { - return fdVal; - } - - - // -- Native methods -- - - private static native void initIDs(); - - private static native void disconnect0(FileDescriptor fd, boolean isIPv6) - throws IOException; - - private native int receive0(FileDescriptor fd, byte[] buf, int pos, int len, - boolean connected) - throws IOException; - - private native int send0(boolean preferIPv6, FileDescriptor fd, byte[] buf, int pos, - int len, InetAddress addr, int port) - throws IOException; - - static { - IOUtil.load(); - initIDs(); - } - -} diff --git a/src/IKVM.Java/local/sun/nio/ch/DatagramDispatcher.java b/src/IKVM.Java/local/sun/nio/ch/DatagramDispatcher.java new file mode 100644 index 0000000000..a200ef4c72 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DatagramDispatcher.java @@ -0,0 +1,21 @@ +package sun.nio.ch; + +import java.io.*; + +class DatagramDispatcher extends NativeDispatcher { + + static { + IOUtil.load(); + } + + native int read(FileDescriptor fd, long address, int len) throws IOException; + + native long readv(FileDescriptor fd, long address, int len) throws IOException; + + native int write(FileDescriptor fd, long address, int len) throws IOException; + + native long writev(FileDescriptor fd, long address, int len) throws IOException; + + native void close(FileDescriptor fd) throws IOException; + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/IKVM.Java/local/sun/nio/ch/DefaultAsynchronousChannelProvider.java new file mode 100644 index 0000000000..25f33fdf87 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DefaultAsynchronousChannelProvider.java @@ -0,0 +1,15 @@ +package sun.nio.ch; + +import java.nio.channels.spi.AsynchronousChannelProvider; + +public class DefaultAsynchronousChannelProvider { + + private DefaultAsynchronousChannelProvider() { + + } + + public static AsynchronousChannelProvider create() { + return new DotNetAsynchronousChannelProvider(); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DefaultSelectorProvider.java b/src/IKVM.Java/local/sun/nio/ch/DefaultSelectorProvider.java index 8c5cf0379b..4e6bebb736 100644 --- a/src/IKVM.Java/local/sun/nio/ch/DefaultSelectorProvider.java +++ b/src/IKVM.Java/local/sun/nio/ch/DefaultSelectorProvider.java @@ -1,49 +1,15 @@ -/* - * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - package sun.nio.ch; import java.io.IOException; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.SelectorProvider; - -/** - * Creates this platform's default SelectorProvider - */ - public class DefaultSelectorProvider { - /** - * Prevent instantiation. - */ - private DefaultSelectorProvider() { } + private DefaultSelectorProvider() { + + } - /** - * Returns the default SelectorProvider. - */ public static SelectorProvider create() { return new SelectorProviderImpl() { public AbstractSelector openSelector() throws IOException { diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelGroup.java b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelGroup.java new file mode 100644 index 0000000000..f86b7e98ec --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelGroup.java @@ -0,0 +1,61 @@ +package sun.nio.ch; + +import java.nio.channels.*; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.io.Closeable; +import java.io.IOException; +import java.io.FileDescriptor; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import ikvm.internal.NotYetImplementedError; + +/** + * .NET implementation of AsynchronousChannelGroup. + */ +class DotNetAsynchronousChannelGroup extends AsynchronousChannelGroupImpl { + + private boolean closed; + + DotNetAsynchronousChannelGroup(AsynchronousChannelProvider provider, ThreadPool pool) throws IOException { + super(provider, pool); + } + + // release all resources + synchronized void implClose() { + closed = true; + } + + @Override + boolean isEmpty() { + return true; + } + + @Override + final Object attachForeignChannel(final Channel channel, FileDescriptor fdObj) throws IOException { + throw new NotYetImplementedError(); + } + + @Override + final void detachForeignChannel(Object key) { + throw new NotYetImplementedError(); + } + + @Override + void closeAllChannels() { + + } + + @Override + void executeOnHandlerTask(Runnable task) { + throw new NotYetImplementedError(); + } + + @Override + void shutdownHandlerTasks() { + + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelProvider.java b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelProvider.java new file mode 100644 index 0000000000..d096956056 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousChannelProvider.java @@ -0,0 +1,60 @@ +package sun.nio.ch; + +import java.nio.channels.*; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.io.IOException; + +public class DotNetAsynchronousChannelProvider extends AsynchronousChannelProvider { + + private static volatile DotNetAsynchronousChannelGroup defaultGroup; + + public DotNetAsynchronousChannelProvider() { + // nothing to do + } + + private DotNetAsynchronousChannelGroup defaultGroup() throws IOException { + if (defaultGroup == null) { + synchronized (DotNetAsynchronousChannelProvider.class) { + if (defaultGroup == null) { + defaultGroup = new DotNetAsynchronousChannelGroup(this, ThreadPool.getDefault()); + } + } + } + + return defaultGroup; + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory) throws IOException { + return new DotNetAsynchronousChannelGroup(this, ThreadPool.create(nThreads, factory)); + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize) throws IOException { + return new DotNetAsynchronousChannelGroup(this, ThreadPool.wrap(executor, initialSize)); + } + + private DotNetAsynchronousChannelGroup toDotNet(AsynchronousChannelGroup group) throws IOException { + if (group == null) { + return defaultGroup(); + } else { + if (!(group instanceof DotNetAsynchronousChannelGroup)) + throw new IllegalChannelGroupException(); + + return (DotNetAsynchronousChannelGroup)group; + } + } + + @Override + public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group) throws IOException { + return new DotNetAsynchronousServerSocketChannelImpl(toDotNet(group)); + } + + @Override + public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group) throws IOException { + return new DotNetAsynchronousSocketChannelImpl(toDotNet(group)); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousFileChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousFileChannelImpl.java new file mode 100644 index 0000000000..3b27cef592 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousFileChannelImpl.java @@ -0,0 +1,134 @@ +package sun.nio.ch; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.concurrent.*; + +/** + * .NET implementation of AsynchronousFileChannel. + */ +public class DotNetAsynchronousFileChannelImpl extends AsynchronousFileChannelImpl implements Cancellable, Groupable { + + private static class DefaultGroupHolder { + + static final DotNetAsynchronousChannelGroup defaultGroup = defaultGroup(); + + private static DotNetAsynchronousChannelGroup defaultGroup() { + try { + return new DotNetAsynchronousChannelGroup(null, ThreadPool.createDefault()); + } catch (IOException ioe) { + throw new InternalError(ioe); + } + } + + } + + public static AsynchronousFileChannel open(FileDescriptor fdo, boolean reading, boolean writing, ThreadPool pool) throws IOException + { + DotNetAsynchronousChannelGroup group; + boolean isDefaultGroup; + + if (pool == null) { + group = DefaultGroupHolder.defaultGroup; + isDefaultGroup = true; + } else { + group = new DotNetAsynchronousChannelGroup(null, pool); + isDefaultGroup = false; + } + try { + return new DotNetAsynchronousFileChannelImpl(fdo, reading, writing, group, isDefaultGroup); + } catch (IOException x) { + // error binding to port so need to close it (if created for this channel) + if (!isDefaultGroup) + group.implClose(); + + throw x; + } + } + + protected final DotNetAsynchronousChannelGroup group; + protected final boolean isDefaultGroup; + + private DotNetAsynchronousFileChannelImpl(FileDescriptor fdObj, boolean reading, boolean writing, DotNetAsynchronousChannelGroup group, boolean isDefaultGroup) throws IOException { + super(fdObj, reading, writing, group.executor()); + this.group = group; + this.isDefaultGroup = isDefaultGroup; + } + + @Override + public AsynchronousChannelGroupImpl group() { + return group; + } + + @Override + public void onCancel(PendingFuture task) { + onCancel0(task); + } + + @Override + public void close() throws IOException { + close0(); + } + + @Override + public long size() throws IOException { + return size0(); + } + + @Override + public AsynchronousFileChannel truncate(long size) throws IOException { + return truncate0(size); + } + + @Override + public void force(boolean metaData) throws IOException { + force0(metaData); + } + + @Override + Future implLock(final long position, final long size, final boolean shared, A attachment, final CompletionHandler handler) { + return implLock0(position, size, shared, attachment, handler); + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) throws IOException { + return tryLock0(position, size, shared); + } + + @Override + protected void implRelease(FileLockImpl fli) throws IOException { + implRelease0(fli); + } + + @Override + Future implRead(ByteBuffer dst, long position, A attachment, CompletionHandler handler) + { + return implRead0(dst, position, attachment, handler); + } + + Future implWrite(ByteBuffer src, long position, A attachment, CompletionHandler handler) { + return implWrite0(src, position, attachment, handler); + } + + private native void onCancel0(PendingFuture task); + + private native void close0(); + + private native long size0(); + + private native AsynchronousFileChannel truncate0(long size); + + private native void force0(boolean metaData); + + private native Future implLock0(final long position, final long size, final boolean shared, A attachment, final CompletionHandler handler); + + private native FileLock tryLock0(long position, long size, boolean shared); + + private native void implRelease0(FileLockImpl fli); + + private native Future implRead0(ByteBuffer dst, long position, A attachment, CompletionHandler handler); + + private native Future implWrite0(ByteBuffer src, long position, A attachment, CompletionHandler handler); + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.java new file mode 100644 index 0000000000..5b726c0c30 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.java @@ -0,0 +1,40 @@ +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.*; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * .NET implementation of AsynchronousServerSocketChannel using overlapped I/O. + */ +class DotNetAsynchronousServerSocketChannelImpl extends AsynchronousServerSocketChannelImpl { + + protected final DotNetAsynchronousChannelGroup group; + protected AtomicBoolean accepting = new AtomicBoolean(); + + DotNetAsynchronousServerSocketChannelImpl(DotNetAsynchronousChannelGroup group) throws IOException { + super(group); + this.group = group; + } + + @Override + public AsynchronousChannelGroupImpl group() { + return group; + } + + @Override + void implClose() throws IOException { + implClose0(); + } + + @Override + Future implAccept(Object attachment, final CompletionHandler handler) { + return implAccept0(attachment, handler); + } + + private native Future implAccept0(Object attachment, final CompletionHandler handler); + + private native void implClose0() throws IOException; + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.java new file mode 100644 index 0000000000..45525e0e8b --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.java @@ -0,0 +1,70 @@ +package sun.nio.ch; + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.concurrent.*; + +/** + * .NET implementation of AsynchronousSocketChannelImpl. + */ +class DotNetAsynchronousSocketChannelImpl extends AsynchronousSocketChannelImpl { + + private final DotNetAsynchronousChannelGroup group; + + DotNetAsynchronousSocketChannelImpl(DotNetAsynchronousChannelGroup group, boolean failIfGroupShutdown) throws IOException { + super(group); + this.group = group; + } + + DotNetAsynchronousSocketChannelImpl(DotNetAsynchronousChannelGroup group) throws IOException { + this(group, true); + } + + DotNetAsynchronousSocketChannelImpl(DotNetAsynchronousChannelGroup group, FileDescriptor fd, InetSocketAddress remote) throws IOException { + super(group, fd, remote); + this.group = group; + } + + @Override + public AsynchronousChannelGroupImpl group() { + return group; + } + + @Override + public void onCancel(PendingFuture task) { + onCancel0(task); + } + + @Override + void implClose() throws IOException { + implClose0(); + } + + @Override + Future implConnect(SocketAddress remote, A attachment, CompletionHandler handler) { + return implConnect0(remote, attachment, handler); + } + + @Override + Future implRead(boolean isScatteringRead, ByteBuffer dst, ByteBuffer[] dsts, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) { + return implRead0(isScatteringRead, dst, dsts, timeout, unit, attachment, handler); + } + + @Override + Future implWrite(boolean gatheringWrite, ByteBuffer src, ByteBuffer[] srcs, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) { + return implWrite0(gatheringWrite, src, srcs, timeout, unit, attachment, handler); + } + + private native void onCancel0(PendingFuture task); + + private native void implClose0(); + + private native Future implConnect0(SocketAddress remote, A attachment, CompletionHandler handler); + + private native Future implRead0(boolean isScatteringRead, ByteBuffer dst, ByteBuffer[] dsts, long timeout, TimeUnit unit, A attachment, CompletionHandler handler); + + private native Future implWrite0(boolean gatheringWrite, ByteBuffer src, ByteBuffer[] srcs, long timeout, TimeUnit unit, A attachment, CompletionHandler handler); + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/DotNetSelectorImpl.java b/src/IKVM.Java/local/sun/nio/ch/DotNetSelectorImpl.java index 5f20394b6e..e86e1dda97 100644 --- a/src/IKVM.Java/local/sun/nio/ch/DotNetSelectorImpl.java +++ b/src/IKVM.Java/local/sun/nio/ch/DotNetSelectorImpl.java @@ -27,13 +27,6 @@ package sun.nio.ch; -import cli.System.Net.Sockets.Socket; -import cli.System.Net.Sockets.SocketException; -import cli.System.Net.Sockets.AddressFamily; -import cli.System.Net.Sockets.SocketType; -import cli.System.Net.Sockets.ProtocolType; -import cli.System.Net.Sockets.SelectMode; -import cli.System.Collections.ArrayList; import java.io.IOException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.Pipe; @@ -50,53 +43,62 @@ import java.util.Iterator; import java.util.Set; -final class DotNetSelectorImpl extends SelectorImpl -{ +import cli.System.Net.Sockets.Socket; +import cli.System.Net.Sockets.SocketException; +import cli.System.Net.Sockets.AddressFamily; +import cli.System.Net.Sockets.SocketType; +import cli.System.Net.Sockets.ProtocolType; +import cli.System.Net.Sockets.SelectMode; +import cli.System.Collections.ArrayList; + +final class DotNetSelectorImpl extends SelectorImpl { + + // class for fdMap entries + private final static class MapEntry { + + SelectionKeyImpl ski; + long updateCount = 0; + long clearedCount = 0; + + MapEntry(SelectionKeyImpl ski) { + this.ski = ski; + } + + } + private ArrayList channelArray = new ArrayList(); private long updateCount = 0; - //Pipe used as a wakeup object. + // pipe used as a wakeup object private final Pipe wakeupPipe; - // File descriptors corresponding to source and sink + // file descriptors corresponding to source and sink private final Socket wakeupSourceFd, wakeupSinkFd; - // Lock for interrupt triggering and clearing + // lock for interrupt triggering and clearing private final Object interruptLock = new Object(); private volatile boolean interruptTriggered = false; - // class for fdMap entries - private final static class MapEntry - { - SelectionKeyImpl ski; - long updateCount = 0; - long clearedCount = 0; - MapEntry(SelectionKeyImpl ski) - { - this.ski = ski; - } - } private final HashMap fdMap = new HashMap(); - DotNetSelectorImpl(SelectorProvider sp) throws IOException - { + DotNetSelectorImpl(SelectorProvider sp) throws IOException { super(sp); + wakeupPipe = Pipe.open(); wakeupSourceFd = ((SelChImpl)wakeupPipe.source()).getFD().getSocket(); - // Disable the Nagle algorithm so that the wakeup is more immediate + // disable the Nagle algorithm so that the wakeup is more immediate SinkChannelImpl sink = (SinkChannelImpl)wakeupPipe.sink(); (sink.sc).socket().setTcpNoDelay(true); wakeupSinkFd = ((SelChImpl)sink).getFD().getSocket(); } - protected int doSelect(long timeout) throws IOException - { + protected int doSelect(long timeout) throws IOException { if (channelArray == null) throw new ClosedSelectorException(); + processDeregisterQueue(); - if (interruptTriggered) - { + if (interruptTriggered) { resetWakeupSocket(); return 0; } @@ -104,66 +106,59 @@ protected int doSelect(long timeout) throws IOException ArrayList read = new ArrayList(); ArrayList write = new ArrayList(); ArrayList error = new ArrayList(); - for (int i = 0; i < channelArray.get_Count(); i++) - { + for (int i = 0; i < channelArray.get_Count(); i++) { SelectionKeyImpl ski = (SelectionKeyImpl)channelArray.get_Item(i); int ops = ski.interestOps(); - if (ski.channel() instanceof SocketChannelImpl) - { + if (ski.channel() instanceof SocketChannelImpl) { // TODO there's a race condition here... - if (((SocketChannelImpl)ski.channel()).isConnected()) - { + if (((SocketChannelImpl)ski.channel()).isConnected()) { ops &= SelectionKey.OP_READ | SelectionKey.OP_WRITE; - } - else - { + } else { ops &= SelectionKey.OP_CONNECT; } } - if ((ops & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) - { - read.Add(ski.getSocket()); + + if ((ops & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) { + read.Add(((SelChImpl)ski.channel()).getFD().getSocket()); } - if ((ops & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0) - { - write.Add(ski.getSocket()); + + if ((ops & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0) { + write.Add(((SelChImpl)ski.channel()).getFD().getSocket()); } - if ((ops & SelectionKey.OP_CONNECT) != 0) - { - error.Add(ski.getSocket()); + + if ((ops & SelectionKey.OP_CONNECT) != 0) { + error.Add(((SelChImpl)ski.channel()).getFD().getSocket()); } } + read.Add(wakeupSourceFd); + try { begin(); int microSeconds = 1000 * (int)Math.min(Integer.MAX_VALUE / 1000, timeout); - try - { + try { if (false) throw new SocketException(); // FXBUG docs say that -1 is infinite timeout, but that doesn't appear to work Socket.Select(read, write, error, timeout < 0 ? Integer.MAX_VALUE : microSeconds); - } - catch (SocketException _) - { + } catch (SocketException _) { read.Clear(); write.Clear(); error.Clear(); } - } - finally - { + } finally { end(); } + processDeregisterQueue(); int updated = updateSelectedKeys(read, write, error); - // Done with poll(). Set wakeupSocket to nonsignaled for the next run. + + // done with poll(), set wakeupSocket to nonsignaled for the next run. resetWakeupSocket(); return updated; } - private int updateSelectedKeys(ArrayList read, ArrayList write, ArrayList error) - { + private int updateSelectedKeys(ArrayList read, ArrayList write, ArrayList error) { updateCount++; int keys = processFDSet(updateCount, read, Net.POLLIN); keys += processFDSet(updateCount, write, Net.POLLCONN | Net.POLLOUT); @@ -171,88 +166,77 @@ private int updateSelectedKeys(ArrayList read, ArrayList write, ArrayList error) return keys; } - private int processFDSet(long updateCount, ArrayList sockets, int rOps) - { + private int processFDSet(long updateCount, ArrayList sockets, int rOps) { int numKeysUpdated = 0; - for (int i = 0; i < sockets.get_Count(); i++) - { + + for (int i = 0; i < sockets.get_Count(); i++) { Socket desc = (Socket)sockets.get_Item(i); - if (desc == wakeupSourceFd) - { - synchronized (interruptLock) - { + if (desc == wakeupSourceFd) { + synchronized (interruptLock) { interruptTriggered = true; } + continue; } + + // if me is null, the key was deregistered in the previous processDeregisterQueue MapEntry me = fdMap.get(desc); - // If me is null, the key was deregistered in the previous - // processDeregisterQueue. if (me == null) continue; SelectionKeyImpl sk = me.ski; - if (selectedKeys.contains(sk)) - { // Key in selected set - if (me.clearedCount != updateCount) - { - if (sk.channel.translateAndSetReadyOps(rOps, sk) && - (me.updateCount != updateCount)) - { + if (selectedKeys.contains(sk)) { + if (me.clearedCount != updateCount) { + // key in selected set + if (sk.channel.translateAndSetReadyOps(rOps, sk) && (me.updateCount != updateCount)) { me.updateCount = updateCount; numKeysUpdated++; } - } - else - { // The readyOps have been set; now add - if (sk.channel.translateAndUpdateReadyOps(rOps, sk) && - (me.updateCount != updateCount)) - { + } else { + // the readyOps have been set; now add + if (sk.channel.translateAndUpdateReadyOps(rOps, sk) && (me.updateCount != updateCount)) { me.updateCount = updateCount; numKeysUpdated++; } } + me.clearedCount = updateCount; - } - else - { // Key is not in selected set yet - if (me.clearedCount != updateCount) - { + } else { + if (me.clearedCount != updateCount) { + // Key is not in selected set yet sk.channel.translateAndSetReadyOps(rOps, sk); - if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) - { + if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) { selectedKeys.add(sk); me.updateCount = updateCount; numKeysUpdated++; } - } - else - { // The readyOps have been set; now add + } else { + // The readyOps have been set; now add sk.channel.translateAndUpdateReadyOps(rOps, sk); - if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) - { + if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) { selectedKeys.add(sk); me.updateCount = updateCount; numKeysUpdated++; } } + me.clearedCount = updateCount; } } + return numKeysUpdated; } - protected void implClose() throws IOException - { - if (channelArray != null) - { + protected void implClose() throws IOException { + if (channelArray != null) { // prevent further wakeup synchronized (interruptLock) { interruptTriggered = true; } + wakeupPipe.sink().close(); wakeupPipe.source().close(); - for (int i = 0; i < channelArray.get_Count(); i++) - { // Deregister channels + for (int i = 0; i < channelArray.get_Count(); i++) { + // Deregister channels SelectionKeyImpl ski = (SelectionKeyImpl)channelArray.get_Item(i); deregister(ski); SelectableChannel selch = ski.channel(); @@ -264,37 +248,33 @@ protected void implClose() throws IOException } } - protected void implRegister(SelectionKeyImpl ski) - { + protected void implRegister(SelectionKeyImpl ski) { channelArray.Add(ski); - fdMap.put(ski.getSocket(), new MapEntry(ski)); + fdMap.put(((SelChImpl)ski.channel()).getFD().getSocket(), new MapEntry(ski)); keys.add(ski); } - protected void implDereg(SelectionKeyImpl ski) throws IOException - { + protected void implDereg(SelectionKeyImpl ski) throws IOException { channelArray.Remove(ski); - fdMap.remove(ski.getSocket()); + fdMap.remove(((SelChImpl)ski.channel()).getFD().getSocket()); keys.remove(ski); selectedKeys.remove(ski); deregister(ski); + SelectableChannel selch = ski.channel(); - if (!selch.isOpen() && !selch.isRegistered()) - { + if (!selch.isOpen() && !selch.isRegistered()) { ((SelChImpl)selch).kill(); } } - public Selector wakeup() - { - synchronized (interruptLock) - { - if (!interruptTriggered) - { + public Selector wakeup() { + synchronized (interruptLock) { + if (!interruptTriggered) { setWakeupSocket(); interruptTriggered = true; } } + return this; } @@ -305,20 +285,19 @@ private void setWakeupSocket() { // Sets Windows wakeup socket to a non-signaled state. private void resetWakeupSocket() { - synchronized (interruptLock) - { + synchronized (interruptLock) { if (interruptTriggered == false) return; + resetWakeupSocket0(wakeupSourceFd); interruptTriggered = false; } } - private static void resetWakeupSocket0(Socket wakeupSourceFd) - { - while (wakeupSourceFd.get_Available() > 0) - { + private static void resetWakeupSocket0(Socket wakeupSourceFd) { + while (wakeupSourceFd.get_Available() > 0) { wakeupSourceFd.Receive(new byte[1]); } } + } diff --git a/src/IKVM.Java/local/sun/nio/ch/FileChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/FileChannelImpl.java deleted file mode 100644 index 2de2ff9271..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/FileChannelImpl.java +++ /dev/null @@ -1,1154 +0,0 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import cli.Microsoft.Win32.SafeHandles.SafeFileHandle; -import cli.System.IntPtr; -import cli.System.IO.FileStream; -import cli.System.Runtime.InteropServices.DllImportAttribute; -import java.io.FileDescriptor; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.FileLockInterruptionException; -import java.nio.channels.NonReadableChannelException; -import java.nio.channels.NonWritableChannelException; -import java.nio.channels.OverlappingFileLockException; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.security.AccessController; -import java.util.ArrayList; -import java.util.List; - -import sun.misc.Cleaner; -import sun.security.action.GetPropertyAction; - -public class FileChannelImpl - extends FileChannel -{ - private static final boolean win32 = ikvm.internal.Util.WINDOWS; - - // Memory allocation size for mapping buffers - private static final long allocationGranularity = 64 * 1024; // HACK we're using a hard coded value here that works on all mainstream platforms - - // Used to make native read and write calls - private final FileDispatcher nd; - - // File descriptor - private final FileDescriptor fd; - - // File access mode (immutable) - private final boolean writable; - private final boolean readable; - private final boolean append; - - // Required to prevent finalization of creating stream (immutable) - private final Object parent; - - // The path of the referenced file - // (null if the parent stream is created with a file descriptor) - private final String path; - - // Thread-safe set of IDs of native threads, for signalling - private final NativeThreadSet threads = new NativeThreadSet(2); - - // Lock for operations involving position and size - private final Object positionLock = new Object(); - - private FileChannelImpl(FileDescriptor fd, String path, boolean readable, - boolean writable, boolean append, Object parent) - { - this.fd = fd; - this.readable = readable; - this.writable = writable; - this.append = append; - this.parent = parent; - this.path = path; - this.nd = new FileDispatcherImpl(append); - } - - // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel() - public static FileChannel open(FileDescriptor fd, String path, - boolean readable, boolean writable, - Object parent) - { - return new FileChannelImpl(fd, path, readable, writable, false, parent); - } - - // Used by FileOutputStream.getChannel - public static FileChannel open(FileDescriptor fd, String path, - boolean readable, boolean writable, - boolean append, Object parent) - { - return new FileChannelImpl(fd, path, readable, writable, append, parent); - } - - private void ensureOpen() throws IOException { - if (!isOpen()) - throw new ClosedChannelException(); - } - - - // -- Standard channel operations -- - - protected void implCloseChannel() throws IOException { - // Release and invalidate any locks that we still hold - if (fileLockTable != null) { - for (FileLock fl: fileLockTable.removeAll()) { - synchronized (fl) { - if (fl.isValid()) { - nd.release(fd, fl.position(), fl.size()); - ((FileLockImpl)fl).invalidate(); - } - } - } - } - - // signal any threads blocked on this channel - threads.signalAndWait(); - - if (parent != null) { - - // Close the fd via the parent stream's close method. The parent - // will reinvoke our close method, which is defined in the - // superclass AbstractInterruptibleChannel, but the isOpen logic in - // that method will prevent this method from being reinvoked. - // - ((java.io.Closeable)parent).close(); - } else { - nd.close(fd); - } - - } - - public int read(ByteBuffer dst) throws IOException { - ensureOpen(); - if (!readable) - throw new NonReadableChannelException(); - synchronized (positionLock) { - int n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return 0; - do { - n = IOUtil.read(fd, dst, -1, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - } - - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) - throw new IndexOutOfBoundsException(); - ensureOpen(); - if (!readable) - throw new NonReadableChannelException(); - synchronized (positionLock) { - long n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return 0; - do { - n = IOUtil.read(fd, dsts, offset, length, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - } - - public int write(ByteBuffer src) throws IOException { - ensureOpen(); - if (!writable) - throw new NonWritableChannelException(); - synchronized (positionLock) { - int n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return 0; - do { - n = IOUtil.write(fd, src, -1, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - } - - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) - throw new IndexOutOfBoundsException(); - ensureOpen(); - if (!writable) - throw new NonWritableChannelException(); - synchronized (positionLock) { - long n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return 0; - do { - n = IOUtil.write(fd, srcs, offset, length, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - } - - // -- Other operations -- - - public long position() throws IOException { - ensureOpen(); - synchronized (positionLock) { - long p = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return 0; - do { - // in append-mode then position is advanced to end before writing - p = (append) ? nd.size(fd) : position0(fd, -1); - } while ((p == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(p); - } finally { - threads.remove(ti); - end(p > -1); - assert IOStatus.check(p); - } - } - } - - public FileChannel position(long newPosition) throws IOException { - ensureOpen(); - if (newPosition < 0) - throw new IllegalArgumentException(); - synchronized (positionLock) { - long p = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return null; - do { - p = position0(fd, newPosition); - } while ((p == IOStatus.INTERRUPTED) && isOpen()); - return this; - } finally { - threads.remove(ti); - end(p > -1); - assert IOStatus.check(p); - } - } - } - - public long size() throws IOException { - ensureOpen(); - synchronized (positionLock) { - long s = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return -1; - do { - s = nd.size(fd); - } while ((s == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(s); - } finally { - threads.remove(ti); - end(s > -1); - assert IOStatus.check(s); - } - } - } - - public FileChannel truncate(long newSize) throws IOException { - ensureOpen(); - if (newSize < 0) - throw new IllegalArgumentException("Negative size"); - if (!writable) - throw new NonWritableChannelException(); - synchronized (positionLock) { - int rv = -1; - long p = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return null; - - // get current size - long size; - do { - size = nd.size(fd); - } while ((size == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; - - // get current position - do { - p = position0(fd, -1); - } while ((p == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; - assert p >= 0; - - // truncate file if given size is less than the current size - if (newSize < size) { - do { - rv = nd.truncate(fd, newSize); - } while ((rv == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; - } - - // [IKVM] in append mode we're not allowed to seek backwards, but the atomic append will honor the new file size - if (append) - return this; - - // if position is beyond new size then adjust it - if (p > newSize) - p = newSize; - do { - rv = (int)position0(fd, p); - } while ((rv == IOStatus.INTERRUPTED) && isOpen()); - return this; - } finally { - threads.remove(ti); - end(rv > -1); - assert IOStatus.check(rv); - } - } - } - - public void force(boolean metaData) throws IOException { - ensureOpen(); - int rv = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return; - do { - rv = nd.force(fd, metaData); - } while ((rv == IOStatus.INTERRUPTED) && isOpen()); - } finally { - threads.remove(ti); - end(rv > -1); - assert IOStatus.check(rv); - } - } - - private long transferToArbitraryChannel(long position, int icount, - WritableByteChannel target) - throws IOException - { - // Untrusted target: Use a newly-erased buffer - int c = Math.min(icount, TRANSFER_SIZE); - ByteBuffer bb = ByteBuffer.allocate(c); - long tw = 0; // Total bytes written - long pos = position; - try { - while (tw < icount) { - bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE)); - int nr = read(bb, pos); - if (nr <= 0) - break; - bb.flip(); - // ## Bug: Will block writing target if this channel - // ## is asynchronously closed - int nw = target.write(bb); - tw += nw; - if (nw != nr) - break; - pos += nw; - bb.clear(); - } - return tw; - } catch (IOException x) { - if (tw > 0) - return tw; - throw x; - } - } - - public long transferTo(long position, long count, - WritableByteChannel target) - throws IOException - { - ensureOpen(); - if (!target.isOpen()) - throw new ClosedChannelException(); - if (!readable) - throw new NonReadableChannelException(); - if (target instanceof FileChannelImpl && - !((FileChannelImpl)target).writable) - throw new NonWritableChannelException(); - if ((position < 0) || (count < 0)) - throw new IllegalArgumentException(); - long sz = size(); - if (position > sz) - return 0; - int icount = (int)Math.min(count, Integer.MAX_VALUE); - if ((sz - position) < icount) - icount = (int)(sz - position); - - // Slow path for untrusted targets - return transferToArbitraryChannel(position, icount, target); - } - - private long transferFromFileChannel(FileChannelImpl src, - long position, long count) - throws IOException - { - if (!src.readable) - throw new NonReadableChannelException(); - return transferFromArbitraryChannel(src, position, count); - } - - private static final int TRANSFER_SIZE = 8192; - - private long transferFromArbitraryChannel(ReadableByteChannel src, - long position, long count) - throws IOException - { - // Untrusted target: Use a newly-erased buffer - int c = (int)Math.min(count, TRANSFER_SIZE); - ByteBuffer bb = ByteBuffer.allocate(c); - long tw = 0; // Total bytes written - long pos = position; - try { - while (tw < count) { - bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE)); - // ## Bug: Will block reading src if this channel - // ## is asynchronously closed - int nr = src.read(bb); - if (nr <= 0) - break; - bb.flip(); - int nw = write(bb, pos); - tw += nw; - if (nw != nr) - break; - pos += nw; - bb.clear(); - } - return tw; - } catch (IOException x) { - if (tw > 0) - return tw; - throw x; - } - } - - public long transferFrom(ReadableByteChannel src, - long position, long count) - throws IOException - { - ensureOpen(); - if (!src.isOpen()) - throw new ClosedChannelException(); - if (!writable) - throw new NonWritableChannelException(); - if ((position < 0) || (count < 0)) - throw new IllegalArgumentException(); - if (position > size()) - return 0; - if (src instanceof FileChannelImpl) - return transferFromFileChannel((FileChannelImpl)src, - position, count); - - return transferFromArbitraryChannel(src, position, count); - } - - public int read(ByteBuffer dst, long position) throws IOException { - if (dst == null) - throw new NullPointerException(); - if (position < 0) - throw new IllegalArgumentException("Negative position"); - if (!readable) - throw new NonReadableChannelException(); - ensureOpen(); - if (nd.needsPositionLock()) { - synchronized (positionLock) { - return readInternal(dst, position); - } - } else { - return readInternal(dst, position); - } - } - - private int readInternal(ByteBuffer dst, long position) throws IOException { - assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); - int n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return -1; - do { - n = IOUtil.read(fd, dst, position, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - - public int write(ByteBuffer src, long position) throws IOException { - if (src == null) - throw new NullPointerException(); - if (position < 0) - throw new IllegalArgumentException("Negative position"); - if (!writable) - throw new NonWritableChannelException(); - ensureOpen(); - if (nd.needsPositionLock()) { - synchronized (positionLock) { - return writeInternal(src, position); - } - } else { - return writeInternal(src, position); - } - } - - private int writeInternal(ByteBuffer src, long position) throws IOException { - assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); - int n = 0; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return -1; - do { - n = IOUtil.write(fd, src, position, nd); - } while ((n == IOStatus.INTERRUPTED) && isOpen()); - return IOStatus.normalize(n); - } finally { - threads.remove(ti); - end(n > 0); - assert IOStatus.check(n); - } - } - - - // -- Memory-mapped buffers -- - - private static class Unmapper - implements Runnable - { - // may be required to close file - private static final NativeDispatcher nd = new FileDispatcherImpl(); - - // keep track of mapped buffer usage - static volatile int count; - static volatile long totalSize; - static volatile long totalCapacity; - - private volatile long address; - private final long size; - private final int cap; - private final FileDescriptor fd; - - private Unmapper(long address, long size, int cap, - FileDescriptor fd) - { - assert (address != 0); - this.address = address; - this.size = size; - this.cap = cap; - this.fd = fd; - - synchronized (Unmapper.class) { - count++; - totalSize += size; - totalCapacity += cap; - } - } - - public void run() { - if (address == 0) - return; - unmap0(address, size); - address = 0; - - // if this mapping has a valid file descriptor then we close it - if (fd.valid()) { - try { - nd.close(fd); - } catch (IOException ignore) { - // nothing we can do - } - } - - synchronized (Unmapper.class) { - count--; - totalSize -= size; - totalCapacity -= cap; - } - } - } - - private static void unmap(MappedByteBuffer bb) { - Cleaner cl = ((DirectBuffer)bb).cleaner(); - if (cl != null) - cl.clean(); - } - - private static final int MAP_RO = 0; - private static final int MAP_RW = 1; - private static final int MAP_PV = 2; - - public MappedByteBuffer map(MapMode mode, long position, long size) - throws IOException - { - ensureOpen(); - if (mode == null) - throw new NullPointerException("Mode is null"); - if (position < 0L) - throw new IllegalArgumentException("Negative position"); - if (size < 0L) - throw new IllegalArgumentException("Negative size"); - if (position + size < 0) - throw new IllegalArgumentException("Position + size overflow"); - if (size > Integer.MAX_VALUE) - throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE"); - - int imode = -1; - if (mode == MapMode.READ_ONLY) - imode = MAP_RO; - else if (mode == MapMode.READ_WRITE) - imode = MAP_RW; - else if (mode == MapMode.PRIVATE) - imode = MAP_PV; - assert (imode >= 0); - if ((mode != MapMode.READ_ONLY) && !writable) - throw new NonWritableChannelException(); - if (!readable) - throw new NonReadableChannelException(); - - long addr = -1; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return null; - - long filesize; - do { - filesize = nd.size(fd); - } while ((filesize == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; - - if (filesize < position + size) { // Extend file size - if (!writable) { - throw new IOException("Channel not open for writing " + - "- cannot extend file to required size"); - } - int rv; - do { - rv = nd.truncate(fd, position + size); - } while ((rv == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; - } - if (size == 0) { - addr = 0; - // a valid file descriptor is not required - FileDescriptor dummy = new FileDescriptor(); - if ((!writable) || (imode == MAP_RO)) - return Util.newMappedByteBufferR(0, 0, dummy, null); - else - return Util.newMappedByteBuffer(0, 0, dummy, null); - } - - int pagePosition = (int)(position % allocationGranularity); - long mapPosition = position - pagePosition; - long mapSize = size + pagePosition; - try { - // If no exception was thrown from map0, the address is valid - addr = map0(imode, mapPosition, mapSize); - } catch (OutOfMemoryError x) { - // An OutOfMemoryError may indicate that we've exhausted memory - // so force gc and re-attempt map - System.gc(); - try { - Thread.sleep(100); - } catch (InterruptedException y) { - Thread.currentThread().interrupt(); - } - try { - addr = map0(imode, mapPosition, mapSize); - } catch (OutOfMemoryError y) { - // After a second OOME, fail - throw new IOException("Map failed", y); - } - } - - // On Windows, and potentially other platforms, we need an open - // file descriptor for some mapping operations. - FileDescriptor mfd; - try { - mfd = nd.duplicateForMapping(fd); - } catch (IOException ioe) { - unmap0(addr, mapSize); - throw ioe; - } - - assert (IOStatus.checkAll(addr)); - assert (addr % allocationGranularity == 0); - int isize = (int)size; - Unmapper um = new Unmapper(addr, mapSize, isize, mfd); - if ((!writable) || (imode == MAP_RO)) { - return Util.newMappedByteBufferR(isize, - addr + pagePosition, - mfd, - um); - } else { - return Util.newMappedByteBuffer(isize, - addr + pagePosition, - mfd, - um); - } - } finally { - threads.remove(ti); - end(IOStatus.checkAll(addr)); - } - } - - /** - * Invoked by sun.management.ManagementFactoryHelper to create the management - * interface for mapped buffers. - */ - public static sun.misc.JavaNioAccess.BufferPool getMappedBufferPool() { - return new sun.misc.JavaNioAccess.BufferPool() { - @Override - public String getName() { - return "mapped"; - } - @Override - public long getCount() { - return Unmapper.count; - } - @Override - public long getTotalCapacity() { - return Unmapper.totalCapacity; - } - @Override - public long getMemoryUsed() { - return Unmapper.totalSize; - } - }; - } - - // -- Locks -- - - - - // keeps track of locks on this file - private volatile FileLockTable fileLockTable; - - // indicates if file locks are maintained system-wide (as per spec) - private static boolean isSharedFileLockTable; - - // indicates if the disableSystemWideOverlappingFileLockCheck property - // has been checked - private static volatile boolean propertyChecked; - - // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so - // the overlap check wasn't system wide when there were multiple channels to - // the same file. This property is used to get 1.4/5.0 behavior if desired. - private static boolean isSharedFileLockTable() { - if (!propertyChecked) { - synchronized (FileChannelImpl.class) { - if (!propertyChecked) { - String value = AccessController.doPrivileged( - new GetPropertyAction( - "sun.nio.ch.disableSystemWideOverlappingFileLockCheck")); - isSharedFileLockTable = ((value == null) || value.equals("false")); - propertyChecked = true; - } - } - } - return isSharedFileLockTable; - } - - private FileLockTable fileLockTable() throws IOException { - if (fileLockTable == null) { - synchronized (this) { - if (fileLockTable == null) { - if (isSharedFileLockTable()) { - int ti = threads.add(); - try { - ensureOpen(); - fileLockTable = FileLockTable.newSharedFileLockTable(this, fd); - } finally { - threads.remove(ti); - } - } else { - fileLockTable = new SimpleFileLockTable(); - } - } - } - } - return fileLockTable; - } - - public FileLock lock(long position, long size, boolean shared) - throws IOException - { - ensureOpen(); - if (shared && !readable) - throw new NonReadableChannelException(); - if (!shared && !writable) - throw new NonWritableChannelException(); - FileLockImpl fli = new FileLockImpl(this, position, size, shared); - FileLockTable flt = fileLockTable(); - flt.add(fli); - boolean completed = false; - int ti = -1; - try { - begin(); - ti = threads.add(); - if (!isOpen()) - return null; - int n; - do { - n = nd.lock(fd, true, position, size, shared); - } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); - if (isOpen()) { - if (n == FileDispatcher.RET_EX_LOCK) { - assert shared; - FileLockImpl fli2 = new FileLockImpl(this, position, size, - false); - flt.replace(fli, fli2); - fli = fli2; - } - completed = true; - } - } finally { - if (!completed) - flt.remove(fli); - threads.remove(ti); - try { - end(completed); - } catch (ClosedByInterruptException e) { - throw new FileLockInterruptionException(); - } - } - return fli; - } - - public FileLock tryLock(long position, long size, boolean shared) - throws IOException - { - ensureOpen(); - if (shared && !readable) - throw new NonReadableChannelException(); - if (!shared && !writable) - throw new NonWritableChannelException(); - FileLockImpl fli = new FileLockImpl(this, position, size, shared); - FileLockTable flt = fileLockTable(); - flt.add(fli); - int result; - - int ti = threads.add(); - try { - try { - ensureOpen(); - result = nd.lock(fd, false, position, size, shared); - } catch (IOException e) { - flt.remove(fli); - throw e; - } - if (result == FileDispatcher.NO_LOCK) { - flt.remove(fli); - return null; - } - if (result == FileDispatcher.RET_EX_LOCK) { - assert shared; - FileLockImpl fli2 = new FileLockImpl(this, position, size, - false); - flt.replace(fli, fli2); - return fli2; - } - return fli; - } finally { - threads.remove(ti); - } - } - - void release(FileLockImpl fli) throws IOException { - int ti = threads.add(); - try { - ensureOpen(); - nd.release(fd, fli.position(), fli.size()); - } finally { - threads.remove(ti); - } - assert fileLockTable != null; - fileLockTable.remove(fli); - } - - // -- File lock support -- - - /** - * A simple file lock table that maintains a list of FileLocks obtained by a - * FileChannel. Use to get 1.4/5.0 behaviour. - */ - private static class SimpleFileLockTable extends FileLockTable { - // synchronize on list for access - private final List lockList = new ArrayList(2); - - public SimpleFileLockTable() { - } - - private void checkList(long position, long size) - throws OverlappingFileLockException - { - assert Thread.holdsLock(lockList); - for (FileLock fl: lockList) { - if (fl.overlaps(position, size)) { - throw new OverlappingFileLockException(); - } - } - } - - public void add(FileLock fl) throws OverlappingFileLockException { - synchronized (lockList) { - checkList(fl.position(), fl.size()); - lockList.add(fl); - } - } - - public void remove(FileLock fl) { - synchronized (lockList) { - lockList.remove(fl); - } - } - - public List removeAll() { - synchronized(lockList) { - List result = new ArrayList(lockList); - lockList.clear(); - return result; - } - } - - public void replace(FileLock fl1, FileLock fl2) { - synchronized (lockList) { - lockList.remove(fl1); - lockList.add(fl2); - } - } - } - - // -- Native methods -- - - // Creates a new mapping - private long map0(int prot, long position, long length) throws IOException - { - FileStream fs = (FileStream)fd.getStream(); - if (win32) - return mapViewOfFileWin32(fs, prot, position, length); - else - return mapViewOfFilePosix(fs, prot, position, length); - } - - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - private static long mapViewOfFileWin32(FileStream fs, int prot, long position, long length) throws IOException - { - try - { - int PAGE_READONLY = 2; - int PAGE_READWRITE = 4; - int PAGE_WRITECOPY = 8; - - int FILE_MAP_WRITE = 2; - int FILE_MAP_READ = 4; - int FILE_MAP_COPY = 1; - - int fileProtect; - int mapAccess; - - switch (prot) - { - case MAP_RO: - fileProtect = PAGE_READONLY; - mapAccess = FILE_MAP_READ; - break; - case MAP_RW: - fileProtect = PAGE_READWRITE; - mapAccess = FILE_MAP_WRITE; - break; - case MAP_PV: - fileProtect = PAGE_WRITECOPY; - mapAccess = FILE_MAP_COPY; - break; - default: - throw new Error(); - } - - long maxSize = length + position; - SafeFileHandle hFileMapping = CreateFileMapping(fs.get_SafeFileHandle(), IntPtr.Zero, fileProtect, (int)(maxSize >> 32), (int)maxSize, null); - int err = cli.System.Runtime.InteropServices.Marshal.GetLastWin32Error(); - if (hFileMapping.get_IsInvalid()) - { - throw new IOException("Win32 error " + err); - } - IntPtr p = MapViewOfFile(hFileMapping, mapAccess, (int)(position >> 32), (int)position, IntPtr.op_Explicit(length)); - err = cli.System.Runtime.InteropServices.Marshal.GetLastWin32Error(); - hFileMapping.Close(); - // - // In Framework IntPtr extends Object (eventually). In Core it also implements IEquatable. - // IKVM compiler generates a bad IL when being compiled for the Core. It makes a call to IEquatable.Equals. - // The C# compiler generates the Object.Equals call. - // So, when we need to compare to IntPtr objects we do an explicit cast to Object. This way - // the IKVM compiler produces the expected Object.Equals call. - // - // A better way would be to fix this problem inside the code emitter part. But that requires a deeper - // knowledge of IKVM compiler internals. - // - if (p.Equals((cli.System.Object)IntPtr.Zero)) - { - if (err == 8 /*ERROR_NOT_ENOUGH_MEMORY*/) - { - throw new OutOfMemoryError("Map failed"); - } - throw new IOException("Win32 error " + err); - } - cli.System.GC.AddMemoryPressure(length); - return p.ToInt64(); - } - finally - { - cli.System.GC.KeepAlive(fs); - } - } - - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - private static long mapViewOfFilePosix(FileStream fs, int prot, long position, long length) throws IOException - { - byte writeable = prot != MAP_RO ? (byte)1 : (byte)0; - byte copy_on_write = prot == MAP_PV ? (byte)1 : (byte)0; - IntPtr p = ikvm_mmap(fs, writeable, copy_on_write, position, (int)length); - cli.System.GC.KeepAlive(fs); - // HACK ikvm_mmap should really be changed to return a null pointer on failure, - // instead of whatever MAP_FAILED is defined to on the particular system we're running on, - // common values for MAP_FAILED are 0 and -1, so we test for these. - if (p.Equals((cli.System.Object)IntPtr.Zero) || p.Equals((cli.System.Object)(new IntPtr(-1)))) - { - throw new IOException("file mapping failed"); - } - cli.System.GC.AddMemoryPressure(length); - return p.ToInt64(); - } - - @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) - private static native SafeFileHandle CreateFileMapping(SafeFileHandle hFile, IntPtr lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, String lpName); - - @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) - private static native IntPtr MapViewOfFile(SafeFileHandle hFileMapping, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, IntPtr dwNumberOfBytesToMap); - - @DllImportAttribute.Annotation("kernel32") - private static native int UnmapViewOfFile(IntPtr lpBaseAddress); - - @DllImportAttribute.Annotation(value="libc", EntryPoint="munmap") - private static native int munmap(IntPtr address, int size); - - private static native IntPtr ikvm_mmap(FileStream handle, byte writeable, byte copy_on_write, long position, int size); - - // Removes an existing mapping - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - static int unmap0(long address, long length) - { - if (win32) - UnmapViewOfFile(IntPtr.op_Explicit(address)); - else - munmap(IntPtr.op_Explicit(address), (int)length); - cli.System.GC.RemoveMemoryPressure(length); - return 0; - } - - // Sets or reports this file's position - // If offset is -1, the current position is returned - // otherwise the position is set to offset - private static long position0(FileDescriptor fd, long offset) throws IOException - { - if (offset == -1) - { - return fd.getFilePointer(); - } - fd.seek(offset); - return offset; - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/FileDispatcherImpl.java b/src/IKVM.Java/local/sun/nio/ch/FileDispatcherImpl.java index 23bb90c346..1f42736545 100644 --- a/src/IKVM.Java/local/sun/nio/ch/FileDispatcherImpl.java +++ b/src/IKVM.Java/local/sun/nio/ch/FileDispatcherImpl.java @@ -1,43 +1,17 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - package sun.nio.ch; -import java.io.*; -import java.nio.ByteBuffer; -import cli.Microsoft.Win32.SafeHandles.SafeFileHandle; -import cli.System.IntPtr; -import cli.System.IO.FileStream; -import cli.System.Runtime.InteropServices.DllImportAttribute; -import cli.System.Runtime.InteropServices.StructLayoutAttribute; -import cli.System.Runtime.InteropServices.LayoutKind; -import cli.System.Runtime.InteropServices.Marshal; -import static ikvm.internal.Util.WINDOWS; +import java.io.FileDescriptor; +import java.io.IOException; +import java.security.PrivilegedAction; + +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + +class FileDispatcherImpl extends FileDispatcher { + + // set to true if fast file transmission (TransmitFile) is enabled + private static final boolean fastFileTransfer; -class FileDispatcherImpl extends FileDispatcher -{ /** * Indicates if the dispatcher should first advance the file position * to the end of file when writing. @@ -57,250 +31,69 @@ boolean needsPositionLock() { return true; } - int read(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException { - return fd.readBytes(buf, offset, length); - } + native int read(FileDescriptor fd, long address, int len) throws IOException; - int write(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException { - fd.writeBytes(buf, offset, length); - return length; - } + native int pread(FileDescriptor fd, long address, int len, long position) throws IOException; - long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException { - long totalRead = 0; - try - { - for (int i = offset; i < offset + length; i++) - { - int size = bufs[i].remaining(); - if (size > 0) - { - int read = IOUtil.read(fd, bufs[i], -1, this); - if (read < 0) - { - break; - } - totalRead += read; - if (read < size || fd.available() == 0) - { - break; - } - } - } - } - catch (IOException x) - { - if (totalRead == 0) - { - throw x; - } - } - return totalRead; - } + native long readv(FileDescriptor fd, long address, int len) throws IOException; - long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException { - long totalWritten = 0; - try - { - for (int i = offset; i < offset + length; i++) - { - int size = bufs[i].remaining(); - if (size > 0) - { - int written = IOUtil.write(fd, bufs[i], -1, this); - totalWritten += written; - if (written < size) - { - break; - } - } - } - } - catch (IOException x) - { - if (totalWritten == 0) - { - throw x; - } - } - return totalWritten; + int write(FileDescriptor fd, long address, int len) throws IOException { + return write0(fd, address, len, append); } - int force(FileDescriptor fd, boolean metaData) throws IOException { - fd.sync(); - return 0; - } + native int write0(FileDescriptor fd, long address, int len, boolean append) throws IOException; - int truncate(FileDescriptor fd, long size) throws IOException { - if (append) { - // HACK in append mode we're not allowed to truncate, so we try to reopen the file and truncate that - try (FileOutputStream fos = new FileOutputStream(((FileStream)fd.getStream()).get_Name())) { - fos.getFD().setLength(size); - } - } else { - fd.setLength(size); - } - return 0; - } + native int pwrite(FileDescriptor fd, long address, int len, long position) throws IOException; - long size(FileDescriptor fd) throws IOException { - return fd.length(); + long writev(FileDescriptor fd, long address, int len) throws IOException { + return writev0(fd, address, len, append); } - @StructLayoutAttribute.Annotation(LayoutKind.__Enum.Sequential) - private static final class OVERLAPPED extends cli.System.Object - { - IntPtr Internal; - IntPtr InternalHigh; - int OffsetLow; - int OffsetHigh; - IntPtr hEvent; - } + native long writev0(FileDescriptor fd, long address, int len, boolean append) throws IOException; - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - int lock(FileDescriptor fd, boolean blocking, long pos, long size, - boolean shared) throws IOException - { - FileStream fs = (FileStream)fd.getStream(); - if (WINDOWS) - { - int LOCKFILE_FAIL_IMMEDIATELY = 1; - int LOCKFILE_EXCLUSIVE_LOCK = 2; - int ERROR_LOCK_VIOLATION = 33; - int flags = 0; - OVERLAPPED o = new OVERLAPPED(); - o.OffsetLow = (int)pos; - o.OffsetHigh = (int)(pos >> 32); - if (!blocking) - { - flags |= LOCKFILE_FAIL_IMMEDIATELY; - } - if (!shared) - { - flags |= LOCKFILE_EXCLUSIVE_LOCK; - } - int result = LockFileEx(fs.get_SafeFileHandle(), flags, 0, (int)size, (int)(size >> 32), o); - if (result == 0) - { - int error = Marshal.GetLastWin32Error(); - if (!blocking && error == ERROR_LOCK_VIOLATION) - { - return NO_LOCK; - } - throw new IOException("Lock failed"); - } - return LOCKED; - } - else - { - try - { - if (false) throw new cli.System.ArgumentOutOfRangeException(); - for (;;) - { - try - { - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(""); - fs.Lock(pos, size); - return shared ? RET_EX_LOCK : LOCKED; - } - catch (cli.System.IO.IOException x) - { - if (!blocking) - { - return NO_LOCK; - } - cli.System.Threading.Thread.Sleep(100); - } - catch (cli.System.ObjectDisposedException x) - { - throw new IOException(x.getMessage()); - } - } - } - catch (cli.System.ArgumentOutOfRangeException x) - { - throw new IOException(x.getMessage()); - } - } + native int force(FileDescriptor fd, boolean metaData) throws IOException; + + native int truncate(FileDescriptor fd, long size) throws IOException; + + native long size(FileDescriptor fd) throws IOException; + + native int lock(FileDescriptor fd, boolean blocking, long pos, long size, boolean shared) throws IOException; + + native void release(FileDescriptor fd, long pos, long size) throws IOException; + + native void close(FileDescriptor fd) throws IOException; + + native FileDescriptor duplicateForMapping(FileDescriptor fd) throws IOException; + + boolean canTransferToDirectly(java.nio.channels.SelectableChannel sc) { + return fastFileTransfer && sc.isBlocking(); } - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - void release(FileDescriptor fd, long pos, long size) throws IOException { - FileStream fs = (FileStream)fd.getStream(); - if (WINDOWS) - { - int ERROR_NOT_LOCKED = 158; - OVERLAPPED o = new OVERLAPPED(); - o.OffsetLow = (int)pos; - o.OffsetHigh = (int)(pos >> 32); - int result = UnlockFileEx(fs.get_SafeFileHandle(), 0, (int)size, (int)(size >> 32), o); - if (result == 0 && Marshal.GetLastWin32Error() != ERROR_NOT_LOCKED) - { - throw new IOException("Release failed"); - } - } - else - { - try - { - if (false) throw new cli.System.ArgumentOutOfRangeException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.ObjectDisposedException(""); - fs.Unlock(pos, size); - } - catch (cli.System.IO.IOException x) - { - if (!NotLockedHack.isErrorNotLocked(x)) - { - throw new IOException(x.getMessage()); - } - } - catch (cli.System.ArgumentOutOfRangeException - | cli.System.ObjectDisposedException x) - { - throw new IOException(x.getMessage()); - } - } + boolean transferToDirectlyNeedsPositionLock() { + return true; } - static class NotLockedHack { - private static String msg; - static { - try { - File tmp = File.createTempFile("lock", null); - try (FileStream fs = new FileStream(tmp.getPath(), cli.System.IO.FileMode.wrap(cli.System.IO.FileMode.Create))) { - try { - if (false) throw new cli.System.IO.IOException(); - fs.Unlock(0, 1); - } catch (cli.System.IO.IOException x) { - msg = x.get_Message(); - } - } - tmp.delete(); - } catch (Throwable _) { + static boolean isFastFileTransferRequested() { + String fileTransferProp = java.security.AccessController.doPrivileged(new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("jdk.nio.enableFastFileTransfer"); } - } - static boolean isErrorNotLocked(cli.System.IO.IOException x) { - return x.get_Message().equals(msg); - } - } + }); + boolean enable; + if ("".equals(fileTransferProp)) { + enable = true; + } else { + enable = Boolean.parseBoolean(fileTransferProp); + } - void close(FileDescriptor fd) throws IOException { - fd.close(); + return enable; } - FileDescriptor duplicateForMapping(FileDescriptor fd) throws IOException { - // we return a dummy FileDescriptor, because we don't need it for mapping operations - // and we don't want the original to be closed - return new FileDescriptor(); + static { + IOUtil.load(); + fastFileTransfer = isFastFileTransferRequested(); } - @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) - private static native int LockFileEx(SafeFileHandle hFile, int dwFlags, int dwReserved, int nNumberOfBytesToLockLow, int nNumberOfBytesToLockHigh, OVERLAPPED lpOverlapped); - - @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) - private static native int UnlockFileEx(SafeFileHandle hFile, int dwReserved, int nNumberOfBytesToUnlockLow, int nNumberOfBytesToUnlockHigh, OVERLAPPED lpOverlapped); } diff --git a/src/IKVM.Java/local/sun/nio/ch/IOUtil.java b/src/IKVM.Java/local/sun/nio/ch/IOUtil.java deleted file mode 100644 index 19657684fa..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/IOUtil.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.nio.channels.spi.*; - - -/** - * File-descriptor based I/O utilities that are shared by NIO classes. - */ - -public class IOUtil { - - private IOUtil() { } // No instantiation - - static boolean randomBytes(byte[] someBytes) - { - try - { - if (false) throw new cli.System.Security.Cryptography.CryptographicException(); - cli.System.Security.Cryptography.RNGCryptoServiceProvider csp = new cli.System.Security.Cryptography.RNGCryptoServiceProvider(); - csp.GetBytes(someBytes); - return true; - } - catch (cli.System.Security.Cryptography.CryptographicException _) - { - return false; - } - } - - static void configureBlocking(FileDescriptor fd, boolean blocking) throws IOException - { - fd.setSocketBlocking(blocking); - } - - // this is a dummy method to allow us to use unmodified socket channel impls - static int fdVal(FileDescriptor fd) - { - return 0xbadc0de; - } - - static int read(FileDescriptor fd, ByteBuffer dst, long position, - NativeDispatcher nd) - throws IOException - { - if (dst.isReadOnly()) - throw new IllegalArgumentException("Read-only buffer"); - - if (position != -1) - { - long prevpos = fd.getFilePointer(); - try - { - fd.seek(position); - return read(fd, dst, -1, nd); - } - finally - { - fd.seek(prevpos); - } - } - - if (dst.hasArray()) - { - byte[] buf = dst.array(); - int len = nd.read(fd, buf, dst.arrayOffset() + dst.position(), dst.remaining()); - if (len > 0) - { - dst.position(dst.position() + len); - } - return len; - } - else - { - byte[] buf = new byte[dst.remaining()]; - int len = nd.read(fd, buf, 0, buf.length); - if (len > 0) - { - dst.put(buf, 0, len); - } - return len; - } - } - - static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) - throws IOException - { - return nd.read(fd, bufs, offset, length); - } - - static int write(FileDescriptor fd, ByteBuffer src, long position, - NativeDispatcher nd) - throws IOException - { - if (position != -1) - { - long prevpos = fd.getFilePointer(); - try - { - fd.seek(position); - return write(fd, src, -1, nd); - } - finally - { - fd.seek(prevpos); - } - } - - if (src.hasArray()) - { - byte[] buf = src.array(); - int len = nd.write(fd, buf, src.arrayOffset() + src.position(), src.remaining()); - if (len > 0) - { - src.position(src.position() + len); - } - return len; - } - else - { - int pos = src.position(); - byte[] buf = new byte[src.remaining()]; - src.get(buf); - src.position(pos); - int len = nd.write(fd, buf, 0, buf.length); - if (len > 0) - { - src.position(pos + len); - } - return len; - } - } - - static long write(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd) - throws IOException - { - return nd.write(fd, bufs, 0, bufs.length); - } - - static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) - throws IOException - { - return nd.write(fd, bufs, offset, length); - } - - /** - * Used to trigger loading of native libraries - */ - public static void load() { } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/Iocp.java b/src/IKVM.Java/local/sun/nio/ch/Iocp.java deleted file mode 100644 index 79403bff25..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/Iocp.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.nio.channels.*; -import java.nio.channels.spi.AsynchronousChannelProvider; -import java.io.Closeable; -import java.io.IOException; -import java.io.FileDescriptor; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import ikvm.internal.NotYetImplementedError; - -/** - * Windows implementation of AsynchronousChannelGroup encapsulating an I/O - * completion port. - */ - -class Iocp extends AsynchronousChannelGroupImpl { - private static final boolean supportsThreadAgnosticIo; - - // true if port has been closed - private boolean closed; - - // the set of "stale" OVERLAPPED structures. These OVERLAPPED structures - // relate to I/O operations where the completion notification was not - // received in a timely manner after the channel is closed. - private final Set staleIoSet = new HashSet(); - - Iocp(AsynchronousChannelProvider provider, ThreadPool pool) - throws IOException - { - super(provider, pool); - } - - Iocp start() { - return this; - } - - /* - * Channels implements this interface support overlapped I/O and can be - * associated with a completion port. - */ - static interface OverlappedChannel extends Closeable { - /** - * Returns a reference to the pending I/O result. - */ - PendingFuture getByOverlapped(long overlapped); - } - - /** - * Indicates if this operating system supports thread agnostic I/O. - */ - static boolean supportsThreadAgnosticIo() { - return supportsThreadAgnosticIo; - } - - // release all resources - void implClose() { - synchronized (this) { - if (closed) - return; - closed = true; - } - } - - @Override - boolean isEmpty() { - return true; - } - - @Override - final Object attachForeignChannel(final Channel channel, FileDescriptor fdObj) - throws IOException - { - throw new NotYetImplementedError(); - } - - @Override - final void detachForeignChannel(Object key) { - throw new NotYetImplementedError(); - } - - @Override - void closeAllChannels() { - } - - @Override - void executeOnHandlerTask(Runnable task) { - throw new NotYetImplementedError(); - } - - @Override - void shutdownHandlerTasks() { - } - - /** - * The handler for consuming the result of an asynchronous I/O operation. - */ - static interface ResultHandler { - /** - * Invoked if the I/O operation completes successfully. - */ - public void completed(int bytesTransferred, boolean canInvokeDirect); - - /** - * Invoked if the I/O operation fails. - */ - public void failed(int error, IOException ioe); - } - - static { - supportsThreadAgnosticIo = true; - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/NativeDispatcher.java b/src/IKVM.Java/local/sun/nio/ch/NativeDispatcher.java deleted file mode 100644 index 9582e6aa77..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/NativeDispatcher.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.io.*; -import java.nio.ByteBuffer; - -/** - * Allows different platforms to call different native methods - * for read and write operations. - */ - -abstract class NativeDispatcher -{ - abstract int read(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException; - - /** - * Returns {@code true} if pread/pwrite needs to be synchronized with - * position sensitive methods. - */ - boolean needsPositionLock() { - return false; - } - - abstract int write(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException; - - abstract long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException; - - abstract long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException; - - abstract void close(FileDescriptor fd) throws IOException; - - // Prepare the given fd for closing by duping it to a known internal fd - // that's already closed. This is necessary on some operating systems - // (Solaris and Linux) to prevent fd recycling. - // - void preClose(FileDescriptor fd) throws IOException { - // Do nothing by default; this is only needed on Unix - } - -} diff --git a/src/IKVM.Java/local/sun/nio/ch/NativeThread.java b/src/IKVM.Java/local/sun/nio/ch/NativeThread.java new file mode 100644 index 0000000000..319b5420b8 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/NativeThread.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + + +// Signalling operations on native threads + + +class NativeThread { + + static long current() { + // return 0 to ensure that async close of blocking sockets will close + // the underlying socket. + return 0; + } + + static void signal(long nt) { } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/Net.java b/src/IKVM.Java/local/sun/nio/ch/Net.java index 7e3663f3ab..be7950b9a6 100644 --- a/src/IKVM.Java/local/sun/nio/ch/Net.java +++ b/src/IKVM.Java/local/sun/nio/ch/Net.java @@ -50,30 +50,8 @@ public String name() { // set to true if exclusive binding is on for Windows private static final boolean exclusiveBind; - static { - int availLevel = isExclusiveBindAvailable(); - if (availLevel >= 0) { - String exclBindProp = - java.security.AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public String run() { - return System.getProperty( - "sun.net.useExclusiveBind"); - } - }); - if (exclBindProp != null) { - exclusiveBind = exclBindProp.length() == 0 ? - true : Boolean.parseBoolean(exclBindProp); - } else if (availLevel == 1) { - exclusiveBind = true; - } else { - exclusiveBind = false; - } - } else { - exclusiveBind = false; - } - } + // set to true if the fast tcp loopback should be enabled on Windows + private static final boolean fastLoopback; // -- Miscellaneous utilities -- @@ -391,6 +369,23 @@ static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, } } + public static boolean isFastTcpLoopbackRequested() { + String loopbackProp = java.security.AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("jdk.net.useFastTcpLoopback"); + } + }); + boolean enable; + if ("".equals(loopbackProp)) { + enable = true; + } else { + enable = Boolean.parseBoolean(loopbackProp); + } + return enable; + } + // -- Socket operations -- private static native boolean isIPv6Available0(); @@ -413,15 +408,16 @@ static FileDescriptor socket(ProtocolFamily family, boolean stream) throws IOException { boolean preferIPv6 = isIPv6Available() && (family != StandardProtocolFamily.INET); - return socket0(preferIPv6, stream, false); + return socket0(preferIPv6, stream, false, fastLoopback); } static FileDescriptor serverSocket(boolean stream) { - return socket0(isIPv6Available(), stream, true); + return socket0(isIPv6Available(), stream, true, fastLoopback); } // Due to oddities SO_REUSEADDR on windows reuse is ignored - private static native FileDescriptor socket0(boolean preferIPv6, boolean stream, boolean reuse); + private static native FileDescriptor socket0(boolean preferIPv6, boolean stream, boolean reuse, + boolean fastLoopback); public static void bind(FileDescriptor fd, InetAddress addr, int port) throws IOException @@ -634,4 +630,30 @@ static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group POLLCONN = pollconnValue(); } -} \ No newline at end of file + static { + int availLevel = isExclusiveBindAvailable(); + if (availLevel >= 0) { + String exclBindProp = + java.security.AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return System.getProperty( + "sun.net.useExclusiveBind"); + } + }); + if (exclBindProp != null) { + exclusiveBind = exclBindProp.length() == 0 ? + true : Boolean.parseBoolean(exclBindProp); + } else if (availLevel == 1) { + exclusiveBind = true; + } else { + exclusiveBind = false; + } + } else { + exclusiveBind = false; + } + + fastLoopback = isFastTcpLoopbackRequested(); + } +} diff --git a/src/IKVM.Java/local/sun/nio/ch/PipeImpl.java b/src/IKVM.Java/local/sun/nio/ch/PipeImpl.java new file mode 100644 index 0000000000..b86580dcca --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/PipeImpl.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.*; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.util.Random; + + +/** + * A simple Pipe implementation based on a socket connection. + */ + +class PipeImpl + extends Pipe +{ + + // Source and sink channels + private SourceChannel source; + private SinkChannel sink; + + // Random object for handshake values + private static final Random rnd; + + static { + byte[] someBytes = new byte[8]; + boolean resultOK = IOUtil.randomBytes(someBytes); + if (resultOK) { + rnd = new Random(ByteBuffer.wrap(someBytes).getLong()); + } else { + rnd = new Random(); + } + } + + private class Initializer + implements PrivilegedExceptionAction + { + + private final SelectorProvider sp; + + private IOException ioe = null; + + private Initializer(SelectorProvider sp) { + this.sp = sp; + } + + @Override + public Void run() throws IOException { + LoopbackConnector connector = new LoopbackConnector(); + connector.run(); + if (ioe instanceof ClosedByInterruptException) { + ioe = null; + Thread connThread = new Thread(connector) { + @Override + public void interrupt() {} + }; + connThread.start(); + for (;;) { + try { + connThread.join(); + break; + } catch (InterruptedException ex) {} + } + Thread.currentThread().interrupt(); + } + + if (ioe != null) + throw new IOException("Unable to establish loopback connection", ioe); + + return null; + } + + private class LoopbackConnector implements Runnable { + + @Override + public void run() { + ServerSocketChannel ssc = null; + SocketChannel sc1 = null; + SocketChannel sc2 = null; + + try { + // Loopback address + InetAddress lb = InetAddress.getByName("127.0.0.1"); + assert(lb.isLoopbackAddress()); + InetSocketAddress sa = null; + for(;;) { + // Bind ServerSocketChannel to a port on the loopback + // address + if (ssc == null || !ssc.isOpen()) { + ssc = ServerSocketChannel.open(); + ssc.socket().bind(new InetSocketAddress(lb, 0)); + sa = new InetSocketAddress(lb, ssc.socket().getLocalPort()); + } + + // Establish connection (assume connections are eagerly + // accepted) + sc1 = SocketChannel.open(sa); + ByteBuffer bb = ByteBuffer.allocate(8); + long secret = rnd.nextLong(); + bb.putLong(secret).flip(); + sc1.write(bb); + + // Get a connection and verify it is legitimate + sc2 = ssc.accept(); + bb.clear(); + sc2.read(bb); + bb.rewind(); + if (bb.getLong() == secret) + break; + sc2.close(); + sc1.close(); + } + + // Create source and sink channels + source = new SourceChannelImpl(sp, sc1); + sink = new SinkChannelImpl(sp, sc2); + } catch (IOException e) { + try { + if (sc1 != null) + sc1.close(); + if (sc2 != null) + sc2.close(); + } catch (IOException e2) {} + ioe = e; + } finally { + try { + if (ssc != null) + ssc.close(); + } catch (IOException e2) {} + } + } + } + } + + PipeImpl(final SelectorProvider sp) throws IOException { + try { + AccessController.doPrivileged(new Initializer(sp)); + } catch (PrivilegedActionException x) { + throw (IOException)x.getCause(); + } + } + + public SourceChannel source() { + return source; + } + + public SinkChannel sink() { + return sink; + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/PollArrayWrapper.java b/src/IKVM.Java/local/sun/nio/ch/PollArrayWrapper.java new file mode 100644 index 0000000000..f05f2b5cd7 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/PollArrayWrapper.java @@ -0,0 +1,87 @@ +package sun.nio.ch; + +import java.lang.annotation.Native; + +/** + * Manipulates a native array of structs corresponding to (fd, events) pairs. + * + * typedef struct pollfd { + * SOCKET fd; // 4 bytes + * short events; // 2 bytes + * } pollfd_t; + * + * @author Konstantin Kladko + * @author Mike McCloskey + */ + +class PollArrayWrapper { + + private AllocatedNativeObject pollArray; // The fd array + + long pollArrayAddress; // pollArrayAddress + + @Native private static final short FD_OFFSET = 0; // fd offset in pollfd + @Native private static final short EVENT_OFFSET = 4; // events offset in pollfd + + static short SIZE_POLLFD = 8; // sizeof pollfd struct + + private int size; // Size of the pollArray + + PollArrayWrapper(int newSize) { + int allocationSize = newSize * SIZE_POLLFD; + pollArray = new AllocatedNativeObject(allocationSize, true); + pollArrayAddress = pollArray.address(); + this.size = newSize; + } + + // Prepare another pollfd struct for use. + void addEntry(int index, SelectionKeyImpl ski) { + putDescriptor(index, ski.channel.getFDVal()); + } + + // Writes the pollfd entry from the source wrapper at the source index + // over the entry in the target wrapper at the target index. + void replaceEntry(PollArrayWrapper source, int sindex, PollArrayWrapper target, int tindex) { + target.putDescriptor(tindex, source.getDescriptor(sindex)); + target.putEventOps(tindex, source.getEventOps(sindex)); + } + + // Grows the pollfd array to new size + void grow(int newSize) { + PollArrayWrapper temp = new PollArrayWrapper(newSize); + for (int i = 0; i < size; i++) + replaceEntry(this, i, temp, i); + pollArray.free(); + pollArray = temp.pollArray; + this.size = temp.size; + pollArrayAddress = pollArray.address(); + } + + void free() { + pollArray.free(); + } + + // Access methods for fd structures + void putDescriptor(int i, int fd) { + pollArray.putInt(SIZE_POLLFD * i + FD_OFFSET, fd); + } + + void putEventOps(int i, int event) { + pollArray.putShort(SIZE_POLLFD * i + EVENT_OFFSET, (short)event); + } + + int getEventOps(int i) { + return pollArray.getShort(SIZE_POLLFD * i + EVENT_OFFSET); + } + + int getDescriptor(int i) { + return pollArray.getInt(SIZE_POLLFD * i + FD_OFFSET); + } + + // Adds Windows wakeup socket at a given index. + void addWakeupSocket(int fdVal, int index) { + putDescriptor(index, fdVal); + putEventOps(index, Net.POLLIN); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/SelectionKeyImpl.java b/src/IKVM.Java/local/sun/nio/ch/SelectionKeyImpl.java deleted file mode 100644 index 5edc300547..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/SelectionKeyImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright (C) 2002, 2003, 2004, 2005, 2006 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -package sun.nio.ch; - -import java.nio.channels.CancelledKeyException; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.spi.AbstractSelectionKey; - -final class SelectionKeyImpl extends AbstractSelectionKey -{ - final SelChImpl channel; - final SelectorImpl selector; - private final cli.System.Net.Sockets.Socket socket; - private int readyOps; - private volatile int interestOps; - - SelectionKeyImpl(SelChImpl ch, SelectorImpl sel) - { - this.channel = ch; - this.selector = sel; - socket = ch.getFD().getSocket(); - } - - public SelectableChannel channel() - { - return (SelectableChannel)channel; - } - - public int readyOps() - { - if (!isValid()) - throw new CancelledKeyException(); - - return readyOps; - } - - void readyOps(int ops) - { - readyOps = ops; - } - - public synchronized int interestOps() - { - if (!isValid()) - throw new CancelledKeyException(); - - return interestOps; - } - - public synchronized SelectionKey interestOps(int ops) - { - if (!isValid()) - throw new CancelledKeyException(); - - if ((ops & ~channel.validOps()) != 0) - throw new IllegalArgumentException(); - - interestOps = ops; - return this; - } - - public Selector selector() - { - return selector; - } - - cli.System.Net.Sockets.Socket getSocket() - { - return socket; - } - - void nioReadyOps(int ops) - { - readyOps = ops; - } - - int nioReadyOps() - { - return readyOps; - } - - int nioInterestOps() - { - return interestOps; - } - - SelectionKey nioInterestOps(int ops) - { - if ((ops & ~channel().validOps()) != 0) - throw new IllegalArgumentException(); - channel.translateAndSetInterestOps(ops, this); - interestOps = ops; - return this; - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/SinkChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/SinkChannelImpl.java new file mode 100644 index 0000000000..57e88ac38c --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/SinkChannelImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.channels.spi.*; + + +/** + * Pipe.SinkChannel implementation based on socket connection. + */ + +class SinkChannelImpl + extends Pipe.SinkChannel + implements SelChImpl +{ + // The SocketChannel assoicated with this pipe + SocketChannel sc; + + public FileDescriptor getFD() { + return ((SocketChannelImpl)sc).getFD(); + } + + public int getFDVal() { + return ((SocketChannelImpl)sc).getFDVal(); + } + + SinkChannelImpl(SelectorProvider sp, SocketChannel sc) { + super(sp); + this.sc = sc; + } + + protected void implCloseSelectableChannel() throws IOException { + if (!isRegistered()) + kill(); + } + + public void kill() throws IOException { + sc.close(); + } + + protected void implConfigureBlocking(boolean block) throws IOException { + sc.configureBlocking(block); + } + + public boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) + throw new Error("POLLNVAL detected"); + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0)) + newOps |= SelectionKey.OP_WRITE; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + if ((ops & SelectionKey.OP_WRITE) != 0) + ops = Net.POLLOUT; + sk.selector.putEventOps(sk, ops); + } + + public int write(ByteBuffer src) throws IOException { + try { + return sc.write(src); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } + + public long write(ByteBuffer[] srcs) throws IOException { + try { + return sc.write(srcs); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } + + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); + try { + return write(Util.subsequence(srcs, offset, length)); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } +} diff --git a/src/IKVM.Java/local/sun/nio/ch/SocketDispatcher.java b/src/IKVM.Java/local/sun/nio/ch/SocketDispatcher.java index d86c699d90..bfeaf9889f 100644 --- a/src/IKVM.Java/local/sun/nio/ch/SocketDispatcher.java +++ b/src/IKVM.Java/local/sun/nio/ch/SocketDispatcher.java @@ -1,132 +1,23 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - package sun.nio.ch; import java.io.*; -import java.net.SocketException; -import java.net.SocketUtil; -import java.nio.ByteBuffer; - -import cli.System.Net.Sockets.SocketError; -import cli.System.Net.Sockets.SocketFlags; - -/** - * Allows different platforms to call different native methods - * for read and write operations. - */ -class SocketDispatcher extends NativeDispatcher -{ - int read(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException - { - if (length == 0) - return 0; +class SocketDispatcher extends NativeDispatcher { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - int read = fd.getSocket().Receive(buf, offset, length, SocketFlags.wrap(SocketFlags.None)); - return read == 0 ? IOStatus.EOF : read; - } - catch (cli.System.Net.Sockets.SocketException e) - { - switch (e.get_SocketErrorCode().Value) - { - case SocketError.Shutdown: - // the socket was shutdown, so we have to return EOF - return IOStatus.EOF; - case SocketError.WouldBlock: - // nothing to read and would block - return IOStatus.UNAVAILABLE; - default: - throw SocketUtil.convertSocketExceptionToIOException(e); - } - } - catch (cli.System.ObjectDisposedException e) - { - throw new SocketException("Socket is closed."); - } + static { + IOUtil.load(); } - int write(FileDescriptor fd, byte[] buf, int offset, int length) throws IOException - { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - return fd.getSocket().Send(buf, offset, length, SocketFlags.wrap(SocketFlags.None)); - } - catch (cli.System.Net.Sockets.SocketException e) - { - switch (e.get_SocketErrorCode().Value) - { - case SocketError.WouldBlock: - return IOStatus.UNAVAILABLE; - default: - throw SocketUtil.convertSocketExceptionToIOException(e); - } - } - catch (cli.System.ObjectDisposedException e) - { - throw new SocketException("Socket is closed."); - } - } + native int read(FileDescriptor fd, long address, int len) throws IOException; - native long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException; + native long readv(FileDescriptor fd, long address, int len) throws IOException; - native long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length) throws IOException; + native int write(FileDescriptor fd, long address, int len) throws IOException; - void close(FileDescriptor fd) throws IOException - { + native long writev(FileDescriptor fd, long address, int len) throws IOException; - } + native void preClose(FileDescriptor fd) throws IOException; - void preClose(FileDescriptor fd) throws IOException - { - closeImpl(fd); - } - - static void closeImpl(FileDescriptor fd) throws IOException - { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - fd.getSocket().Close(); - } - catch (cli.System.Net.Sockets.SocketException e) - { - throw java.net.SocketUtil.convertSocketExceptionToIOException(e); - } - catch (cli.System.ObjectDisposedException e) - { - throw new java.net.SocketException("Socket is closed"); - } - - } + native void close(FileDescriptor fd) throws IOException; } diff --git a/src/IKVM.Java/local/sun/nio/ch/SourceChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/SourceChannelImpl.java new file mode 100644 index 0000000000..2605d61b47 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/SourceChannelImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.channels.spi.*; + +/** + * Pipe.SourceChannel implementation based on socket connection. + */ + +class SourceChannelImpl + extends Pipe.SourceChannel + implements SelChImpl +{ + // The SocketChannel assoicated with this pipe + SocketChannel sc; + + public FileDescriptor getFD() { + return ((SocketChannelImpl) sc).getFD(); + } + + public int getFDVal() { + return ((SocketChannelImpl) sc).getFDVal(); + } + + SourceChannelImpl(SelectorProvider sp, SocketChannel sc) { + super(sp); + this.sc = sc; + } + + protected void implCloseSelectableChannel() throws IOException { + if (!isRegistered()) + kill(); + } + + public void kill() throws IOException { + sc.close(); + } + + protected void implConfigureBlocking(boolean block) throws IOException { + sc.configureBlocking(block); + } + + public boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) + throw new Error("POLLNVAL detected"); + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0)) + newOps |= SelectionKey.OP_READ; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + if ((ops & SelectionKey.OP_READ) != 0) + ops = Net.POLLIN; + sk.selector.putEventOps(sk, ops); + } + + public int read(ByteBuffer dst) throws IOException { + try { + return sc.read(dst); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } + + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); + try { + return read(Util.subsequence(dsts, offset, length)); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } + + public long read(ByteBuffer[] dsts) throws IOException { + try { + return sc.read(dsts); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/Util.java b/src/IKVM.Java/local/sun/nio/ch/Util.java deleted file mode 100644 index 4522419667..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/Util.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.lang.ref.SoftReference; -import java.lang.reflect.*; -import java.io.IOException; -import java.io.FileDescriptor; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.*; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; -import sun.misc.Unsafe; -import sun.misc.Cleaner; -import sun.security.action.GetPropertyAction; - - -public class Util { - - - // -- Random stuff -- - - static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) { - if ((offset == 0) && (length == bs.length)) - return bs; - int n = length; - ByteBuffer[] bs2 = new ByteBuffer[n]; - for (int i = 0; i < n; i++) - bs2[i] = bs[offset + i]; - return bs2; - } - - static Set ungrowableSet(final Set s) { - return new Set() { - - public int size() { return s.size(); } - public boolean isEmpty() { return s.isEmpty(); } - public boolean contains(Object o) { return s.contains(o); } - public Object[] toArray() { return s.toArray(); } - public T[] toArray(T[] a) { return s.toArray(a); } - public String toString() { return s.toString(); } - public Iterator iterator() { return s.iterator(); } - public boolean equals(Object o) { return s.equals(o); } - public int hashCode() { return s.hashCode(); } - public void clear() { s.clear(); } - public boolean remove(Object o) { return s.remove(o); } - - public boolean containsAll(Collection coll) { - return s.containsAll(coll); - } - public boolean removeAll(Collection coll) { - return s.removeAll(coll); - } - public boolean retainAll(Collection coll) { - return s.retainAll(coll); - } - - public boolean add(E o){ - throw new UnsupportedOperationException(); - } - public boolean addAll(Collection coll) { - throw new UnsupportedOperationException(); - } - - }; - } - - - private static volatile Constructor directByteBufferConstructor = null; - - private static void initDBBConstructor() { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - try { - Class cl = Class.forName("java.nio.DirectByteBuffer"); - Constructor ctor = cl.getDeclaredConstructor( - new Class[] { int.class, - long.class, - FileDescriptor.class, - Runnable.class }); - ctor.setAccessible(true); - directByteBufferConstructor = ctor; - } catch (ClassNotFoundException | - NoSuchMethodException | - IllegalArgumentException | - ClassCastException x) { - throw new InternalError(x); - } - return null; - }}); - } - - static MappedByteBuffer newMappedByteBuffer(int size, long addr, - FileDescriptor fd, - Runnable unmapper) - { - MappedByteBuffer dbb; - if (directByteBufferConstructor == null) - initDBBConstructor(); - try { - dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance( - new Object[] { new Integer(size), - new Long(addr), - fd, - unmapper }); - } catch (InstantiationException | - IllegalAccessException | - InvocationTargetException e) { - throw new InternalError(e); - } - return dbb; - } - - private static volatile Constructor directByteBufferRConstructor = null; - - private static void initDBBRConstructor() { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - try { - Class cl = Class.forName("java.nio.DirectByteBufferR"); - Constructor ctor = cl.getDeclaredConstructor( - new Class[] { int.class, - long.class, - FileDescriptor.class, - Runnable.class }); - ctor.setAccessible(true); - directByteBufferRConstructor = ctor; - } catch (ClassNotFoundException | - NoSuchMethodException | - IllegalArgumentException | - ClassCastException x) { - throw new InternalError(x); - } - return null; - }}); - } - - static MappedByteBuffer newMappedByteBufferR(int size, long addr, - FileDescriptor fd, - Runnable unmapper) - { - MappedByteBuffer dbb; - if (directByteBufferRConstructor == null) - initDBBRConstructor(); - try { - dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance( - new Object[] { new Integer(size), - new Long(addr), - fd, - unmapper }); - } catch (InstantiationException | - IllegalAccessException | - InvocationTargetException e) { - throw new InternalError(e); - } - return dbb; - } - - - // -- Bug compatibility -- - - private static volatile String bugLevel = null; - - static boolean atBugLevel(String bl) { // package-private - if (bugLevel == null) { - if (!sun.misc.VM.isBooted()) - return false; - String value = AccessController.doPrivileged( - new GetPropertyAction("sun.nio.ch.bugLevel")); - bugLevel = (value != null) ? value : ""; - } - return bugLevel.equals(bl); - } - -} diff --git a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java deleted file mode 100644 index a13f65f6a9..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.nio.channels.*; -import java.util.concurrent.*; -import java.nio.ByteBuffer; -import java.nio.BufferOverflowException; -import java.io.IOException; -import java.io.FileDescriptor; -import cli.System.AsyncCallback; -import cli.System.IAsyncResult; -import cli.System.IO.FileStream; -import cli.System.IO.SeekOrigin; - -/** - * Windows implementation of AsynchronousFileChannel using overlapped I/O. - */ - -public class WindowsAsynchronousFileChannelImpl - extends AsynchronousFileChannelImpl - implements Groupable -{ - // error when EOF is detected asynchronously. - private static final int ERROR_HANDLE_EOF = 38; - - // Lazy initialization of default I/O completion port - private static class DefaultIocpHolder { - static final Iocp defaultIocp = defaultIocp(); - private static Iocp defaultIocp() { - try { - return new Iocp(null, ThreadPool.createDefault()).start(); - } catch (IOException ioe) { - throw new InternalError(ioe); - } - } - } - - // Used for force/truncate/size methods - private static final FileDispatcher nd = new FileDispatcherImpl(); - - // I/O completion port (group) - private final Iocp iocp; - - private final boolean isDefaultIocp; - - - private WindowsAsynchronousFileChannelImpl(FileDescriptor fdObj, - boolean reading, - boolean writing, - Iocp iocp, - boolean isDefaultIocp) - throws IOException - { - super(fdObj, reading, writing, iocp.executor()); - this.iocp = iocp; - this.isDefaultIocp = isDefaultIocp; - } - - public static AsynchronousFileChannel open(FileDescriptor fdo, - boolean reading, - boolean writing, - ThreadPool pool) - throws IOException - { - Iocp iocp; - boolean isDefaultIocp; - if (pool == null) { - iocp = DefaultIocpHolder.defaultIocp; - isDefaultIocp = true; - } else { - iocp = new Iocp(null, pool).start(); - isDefaultIocp = false; - } - try { - return new - WindowsAsynchronousFileChannelImpl(fdo, reading, writing, iocp, isDefaultIocp); - } catch (IOException x) { - // error binding to port so need to close it (if created for this channel) - if (!isDefaultIocp) - iocp.implClose(); - throw x; - } - } - - @Override - public void close() throws IOException { - closeLock.writeLock().lock(); - try { - if (closed) - return; // already closed - closed = true; - } finally { - closeLock.writeLock().unlock(); - } - - // invalidate all locks held for this channel - invalidateAllLocks(); - - // close the file - fdObj.close(); - - // for the non-default group close the port - if (!isDefaultIocp) - iocp.detachFromThreadPool(); - } - - @Override - public AsynchronousChannelGroupImpl group() { - return iocp; - } - - /** - * Translates Throwable to IOException - */ - private static IOException toIOException(Throwable x) { - if (x instanceof cli.System.ArgumentException) { - return new IOException(x.getMessage()); - } - if (x instanceof cli.System.IO.IOException) { - return new IOException(x.getMessage()); - } - if (x instanceof IOException) { - if (x instanceof ClosedChannelException) - x = new AsynchronousCloseException(); - return (IOException)x; - } - return new IOException(x); - } - - @Override - public long size() throws IOException { - try { - begin(); - return nd.size(fdObj); - } finally { - end(); - } - } - - @Override - public AsynchronousFileChannel truncate(long size) throws IOException { - if (size < 0) - throw new IllegalArgumentException("Negative size"); - if (!writing) - throw new NonWritableChannelException(); - try { - begin(); - if (size > nd.size(fdObj)) - return this; - nd.truncate(fdObj, size); - } finally { - end(); - } - return this; - } - - @Override - public void force(boolean metaData) throws IOException { - try { - begin(); - nd.force(fdObj, metaData); - } finally { - end(); - } - } - - // -- file locking -- - - /** - * Task that initiates locking operation and handles completion result. - */ - private class LockTask implements Runnable, Iocp.ResultHandler { - private final long position; - private final FileLockImpl fli; - private final PendingFuture result; - - LockTask(long position, - FileLockImpl fli, - PendingFuture result) - { - this.position = position; - this.fli = fli; - this.result = result; - } - - @Override - public void run() { - FileStream fs = (FileStream)fdObj.getStream(); - for (;;) { - try { - begin(); - - try { - if (false) throw new cli.System.IO.IOException(); - fs.Lock(position, fli.size()); - result.setResult(fli); - break; - } catch (cli.System.IO.IOException _) { - // we failed to acquire the lock, try again next iteration - } - } catch (Throwable x) { - // lock failed or channel closed - removeFromFileLockTable(fli); - result.setFailure(toIOException(x)); - } finally { - end(); - } - cli.System.Threading.Thread.Sleep(100); - } - - // invoke completion handler - Invoker.invoke(result); - } - - @Override - public void completed(int bytesTransferred, boolean canInvokeDirect) { - // release waiters and invoke completion handler - result.setResult(fli); - if (canInvokeDirect) { - Invoker.invokeUnchecked(result); - } else { - Invoker.invoke(result); - } - } - - @Override - public void failed(int error, IOException x) { - // lock not acquired so remove from lock table - removeFromFileLockTable(fli); - - // release waiters - if (isOpen()) { - result.setFailure(x); - } else { - result.setFailure(new AsynchronousCloseException()); - } - Invoker.invoke(result); - } - } - - @Override - Future implLock(final long position, - final long size, - final boolean shared, - A attachment, - final CompletionHandler handler) - { - if (shared && !reading) - throw new NonReadableChannelException(); - if (!shared && !writing) - throw new NonWritableChannelException(); - - // add to lock table - FileLockImpl fli = addToFileLockTable(position, size, shared); - if (fli == null) { - Throwable exc = new ClosedChannelException(); - if (handler == null) - return CompletedFuture.withFailure(exc); - Invoker.invoke(this, handler, attachment, null, exc); - return null; - } - - // create Future and task that will be invoked to acquire lock - PendingFuture result = - new PendingFuture(this, handler, attachment); - LockTask lockTask = new LockTask(position, fli, result); - result.setContext(lockTask); - - // initiate I/O - if (false) { - lockTask.run(); - } else { - boolean executed = false; - try { - Invoker.invokeOnThreadInThreadPool(this, lockTask); - executed = true; - } finally { - if (!executed) { - // rollback - removeFromFileLockTable(fli); - } - } - } - return result; - } - - static final int NO_LOCK = -1; // Failed to lock - static final int LOCKED = 0; // Obtained requested lock - - @Override - public FileLock tryLock(long position, long size, boolean shared) - throws IOException - { - if (shared && !reading) - throw new NonReadableChannelException(); - if (!shared && !writing) - throw new NonWritableChannelException(); - - // add to lock table - final FileLockImpl fli = addToFileLockTable(position, size, shared); - if (fli == null) - throw new ClosedChannelException(); - - boolean gotLock = false; - try { - begin(); - // try to acquire the lock - int res; - try { - if (false) throw new cli.System.IO.IOException(); - FileStream fs = (FileStream)fdObj.getStream(); - fs.Lock(position, size); - res = LOCKED; - } catch (cli.System.IO.IOException _) { - res = NO_LOCK; - } - if (res == NO_LOCK) - return null; - gotLock = true; - return fli; - } finally { - if (!gotLock) - removeFromFileLockTable(fli); - end(); - } - } - - @Override - protected void implRelease(FileLockImpl fli) throws IOException { - try { - if (false) throw new cli.System.IO.IOException(); - FileStream fs = (FileStream)fdObj.getStream(); - fs.Unlock(fli.position(), fli.size()); - } catch (cli.System.IO.IOException x) { - if (!FileDispatcherImpl.NotLockedHack.isErrorNotLocked(x)) { - throw new IOException(x.getMessage()); - } - } - } - - /** - * Task that initiates read operation and handles completion result. - */ - private class ReadTask implements Runnable, Iocp.ResultHandler, AsyncCallback.Method { - private final ByteBuffer dst; - private final int pos, rem; // buffer position/remaining - private final long position; // file position - private final PendingFuture result; - - // set to dst if direct; otherwise set to substituted direct buffer - private volatile ByteBuffer buf; - - ReadTask(ByteBuffer dst, - int pos, - int rem, - long position, - PendingFuture result) - { - this.dst = dst; - this.pos = pos; - this.rem = rem; - this.position = position; - this.result = result; - } - - void updatePosition(int bytesTransferred) { - // if the I/O succeeded then adjust buffer position - if (bytesTransferred > 0) { - if (buf == dst) { - try { - dst.position(pos + bytesTransferred); - } catch (IllegalArgumentException x) { - // someone has changed the position; ignore - } - } else { - // had to substitute direct buffer - buf.position(bytesTransferred).flip(); - try { - dst.put(buf); - } catch (BufferOverflowException x) { - // someone has changed the position; ignore - } - } - } - } - - @Override - public void run() { - // Substitute an array backed buffer if not - if (dst.hasArray()) { - buf = dst; - } else { - buf = ByteBuffer.allocate(rem); - } - - try { - begin(); - - // initiate read - FileStream fs = (FileStream)fdObj.getStream(); - fs.Seek(position, SeekOrigin.wrap(SeekOrigin.Begin)); - fs.BeginRead(buf.array(), buf.arrayOffset() + pos, rem, new AsyncCallback(this), null); - return; - - } catch (Throwable x) { - // failed to initiate read - result.setFailure(toIOException(x)); - } finally { - end(); - } - - // invoke completion handler - Invoker.invoke(result); - } - - public void Invoke(IAsyncResult ar) { - try { - FileStream fs = (FileStream)fdObj.getStream(); - completed(fs.EndRead(ar), false); - } catch (Throwable x) { - failed(0, toIOException(x)); - } - } - - /** - * Executed when the I/O has completed - */ - @Override - public void completed(int bytesTransferred, boolean canInvokeDirect) { - updatePosition(bytesTransferred); - - // release waiters and invoke completion handler - result.setResult(bytesTransferred); - if (canInvokeDirect) { - Invoker.invokeUnchecked(result); - } else { - Invoker.invoke(result); - } - } - - @Override - public void failed(int error, IOException x) { - // if EOF detected asynchronously then it is reported as error - if (error == ERROR_HANDLE_EOF) { - completed(-1, false); - } else { - // release waiters - if (isOpen()) { - result.setFailure(x); - } else { - result.setFailure(new AsynchronousCloseException()); - } - Invoker.invoke(result); - } - } - } - - @Override - Future implRead(ByteBuffer dst, - long position, - A attachment, - CompletionHandler handler) - { - if (!reading) - throw new NonReadableChannelException(); - if (position < 0) - throw new IllegalArgumentException("Negative position"); - if (dst.isReadOnly()) - throw new IllegalArgumentException("Read-only buffer"); - - // check if channel is closed - if (!isOpen()) { - Throwable exc = new ClosedChannelException(); - if (handler == null) - return CompletedFuture.withFailure(exc); - Invoker.invoke(this, handler, attachment, null, exc); - return null; - } - - int pos = dst.position(); - int lim = dst.limit(); - assert (pos <= lim); - int rem = (pos <= lim ? lim - pos : 0); - - // no space remaining - if (rem == 0) { - if (handler == null) - return CompletedFuture.withResult(0); - Invoker.invoke(this, handler, attachment, 0, null); - return null; - } - - // create Future and task that initiates read - PendingFuture result = - new PendingFuture(this, handler, attachment); - ReadTask readTask = new ReadTask(dst, pos, rem, position, result); - result.setContext(readTask); - - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - readTask.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, readTask); - } - return result; - } - - /** - * Task that initiates write operation and handles completion result. - */ - private class WriteTask implements Runnable, Iocp.ResultHandler, AsyncCallback.Method { - private final ByteBuffer src; - private final int pos, rem; // buffer position/remaining - private final long position; // file position - private final PendingFuture result; - - // set to src if direct; otherwise set to substituted direct buffer - private volatile ByteBuffer buf; - - WriteTask(ByteBuffer src, - int pos, - int rem, - long position, - PendingFuture result) - { - this.src = src; - this.pos = pos; - this.rem = rem; - this.position = position; - this.result = result; - } - - void updatePosition(int bytesTransferred) { - // if the I/O succeeded then adjust buffer position - if (bytesTransferred > 0) { - try { - src.position(pos + bytesTransferred); - } catch (IllegalArgumentException x) { - // someone has changed the position - } - } - } - - @Override - public void run() { - // Substitute an array backed buffer if not - if (src.hasArray()) { - buf = src; - } else { - buf = ByteBuffer.allocate(rem); - buf.put(src); - buf.flip(); - // temporarily restore position as we don't know how many bytes - // will be written - src.position(pos); - } - - try { - begin(); - - // initiate the write - FileStream fs = (FileStream)fdObj.getStream(); - fs.Seek(position, SeekOrigin.wrap(SeekOrigin.Begin)); - fs.BeginWrite(buf.array(), buf.arrayOffset() + pos, rem, new AsyncCallback(this), null); - return; - - } catch (Throwable x) { - // failed to initiate read: - result.setFailure(toIOException(x)); - - } finally { - end(); - } - - // invoke completion handler - Invoker.invoke(result); - } - - public void Invoke(IAsyncResult ar) { - try { - FileStream fs = (FileStream)fdObj.getStream(); - fs.EndWrite(ar); - completed(rem, false); - } catch (Throwable x) { - failed(0, toIOException(x)); - } - } - - /** - * Executed when the I/O has completed - */ - @Override - public void completed(int bytesTransferred, boolean canInvokeDirect) { - updatePosition(bytesTransferred); - - // release waiters and invoke completion handler - result.setResult(bytesTransferred); - if (canInvokeDirect) { - Invoker.invokeUnchecked(result); - } else { - Invoker.invoke(result); - } - } - - @Override - public void failed(int error, IOException x) { - // release waiters and invoker completion handler - if (isOpen()) { - result.setFailure(x); - } else { - result.setFailure(new AsynchronousCloseException()); - } - Invoker.invoke(result); - } - } - - Future implWrite(ByteBuffer src, - long position, - A attachment, - CompletionHandler handler) - { - if (!writing) - throw new NonWritableChannelException(); - if (position < 0) - throw new IllegalArgumentException("Negative position"); - - // check if channel is closed - if (!isOpen()) { - Throwable exc = new ClosedChannelException(); - if (handler == null) - return CompletedFuture.withFailure(exc); - Invoker.invoke(this, handler, attachment, null, exc); - return null; - } - - int pos = src.position(); - int lim = src.limit(); - assert (pos <= lim); - int rem = (pos <= lim ? lim - pos : 0); - - // nothing to write - if (rem == 0) { - if (handler == null) - return CompletedFuture.withResult(0); - Invoker.invoke(this, handler, attachment, 0, null); - return null; - } - - // create Future and task to initiate write - PendingFuture result = - new PendingFuture(this, handler, attachment); - WriteTask writeTask = new WriteTask(src, pos, rem, position, result); - result.setContext(writeTask); - - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - writeTask.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, writeTask); - } - return result; - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java deleted file mode 100644 index b8cae29687..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.nio.channels.*; -import java.net.InetSocketAddress; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; -import java.io.FileDescriptor; -import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; - -/** - * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O. - */ - -class WindowsAsynchronousServerSocketChannelImpl - extends AsynchronousServerSocketChannelImpl -{ - private final Iocp iocp; - - // flag to indicate that an accept operation is outstanding - private AtomicBoolean accepting = new AtomicBoolean(); - - - WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException { - super(iocp); - - this.iocp = iocp; - } - - @Override - void implClose() throws IOException { - // close socket (which may cause outstanding accept to be aborted). - SocketDispatcher.closeImpl(fd); - } - - @Override - public AsynchronousChannelGroupImpl group() { - return iocp; - } - - /** - * Task to initiate accept operation and to handle result. - */ - private class AcceptTask implements Runnable, Iocp.ResultHandler { - private final WindowsAsynchronousSocketChannelImpl channel; - private final AccessControlContext acc; - private final PendingFuture result; - - AcceptTask(WindowsAsynchronousSocketChannelImpl channel, - AccessControlContext acc, - PendingFuture result) - { - this.channel = channel; - this.acc = acc; - this.result = result; - } - - void enableAccept() { - accepting.set(false); - } - - void closeChildChannel() { - try { - channel.close(); - } catch (IOException ignore) { } - } - - // caller must have acquired read lock for the listener and child channel. - void finishAccept() throws IOException { - /** - * Set local/remote addresses. This is currently very inefficient - * in that it requires 2 calls to getsockname and 2 calls to getpeername. - * (should change this to use GetAcceptExSockaddrs) - */ - updateAcceptContext(fd, channel.fd); - - InetSocketAddress local = Net.localAddress(channel.fd); - final InetSocketAddress remote = Net.remoteAddress(channel.fd); - channel.setConnected(local, remote); - - // permission check (in context of initiating thread) - if (acc != null) { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - SecurityManager sm = System.getSecurityManager(); - sm.checkAccept(remote.getAddress().getHostAddress(), - remote.getPort()); - return null; - } - }, acc); - } - } - - /** - * Initiates the accept operation. - */ - @Override - public void run() { - - try { - // begin usage of listener socket - begin(); - try { - // begin usage of child socket (as it is registered with - // completion port and so may be closed in the event that - // the group is forcefully closed). - channel.begin(); - - synchronized (result) { - - int n = accept0(fd, channel.fd, this); - if (n == IOStatus.UNAVAILABLE) { - return; - } - - // connection accepted immediately - finishAccept(); - - // allow another accept before the result is set - enableAccept(); - result.setResult(channel); - } - } finally { - // end usage on child socket - channel.end(); - } - } catch (Throwable x) { - // failed to initiate accept so release resources - closeChildChannel(); - if (x instanceof ClosedChannelException) - x = new AsynchronousCloseException(); - if (!(x instanceof IOException) && !(x instanceof SecurityException)) - x = new IOException(x); - enableAccept(); - result.setFailure(x); - } finally { - // end of usage of listener socket - end(); - } - - // accept completed immediately but may not have executed on - // initiating thread in which case the operation may have been - // cancelled. - if (result.isCancelled()) { - closeChildChannel(); - } - - // invoke completion handler - Invoker.invokeIndirectly(result); - } - - /** - * Executed when the I/O has completed - */ - @Override - public void completed(int bytesTransferred, boolean canInvokeDirect) { - try { - // connection accept after group has shutdown - if (iocp.isShutdown()) { - throw new IOException(new ShutdownChannelGroupException()); - } - - // finish the accept - try { - begin(); - try { - channel.begin(); - finishAccept(); - } finally { - channel.end(); - } - } finally { - end(); - } - - // allow another accept before the result is set - enableAccept(); - result.setResult(channel); - } catch (Throwable x) { - enableAccept(); - closeChildChannel(); - if (x instanceof ClosedChannelException) - x = new AsynchronousCloseException(); - if (!(x instanceof IOException) && !(x instanceof SecurityException)) - x = new IOException(x); - result.setFailure(x); - } - - // if an async cancel has already cancelled the operation then - // close the new channel so as to free resources - if (result.isCancelled()) { - closeChildChannel(); - } - - // invoke handler (but not directly) - Invoker.invokeIndirectly(result); - } - - @Override - public void failed(int error, IOException x) { - enableAccept(); - closeChildChannel(); - - // release waiters - if (isOpen()) { - result.setFailure(x); - } else { - result.setFailure(new AsynchronousCloseException()); - } - Invoker.invokeIndirectly(result); - } - } - - @Override - Future implAccept(Object attachment, - final CompletionHandler handler) - { - if (!isOpen()) { - Throwable exc = new ClosedChannelException(); - if (handler == null) - return CompletedFuture.withFailure(exc); - Invoker.invokeIndirectly(this, handler, attachment, null, exc); - return null; - } - if (isAcceptKilled()) - throw new RuntimeException("Accept not allowed due to cancellation"); - - // ensure channel is bound to local address - if (localAddress == null) - throw new NotYetBoundException(); - - // create the socket that will be accepted. The creation of the socket - // is enclosed by a begin/end for the listener socket to ensure that - // we check that the listener is open and also to prevent the I/O - // port from being closed as the new socket is registered. - WindowsAsynchronousSocketChannelImpl ch = null; - IOException ioe = null; - try { - begin(); - ch = new WindowsAsynchronousSocketChannelImpl(iocp, false); - } catch (IOException x) { - ioe = x; - } finally { - end(); - } - if (ioe != null) { - if (handler == null) - return CompletedFuture.withFailure(ioe); - Invoker.invokeIndirectly(this, handler, attachment, null, ioe); - return null; - } - - // need calling context when there is security manager as - // permission check may be done in a different thread without - // any application call frames on the stack - AccessControlContext acc = (System.getSecurityManager() == null) ? - null : AccessController.getContext(); - - PendingFuture result = - new PendingFuture(this, handler, attachment); - AcceptTask task = new AcceptTask(ch, acc, result); - result.setContext(task); - - // check and set flag to prevent concurrent accepting - if (!accepting.compareAndSet(false, true)) - throw new AcceptPendingException(); - - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - task.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, task); - } - return result; - } - - // -- Native methods -- - - private static native void initIDs(); - - private static native int accept0(FileDescriptor listenSocket, FileDescriptor acceptSocket, - Iocp.ResultHandler handler) throws IOException; - - private static native void updateAcceptContext(FileDescriptor listenSocket, - FileDescriptor acceptSocket) throws IOException; - - private static native void closesocket0(long socket) throws IOException; - - static { - IOUtil.load(); - initIDs(); - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java deleted file mode 100644 index 5aa53e90ee..0000000000 --- a/src/IKVM.Java/local/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java +++ /dev/null @@ -1,842 +0,0 @@ -/* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.nio.ch; - -import java.nio.channels.*; -import java.nio.ByteBuffer; -import java.nio.BufferOverflowException; -import java.net.*; -import java.util.concurrent.*; -import java.io.FileDescriptor; -import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; - -/** - * Windows implementation of AsynchronousSocketChannel using overlapped I/O. - */ - -class WindowsAsynchronousSocketChannelImpl - extends AsynchronousSocketChannelImpl -{ - // maximum vector size for scatter/gather I/O - private static final int MAX_WSABUF = 16; - - // I/O completion port that the socket is associated with - private final Iocp iocp; - - - WindowsAsynchronousSocketChannelImpl(Iocp iocp, boolean failIfGroupShutdown) - throws IOException - { - super(iocp); - - this.iocp = iocp; - } - - WindowsAsynchronousSocketChannelImpl(Iocp iocp) throws IOException { - this(iocp, true); - } - - @Override - public AsynchronousChannelGroupImpl group() { - return iocp; - } - - // invoked by WindowsAsynchronousServerSocketChannelImpl when new connection - // accept - void setConnected(InetSocketAddress localAddress, - InetSocketAddress remoteAddress) - { - synchronized (stateLock) { - state = ST_CONNECTED; - this.localAddress = localAddress; - this.remoteAddress = remoteAddress; - } - } - - @Override - void implClose() throws IOException { - // close socket (may cause outstanding async I/O operations to fail). - SocketDispatcher.closeImpl(fd); - } - - @Override - public void onCancel(PendingFuture task) { - if (task.getContext() instanceof ConnectTask) - killConnect(); - if (task.getContext() instanceof ReadTask) - killReading(); - if (task.getContext() instanceof WriteTask) - killWriting(); - } - - /** - * Implements the task to initiate a connection and the handler to - * consume the result when the connection is established (or fails). - */ - private class ConnectTask implements Runnable, Iocp.ResultHandler { - private final InetSocketAddress remote; - private final PendingFuture result; - - ConnectTask(InetSocketAddress remote, PendingFuture result) { - this.remote = remote; - this.result = result; - } - - private void closeChannel() { - try { - close(); - } catch (IOException ignore) { } - } - - private IOException toIOException(Throwable x) { - if (x instanceof IOException) { - if (x instanceof ClosedChannelException) - x = new AsynchronousCloseException(); - return (IOException)x; - } - return new IOException(x); - } - - /** - * Invoke after a connection is successfully established. - */ - private void afterConnect() throws IOException { - updateConnectContext(fd); - synchronized (stateLock) { - state = ST_CONNECTED; - remoteAddress = remote; - } - } - - /** - * Task to initiate a connection. - */ - @Override - public void run() { - Throwable exc = null; - try { - begin(); - - // synchronize on result to allow this thread handle the case - // where the connection is established immediately. - synchronized (result) { - // initiate the connection - int n = connect0(fd, Net.isIPv6Available(), remote.getAddress(), - remote.getPort(), this); - if (n == IOStatus.UNAVAILABLE) { - // connection is pending - return; - } - - // connection established immediately - afterConnect(); - result.setResult(null); - } - } catch (Throwable x) { - exc = x; - } finally { - end(); - } - - if (exc != null) { - closeChannel(); - result.setFailure(toIOException(exc)); - } - Invoker.invoke(result); - } - - /** - * Invoked by handler thread when connection established. - */ - @Override - public void completed(int bytesTransferred, boolean canInvokeDirect) { - Throwable exc = null; - try { - begin(); - afterConnect(); - result.setResult(null); - } catch (Throwable x) { - // channel is closed or unable to finish connect - exc = x; - } finally { - end(); - } - - // can't close channel while in begin/end block - if (exc != null) { - closeChannel(); - result.setFailure(toIOException(exc)); - } - - if (canInvokeDirect) { - Invoker.invokeUnchecked(result); - } else { - Invoker.invoke(result); - } - } - - /** - * Invoked by handler thread when failed to establish connection. - */ - @Override - public void failed(int error, IOException x) { - if (isOpen()) { - closeChannel(); - result.setFailure(x); - } else { - result.setFailure(new AsynchronousCloseException()); - } - Invoker.invoke(result); - } - } - - private void doPrivilegedBind(final SocketAddress sa) throws IOException { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws IOException { - bind(sa); - return null; - } - }); - } catch (PrivilegedActionException e) { - throw (IOException) e.getException(); - } - } - - @Override - Future implConnect(SocketAddress remote, - A attachment, - CompletionHandler handler) - { - if (!isOpen()) { - Throwable exc = new ClosedChannelException(); - if (handler == null) - return CompletedFuture.withFailure(exc); - Invoker.invoke(this, handler, attachment, null, exc); - return null; - } - - InetSocketAddress isa = Net.checkAddress(remote); - - // permission check - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); - - // check and update state - // ConnectEx requires the socket to be bound to a local address - IOException bindException = null; - synchronized (stateLock) { - if (state == ST_CONNECTED) - throw new AlreadyConnectedException(); - if (state == ST_PENDING) - throw new ConnectionPendingException(); - if (localAddress == null) { - try { - SocketAddress any = new InetSocketAddress(0); - if (sm == null) { - bind(any); - } else { - doPrivilegedBind(any); - } - } catch (IOException x) { - bindException = x; - } - } - if (bindException == null) - state = ST_PENDING; - } - - // handle bind failure - if (bindException != null) { - try { - close(); - } catch (IOException ignore) { } - if (handler == null) - return CompletedFuture.withFailure(bindException); - Invoker.invoke(this, handler, attachment, null, bindException); - return null; - } - - // setup task - PendingFuture result = - new PendingFuture(this, handler, attachment); - ConnectTask task = new ConnectTask(isa, result); - result.setContext(task); - - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - task.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, task); - } - return result; - } - - /** - * Implements the task to initiate a read and the handler to consume the - * result when the read completes. - */ - private class ReadTask implements Runnable, Iocp.ResultHandler { - private final ByteBuffer[] bufs; - private final int numBufs; - private final boolean scatteringRead; - private final PendingFuture result; - - // set by run method - private ByteBuffer[] shadow; - - ReadTask(ByteBuffer[] bufs, - boolean scatteringRead, - PendingFuture result) - { - this.bufs = bufs; - this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length; - this.scatteringRead = scatteringRead; - this.result = result; - } - - /** - * Invoked prior to read to prepare the WSABUF array. Where necessary, - * it substitutes direct buffers with managed buffers. - */ - void prepareBuffers() { - shadow = new ByteBuffer[numBufs]; - for (int i=0; i= len) { - bytesRead -= len; - int newPosition = pos + len; - try { - nextBuffer.position(newPosition); - } catch (IllegalArgumentException x) { - // position changed by another - } - } else { // Buffers not completely filled - if (bytesRead > 0) { - assert(pos + bytesRead < (long)Integer.MAX_VALUE); - int newPosition = pos + bytesRead; - try { - nextBuffer.position(newPosition); - } catch (IllegalArgumentException x) { - // position changed by another - } - } - break; - } - } - - // Put results from shadow into the slow buffers - for (int i=0; i Future implRead(boolean isScatteringRead, - ByteBuffer dst, - ByteBuffer[] dsts, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler handler) - { - // setup task - PendingFuture result = - new PendingFuture(this, handler, attachment); - ByteBuffer[] bufs; - if (isScatteringRead) { - bufs = dsts; - } else { - bufs = new ByteBuffer[1]; - bufs[0] = dst; - } - final ReadTask readTask = - new ReadTask(bufs, isScatteringRead, result); - result.setContext(readTask); - - // schedule timeout - if (timeout > 0L) { - Future timeoutTask = iocp.schedule(new Runnable() { - public void run() { - readTask.timeout(); - } - }, timeout, unit); - result.setTimeoutTask(timeoutTask); - } - - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - readTask.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, readTask); - } - return result; - } - - /** - * Implements the task to initiate a write and the handler to consume the - * result when the write completes. - */ - private class WriteTask implements Runnable, Iocp.ResultHandler { - private final ByteBuffer[] bufs; - private final int numBufs; - private final boolean gatheringWrite; - private final PendingFuture result; - - // set by run method - private ByteBuffer[] shadow; - - WriteTask(ByteBuffer[] bufs, - boolean gatheringWrite, - PendingFuture result) - { - this.bufs = bufs; - this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length; - this.gatheringWrite = gatheringWrite; - this.result = result; - } - - /** - * Invoked prior to write to prepare the WSABUF array. Where necessary, - * it substitutes direct buffers with managed buffers. - */ - void prepareBuffers() { - shadow = new ByteBuffer[numBufs]; - for (int i=0; i= len) { - bytesWritten -= len; - int newPosition = pos + len; - try { - nextBuffer.position(newPosition); - } catch (IllegalArgumentException x) { - // position changed by someone else - } - } else { // Buffers not completely filled - if (bytesWritten > 0) { - assert(pos + bytesWritten < (long)Integer.MAX_VALUE); - int newPosition = pos + bytesWritten; - try { - nextBuffer.position(newPosition); - } catch (IllegalArgumentException x) { - // position changed by someone else - } - } - break; - } - } - } - - void releaseBuffers() { - } - - @Override - //@SuppressWarnings("unchecked") - public void run() { - boolean prepared = false; - boolean pending = false; - boolean shutdown = false; - - try { - begin(); - - // substitute direct buffers - prepareBuffers(); - prepared = true; - - int n = write0(fd, shadow, this); - if (n == IOStatus.UNAVAILABLE) { - // I/O is pending - pending = true; - return; - } - if (n == IOStatus.EOF) { - // special case for shutdown output - shutdown = true; - throw new ClosedChannelException(); - } - // write completed immediately - updateBuffers(n); - releaseBuffers(); - enableWriting(); - if (gatheringWrite) { - result.setResult((V)Long.valueOf(n)); - } else { - result.setResult((V)Integer.valueOf(n)); - } - } catch (Throwable x) { - // write failed. Enable writing before releasing waiters. - enableWriting(); - if (!shutdown && (x instanceof ClosedChannelException)) - x = new AsynchronousCloseException(); - if (!(x instanceof IOException)) - x = new IOException(x); - result.setFailure(x); - } finally { - // release resources if I/O not pending - if (!pending) { - if (prepared) - releaseBuffers(); - } - end(); - } - - // invoke completion handler - Invoker.invoke(result); - } - - /** - * Executed when the I/O has completed - */ - @Override - @SuppressWarnings("unchecked") - public void completed(int bytesTransferred, boolean canInvokeDirect) { - updateBuffers(bytesTransferred); - - // return direct buffer to cache if substituted - releaseBuffers(); - - // release waiters if not already released by timeout - synchronized (result) { - if (result.isDone()) - return; - enableWriting(); - if (gatheringWrite) { - result.setResult((V)Long.valueOf(bytesTransferred)); - } else { - result.setResult((V)Integer.valueOf(bytesTransferred)); - } - } - if (canInvokeDirect) { - Invoker.invokeUnchecked(result); - } else { - Invoker.invoke(result); - } - } - - @Override - public void failed(int error, IOException x) { - // return direct buffer to cache if substituted - releaseBuffers(); - - // release waiters if not already released by timeout - if (!isOpen()) - x = new AsynchronousCloseException(); - - synchronized (result) { - if (result.isDone()) - return; - enableWriting(); - result.setFailure(x); - } - Invoker.invoke(result); - } - - /** - * Invoked if timeout expires before it is cancelled - */ - void timeout() { - // synchronize on result as the I/O could complete/fail - synchronized (result) { - if (result.isDone()) - return; - - // kill further writing before releasing waiters - enableWriting(true); - result.setFailure(new InterruptedByTimeoutException()); - } - - // invoke handler without any locks - Invoker.invoke(result); - } - } - - @Override - Future implWrite(boolean gatheringWrite, - ByteBuffer src, - ByteBuffer[] srcs, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler handler) - { - // setup task - PendingFuture result = - new PendingFuture(this, handler, attachment); - ByteBuffer[] bufs; - if (gatheringWrite) { - bufs = srcs; - } else { - bufs = new ByteBuffer[1]; - bufs[0] = src; - } - final WriteTask writeTask = - new WriteTask(bufs, gatheringWrite, result); - result.setContext(writeTask); - - // schedule timeout - if (timeout > 0L) { - Future timeoutTask = iocp.schedule(new Runnable() { - public void run() { - writeTask.timeout(); - } - }, timeout, unit); - result.setTimeoutTask(timeoutTask); - } - - // initiate I/O (can only be done from thread in thread pool) - // initiate I/O - if (Iocp.supportsThreadAgnosticIo()) { - writeTask.run(); - } else { - Invoker.invokeOnThreadInThreadPool(this, writeTask); - } - return result; - } - - // -- Native methods -- - - private static native void initIDs(); - - private static native int connect0(FileDescriptor fd, boolean preferIPv6, - InetAddress remote, int remotePort, Iocp.ResultHandler handler) throws IOException; - - private static native void updateConnectContext(FileDescriptor fd) throws IOException; - - private static native int read0(FileDescriptor fd, ByteBuffer[] bufs, Iocp.ResultHandler handler) - throws IOException; - - private static native int write0(FileDescriptor fd, ByteBuffer[] bufs, Iocp.ResultHandler handler) - throws IOException; - - private static native void shutdown0(long socket, int how) throws IOException; - - private static native void closesocket0(long socket) throws IOException; - - static { - IOUtil.load(); - initIDs(); - } -} diff --git a/src/IKVM.Java/local/sun/nio/ch/sctp/SctpChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpChannelImpl.java new file mode 100644 index 0000000000..53ac4846d8 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpChannelImpl.java @@ -0,0 +1,115 @@ +package sun.nio.ch.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.SelectorProvider; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpSocketOption; + +public class SctpChannelImpl extends SctpChannel { + + private static final String message = "SCTP not supported on this platform"; + + public SctpChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public Association association() { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel bindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel unbindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean connect(SocketAddress remote) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean connect(SocketAddress remote, int maxOutStreams, int maxInStreams) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean isConnectionPending() { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean finishConnect() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getRemoteAddresses() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel shutdown() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel setOption(SctpSocketOption name, T value) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + public MessageInfo receive(ByteBuffer dst, T attachment, NotificationHandler handler) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public int send(ByteBuffer src, MessageInfo messageInfo) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/sctp/SctpMultiChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpMultiChannelImpl.java new file mode 100644 index 0000000000..bab2870f5a --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpMultiChannelImpl.java @@ -0,0 +1,101 @@ +package sun.nio.ch.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.SelectorProvider; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.SctpMultiChannel; +import com.sun.nio.sctp.SctpSocketOption; + +public class SctpMultiChannelImpl extends SctpMultiChannel { + + private static final String message = "SCTP not supported on this platform"; + + public SctpMultiChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public Set associations() { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel bind(SocketAddress local, int backlog) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel bindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel unbindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getRemoteAddresses (Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel shutdown(Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name, Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel setOption(SctpSocketOption name, T value, Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + public MessageInfo receive(ByteBuffer buffer, T attachment, NotificationHandler handler) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public int send(ByteBuffer buffer, MessageInfo messageInfo) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel branch(Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/ch/sctp/SctpServerChannelImpl.java b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpServerChannelImpl.java new file mode 100644 index 0000000000..d8b1afbfb4 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/ch/sctp/SctpServerChannelImpl.java @@ -0,0 +1,72 @@ +package sun.nio.ch.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.channels.spi.SelectorProvider; + +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.SctpSocketOption; + +public class SctpServerChannelImpl extends SctpServerChannel { + + private static final String message = "SCTP not supported on this platform"; + + public SctpServerChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel accept() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel bind(SocketAddress local, int backlog) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel bindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel unbindAddress(InetAddress address) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel setOption(SctpSocketOption name, T value) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } + +} diff --git a/src/IKVM.Java/local/sun/nio/cs/StandardCharsets.java b/src/IKVM.Java/local/sun/nio/cs/StandardCharsets.java deleted file mode 100644 index b2f3b1b380..0000000000 --- a/src/IKVM.Java/local/sun/nio/cs/StandardCharsets.java +++ /dev/null @@ -1,715 +0,0 @@ -/* - * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -// -- This file was mechanically generated: Do not edit! -- // - -package sun.nio.cs; - -import java.nio.charset.*; - - -public class StandardCharsets - extends FastCharsetProvider -{ - - static final String[] aliases_US_ASCII = new String[] { - "iso-ir-6", - "ANSI_X3.4-1986", - "ISO_646.irv:1991", - "ASCII", - "ISO646-US", - "us", - "IBM367", - "cp367", - "csASCII", - "default", - "646", - "iso_646.irv:1983", - "ANSI_X3.4-1968", - "ascii7", - }; - - static final String[] aliases_UTF_8 = new String[] { - "UTF8", - "unicode-1-1-utf-8", - }; - - static final String[] aliases_CESU_8 = new String[] { - "CESU8", - "csCESU-8", - }; - - static final String[] aliases_UTF_16 = new String[] { - "UTF_16", - "utf16", - "unicode", - "UnicodeBig", - }; - - static final String[] aliases_UTF_16BE = new String[] { - "UTF_16BE", - "ISO-10646-UCS-2", - "X-UTF-16BE", - "UnicodeBigUnmarked", - }; - - static final String[] aliases_UTF_16LE = new String[] { - "UTF_16LE", - "X-UTF-16LE", - "UnicodeLittleUnmarked", - }; - - static final String[] aliases_UTF_16LE_BOM = new String[] { - "UnicodeLittle", - }; - - static final String[] aliases_UTF_32 = new String[] { - "UTF_32", - "UTF32", - }; - - static final String[] aliases_UTF_32LE = new String[] { - "UTF_32LE", - "X-UTF-32LE", - }; - - static final String[] aliases_UTF_32BE = new String[] { - "UTF_32BE", - "X-UTF-32BE", - }; - - static final String[] aliases_UTF_32LE_BOM = new String[] { - "UTF_32LE_BOM", - "UTF-32LE-BOM", - }; - - static final String[] aliases_UTF_32BE_BOM = new String[] { - "UTF_32BE_BOM", - "UTF-32BE-BOM", - }; - - static final String[] aliases_ISO_8859_1 = new String[] { - "iso-ir-100", - "ISO_8859-1", - "latin1", - "l1", - "IBM819", - "cp819", - "csISOLatin1", - "819", - "IBM-819", - "ISO8859_1", - "ISO_8859-1:1987", - "ISO_8859_1", - "8859_1", - "ISO8859-1", - }; - - static final String[] aliases_ISO_8859_2 = new String[] { - "iso8859_2", - "8859_2", - "iso-ir-101", - "ISO_8859-2", - "ISO_8859-2:1987", - "ISO8859-2", - "latin2", - "l2", - "ibm912", - "ibm-912", - "cp912", - "912", - "csISOLatin2", - }; - - static final String[] aliases_ISO_8859_4 = new String[] { - "iso8859_4", - "iso8859-4", - "8859_4", - "iso-ir-110", - "ISO_8859-4", - "ISO_8859-4:1988", - "latin4", - "l4", - "ibm914", - "ibm-914", - "cp914", - "914", - "csISOLatin4", - }; - - static final String[] aliases_ISO_8859_5 = new String[] { - "iso8859_5", - "8859_5", - "iso-ir-144", - "ISO_8859-5", - "ISO_8859-5:1988", - "ISO8859-5", - "cyrillic", - "ibm915", - "ibm-915", - "cp915", - "915", - "csISOLatinCyrillic", - }; - - static final String[] aliases_ISO_8859_7 = new String[] { - "iso8859_7", - "8859_7", - "iso-ir-126", - "ISO_8859-7", - "ISO_8859-7:1987", - "ELOT_928", - "ECMA-118", - "greek", - "greek8", - "csISOLatinGreek", - "sun_eu_greek", - "ibm813", - "ibm-813", - "813", - "cp813", - "iso8859-7", - }; - - static final String[] aliases_ISO_8859_9 = new String[] { - "iso8859_9", - "8859_9", - "iso-ir-148", - "ISO_8859-9", - "ISO_8859-9:1989", - "ISO8859-9", - "latin5", - "l5", - "ibm920", - "ibm-920", - "920", - "cp920", - "csISOLatin5", - }; - - static final String[] aliases_ISO_8859_13 = new String[] { - "iso8859_13", - "8859_13", - "iso_8859-13", - "ISO8859-13", - }; - - static final String[] aliases_ISO_8859_15 = new String[] { - "ISO_8859-15", - "8859_15", - "ISO-8859-15", - "ISO8859_15", - "ISO8859-15", - "IBM923", - "IBM-923", - "cp923", - "923", - "LATIN0", - "LATIN9", - "L9", - "csISOlatin0", - "csISOlatin9", - "ISO8859_15_FDIS", - }; - - static final String[] aliases_KOI8_R = new String[] { - "koi8_r", - "koi8", - "cskoi8r", - }; - - static final String[] aliases_KOI8_U = new String[] { - "koi8_u", - }; - - static final String[] aliases_MS1250 = new String[] { - "cp1250", - "cp5346", - }; - - static final String[] aliases_MS1251 = new String[] { - "cp1251", - "cp5347", - "ansi-1251", - }; - - static final String[] aliases_MS1252 = new String[] { - "cp1252", - "cp5348", - }; - - static final String[] aliases_MS1253 = new String[] { - "cp1253", - "cp5349", - }; - - static final String[] aliases_MS1254 = new String[] { - "cp1254", - "cp5350", - }; - - static final String[] aliases_MS1257 = new String[] { - "cp1257", - "cp5353", - }; - - static final String[] aliases_IBM437 = new String[] { - "cp437", - "ibm437", - "ibm-437", - "437", - "cspc8codepage437", - "windows-437", - }; - - static final String[] aliases_IBM737 = new String[] { - "cp737", - "ibm737", - "ibm-737", - "737", - }; - - static final String[] aliases_IBM775 = new String[] { - "cp775", - "ibm775", - "ibm-775", - "775", - }; - - static final String[] aliases_IBM850 = new String[] { - "cp850", - "ibm-850", - "ibm850", - "850", - "cspc850multilingual", - }; - - static final String[] aliases_IBM852 = new String[] { - "cp852", - "ibm852", - "ibm-852", - "852", - "csPCp852", - }; - - static final String[] aliases_IBM855 = new String[] { - "cp855", - "ibm-855", - "ibm855", - "855", - "cspcp855", - }; - - static final String[] aliases_IBM857 = new String[] { - "cp857", - "ibm857", - "ibm-857", - "857", - "csIBM857", - }; - - static final String[] aliases_IBM858 = new String[] { - "cp858", - "ccsid00858", - "cp00858", - "858", - "PC-Multilingual-850+euro", - }; - - static final String[] aliases_IBM862 = new String[] { - "cp862", - "ibm862", - "ibm-862", - "862", - "csIBM862", - "cspc862latinhebrew", - }; - - static final String[] aliases_IBM866 = new String[] { - "cp866", - "ibm866", - "ibm-866", - "866", - "csIBM866", - }; - - static final String[] aliases_IBM874 = new String[] { - "cp874", - "ibm874", - "ibm-874", - "874", - }; - - private static final class Aliases - extends sun.util.PreHashedMap - { - - private static final int ROWS = 1024; - private static final int SIZE = 211; - private static final int SHIFT = 0; - private static final int MASK = 0x3ff; - - private Aliases() { - super(ROWS, SIZE, SHIFT, MASK); - } - - protected void init(Object[] ht) { - ht[1] = new Object[] { "csisolatin0", "iso-8859-15" }; - ht[2] = new Object[] { "csisolatin1", "iso-8859-1" }; - ht[3] = new Object[] { "csisolatin2", "iso-8859-2" }; - ht[5] = new Object[] { "csisolatin4", "iso-8859-4" }; - ht[6] = new Object[] { "csisolatin5", "iso-8859-9" }; - ht[10] = new Object[] { "csisolatin9", "iso-8859-15" }; - ht[19] = new Object[] { "unicodelittle", "x-utf-16le-bom" }; - ht[24] = new Object[] { "iso646-us", "us-ascii" }; - ht[25] = new Object[] { "iso_8859-7:1987", "iso-8859-7" }; - ht[26] = new Object[] { "912", "iso-8859-2" }; - ht[28] = new Object[] { "914", "iso-8859-4" }; - ht[29] = new Object[] { "915", "iso-8859-5" }; - ht[55] = new Object[] { "920", "iso-8859-9" }; - ht[58] = new Object[] { "923", "iso-8859-15" }; - ht[86] = new Object[] { "csisolatincyrillic", "iso-8859-5", - new Object[] { "8859_1", "iso-8859-1" } }; - ht[87] = new Object[] { "8859_2", "iso-8859-2" }; - ht[89] = new Object[] { "8859_4", "iso-8859-4" }; - ht[90] = new Object[] { "813", "iso-8859-7", - new Object[] { "8859_5", "iso-8859-5" } }; - ht[92] = new Object[] { "8859_7", "iso-8859-7" }; - ht[94] = new Object[] { "8859_9", "iso-8859-9" }; - ht[95] = new Object[] { "iso_8859-1:1987", "iso-8859-1" }; - ht[96] = new Object[] { "819", "iso-8859-1" }; - ht[106] = new Object[] { "unicode-1-1-utf-8", "utf-8" }; - ht[121] = new Object[] { "x-utf-16le", "utf-16le" }; - ht[125] = new Object[] { "ecma-118", "iso-8859-7" }; - ht[134] = new Object[] { "koi8_r", "koi8-r" }; - ht[137] = new Object[] { "koi8_u", "koi8-u" }; - ht[141] = new Object[] { "cp912", "iso-8859-2" }; - ht[143] = new Object[] { "cp914", "iso-8859-4" }; - ht[144] = new Object[] { "cp915", "iso-8859-5" }; - ht[170] = new Object[] { "cp920", "iso-8859-9" }; - ht[173] = new Object[] { "cp923", "iso-8859-15" }; - ht[177] = new Object[] { "utf_32le_bom", "x-utf-32le-bom" }; - ht[192] = new Object[] { "utf_16be", "utf-16be" }; - ht[199] = new Object[] { "cspc8codepage437", "ibm437", - new Object[] { "ansi-1251", "windows-1251" } }; - ht[205] = new Object[] { "cp813", "iso-8859-7" }; - ht[211] = new Object[] { "850", "ibm850", - new Object[] { "cp819", "iso-8859-1" } }; - ht[213] = new Object[] { "852", "ibm852" }; - ht[216] = new Object[] { "855", "ibm855" }; - ht[218] = new Object[] { "857", "ibm857", - new Object[] { "iso-ir-6", "us-ascii" } }; - ht[219] = new Object[] { "858", "ibm00858", - new Object[] { "737", "x-ibm737" } }; - ht[225] = new Object[] { "csascii", "us-ascii" }; - ht[244] = new Object[] { "862", "ibm862" }; - ht[248] = new Object[] { "866", "ibm866" }; - ht[253] = new Object[] { "x-utf-32be", "utf-32be" }; - ht[254] = new Object[] { "iso_8859-2:1987", "iso-8859-2" }; - ht[259] = new Object[] { "unicodebig", "utf-16" }; - ht[269] = new Object[] { "iso8859_15_fdis", "iso-8859-15" }; - ht[277] = new Object[] { "874", "x-ibm874" }; - ht[280] = new Object[] { "unicodelittleunmarked", "utf-16le" }; - ht[283] = new Object[] { "iso8859_1", "iso-8859-1" }; - ht[284] = new Object[] { "iso8859_2", "iso-8859-2" }; - ht[286] = new Object[] { "iso8859_4", "iso-8859-4" }; - ht[287] = new Object[] { "iso8859_5", "iso-8859-5" }; - ht[289] = new Object[] { "iso8859_7", "iso-8859-7" }; - ht[291] = new Object[] { "iso8859_9", "iso-8859-9" }; - ht[294] = new Object[] { "ibm912", "iso-8859-2" }; - ht[296] = new Object[] { "ibm914", "iso-8859-4" }; - ht[297] = new Object[] { "ibm915", "iso-8859-5" }; - ht[305] = new Object[] { "iso_8859-13", "iso-8859-13" }; - ht[307] = new Object[] { "iso_8859-15", "iso-8859-15" }; - ht[312] = new Object[] { "greek8", "iso-8859-7", - new Object[] { "646", "us-ascii" } }; - ht[321] = new Object[] { "ibm-912", "iso-8859-2" }; - ht[323] = new Object[] { "ibm920", "iso-8859-9", - new Object[] { "ibm-914", "iso-8859-4" } }; - ht[324] = new Object[] { "ibm-915", "iso-8859-5" }; - ht[325] = new Object[] { "l1", "iso-8859-1" }; - ht[326] = new Object[] { "cp850", "ibm850", - new Object[] { "ibm923", "iso-8859-15", - new Object[] { "l2", "iso-8859-2" } } }; - ht[327] = new Object[] { "cyrillic", "iso-8859-5" }; - ht[328] = new Object[] { "cp852", "ibm852", - new Object[] { "l4", "iso-8859-4" } }; - ht[329] = new Object[] { "l5", "iso-8859-9" }; - ht[331] = new Object[] { "cp855", "ibm855" }; - ht[333] = new Object[] { "cp857", "ibm857", - new Object[] { "l9", "iso-8859-15" } }; - ht[334] = new Object[] { "cp858", "ibm00858", - new Object[] { "cp737", "x-ibm737" } }; - ht[336] = new Object[] { "iso_8859_1", "iso-8859-1" }; - ht[339] = new Object[] { "koi8", "koi8-r" }; - ht[341] = new Object[] { "775", "ibm775" }; - ht[345] = new Object[] { "iso_8859-9:1989", "iso-8859-9" }; - ht[350] = new Object[] { "ibm-920", "iso-8859-9" }; - ht[353] = new Object[] { "ibm-923", "iso-8859-15" }; - ht[358] = new Object[] { "ibm813", "iso-8859-7" }; - ht[359] = new Object[] { "cp862", "ibm862" }; - ht[363] = new Object[] { "cp866", "ibm866" }; - ht[364] = new Object[] { "ibm819", "iso-8859-1" }; - ht[378] = new Object[] { "ansi_x3.4-1968", "us-ascii" }; - ht[385] = new Object[] { "ibm-813", "iso-8859-7" }; - ht[391] = new Object[] { "ibm-819", "iso-8859-1" }; - ht[392] = new Object[] { "cp874", "x-ibm874" }; - ht[405] = new Object[] { "iso-ir-100", "iso-8859-1" }; - ht[406] = new Object[] { "iso-ir-101", "iso-8859-2" }; - ht[408] = new Object[] { "437", "ibm437" }; - ht[421] = new Object[] { "iso-8859-15", "iso-8859-15" }; - ht[428] = new Object[] { "latin0", "iso-8859-15" }; - ht[429] = new Object[] { "latin1", "iso-8859-1" }; - ht[430] = new Object[] { "latin2", "iso-8859-2" }; - ht[432] = new Object[] { "latin4", "iso-8859-4" }; - ht[433] = new Object[] { "latin5", "iso-8859-9" }; - ht[436] = new Object[] { "iso-ir-110", "iso-8859-4" }; - ht[437] = new Object[] { "latin9", "iso-8859-15" }; - ht[438] = new Object[] { "ansi_x3.4-1986", "us-ascii" }; - ht[443] = new Object[] { "utf-32be-bom", "x-utf-32be-bom" }; - ht[456] = new Object[] { "cp775", "ibm775" }; - ht[473] = new Object[] { "iso-ir-126", "iso-8859-7" }; - ht[479] = new Object[] { "ibm850", "ibm850" }; - ht[481] = new Object[] { "ibm852", "ibm852" }; - ht[484] = new Object[] { "ibm855", "ibm855" }; - ht[486] = new Object[] { "ibm857", "ibm857" }; - ht[487] = new Object[] { "ibm737", "x-ibm737" }; - ht[502] = new Object[] { "utf_16le", "utf-16le" }; - ht[506] = new Object[] { "ibm-850", "ibm850" }; - ht[508] = new Object[] { "ibm-852", "ibm852" }; - ht[511] = new Object[] { "ibm-855", "ibm855" }; - ht[512] = new Object[] { "ibm862", "ibm862" }; - ht[513] = new Object[] { "ibm-857", "ibm857" }; - ht[514] = new Object[] { "ibm-737", "x-ibm737" }; - ht[516] = new Object[] { "ibm866", "ibm866" }; - ht[520] = new Object[] { "unicodebigunmarked", "utf-16be" }; - ht[523] = new Object[] { "cp437", "ibm437" }; - ht[524] = new Object[] { "utf16", "utf-16" }; - ht[533] = new Object[] { "iso-ir-144", "iso-8859-5" }; - ht[537] = new Object[] { "iso-ir-148", "iso-8859-9" }; - ht[539] = new Object[] { "ibm-862", "ibm862" }; - ht[543] = new Object[] { "ibm-866", "ibm866" }; - ht[545] = new Object[] { "ibm874", "x-ibm874" }; - ht[563] = new Object[] { "x-utf-32le", "utf-32le" }; - ht[572] = new Object[] { "ibm-874", "x-ibm874" }; - ht[573] = new Object[] { "iso_8859-4:1988", "iso-8859-4" }; - ht[577] = new Object[] { "default", "us-ascii" }; - ht[582] = new Object[] { "utf32", "utf-32" }; - ht[583] = new Object[] { "pc-multilingual-850+euro", "ibm00858" }; - ht[588] = new Object[] { "elot_928", "iso-8859-7" }; - ht[593] = new Object[] { "csisolatingreek", "iso-8859-7" }; - ht[598] = new Object[] { "csibm857", "ibm857" }; - ht[609] = new Object[] { "ibm775", "ibm775" }; - ht[617] = new Object[] { "cp1250", "windows-1250" }; - ht[618] = new Object[] { "cp1251", "windows-1251" }; - ht[619] = new Object[] { "cp1252", "windows-1252" }; - ht[620] = new Object[] { "cp1253", "windows-1253" }; - ht[621] = new Object[] { "cp1254", "windows-1254" }; - ht[624] = new Object[] { "csibm862", "ibm862", - new Object[] { "cp1257", "windows-1257" } }; - ht[628] = new Object[] { "csibm866", "ibm866", - new Object[] { "cesu8", "cesu-8" } }; - ht[632] = new Object[] { "iso8859_13", "iso-8859-13" }; - ht[634] = new Object[] { "iso8859_15", "iso-8859-15", - new Object[] { "utf_32be", "utf-32be" } }; - ht[635] = new Object[] { "utf_32be_bom", "x-utf-32be-bom" }; - ht[636] = new Object[] { "ibm-775", "ibm775" }; - ht[654] = new Object[] { "cp00858", "ibm00858" }; - ht[669] = new Object[] { "8859_13", "iso-8859-13" }; - ht[670] = new Object[] { "us", "us-ascii" }; - ht[671] = new Object[] { "8859_15", "iso-8859-15" }; - ht[676] = new Object[] { "ibm437", "ibm437" }; - ht[679] = new Object[] { "cp367", "us-ascii" }; - ht[686] = new Object[] { "iso-10646-ucs-2", "utf-16be" }; - ht[703] = new Object[] { "ibm-437", "ibm437" }; - ht[710] = new Object[] { "iso8859-13", "iso-8859-13" }; - ht[712] = new Object[] { "iso8859-15", "iso-8859-15" }; - ht[732] = new Object[] { "iso_8859-5:1988", "iso-8859-5" }; - ht[733] = new Object[] { "unicode", "utf-16" }; - ht[768] = new Object[] { "greek", "iso-8859-7" }; - ht[774] = new Object[] { "ascii7", "us-ascii" }; - ht[781] = new Object[] { "iso8859-1", "iso-8859-1" }; - ht[782] = new Object[] { "iso8859-2", "iso-8859-2" }; - ht[783] = new Object[] { "cskoi8r", "koi8-r" }; - ht[784] = new Object[] { "iso8859-4", "iso-8859-4" }; - ht[785] = new Object[] { "iso8859-5", "iso-8859-5" }; - ht[787] = new Object[] { "iso8859-7", "iso-8859-7" }; - ht[789] = new Object[] { "iso8859-9", "iso-8859-9" }; - ht[813] = new Object[] { "ccsid00858", "ibm00858" }; - ht[818] = new Object[] { "cspc862latinhebrew", "ibm862" }; - ht[832] = new Object[] { "ibm367", "us-ascii" }; - ht[834] = new Object[] { "iso_8859-1", "iso-8859-1" }; - ht[835] = new Object[] { "iso_8859-2", "iso-8859-2", - new Object[] { "x-utf-16be", "utf-16be" } }; - ht[836] = new Object[] { "sun_eu_greek", "iso-8859-7" }; - ht[837] = new Object[] { "iso_8859-4", "iso-8859-4" }; - ht[838] = new Object[] { "iso_8859-5", "iso-8859-5" }; - ht[840] = new Object[] { "cspcp852", "ibm852", - new Object[] { "iso_8859-7", "iso-8859-7" } }; - ht[842] = new Object[] { "iso_8859-9", "iso-8859-9" }; - ht[843] = new Object[] { "cspcp855", "ibm855" }; - ht[846] = new Object[] { "windows-437", "ibm437" }; - ht[849] = new Object[] { "ascii", "us-ascii" }; - ht[863] = new Object[] { "cscesu-8", "cesu-8" }; - ht[881] = new Object[] { "utf8", "utf-8" }; - ht[896] = new Object[] { "iso_646.irv:1983", "us-ascii" }; - ht[909] = new Object[] { "cp5346", "windows-1250" }; - ht[910] = new Object[] { "cp5347", "windows-1251" }; - ht[911] = new Object[] { "cp5348", "windows-1252" }; - ht[912] = new Object[] { "cp5349", "windows-1253" }; - ht[925] = new Object[] { "iso_646.irv:1991", "us-ascii" }; - ht[934] = new Object[] { "cp5350", "windows-1254" }; - ht[937] = new Object[] { "cp5353", "windows-1257" }; - ht[944] = new Object[] { "utf_32le", "utf-32le" }; - ht[957] = new Object[] { "utf_16", "utf-16" }; - ht[993] = new Object[] { "cspc850multilingual", "ibm850" }; - ht[1009] = new Object[] { "utf-32le-bom", "x-utf-32le-bom" }; - ht[1015] = new Object[] { "utf_32", "utf-32" }; - } - - } - - private static final class Classes - extends sun.util.PreHashedMap - { - - private static final int ROWS = 32; - private static final int SIZE = 39; - private static final int SHIFT = 1; - private static final int MASK = 0x1f; - - private Classes() { - super(ROWS, SIZE, SHIFT, MASK); - } - - protected void init(Object[] ht) { - ht[0] = new Object[] { "ibm862", "IBM862" }; - ht[2] = new Object[] { "ibm866", "IBM866", - new Object[] { "utf-32", "UTF_32", - new Object[] { "utf-16le", "UTF_16LE" } } }; - ht[3] = new Object[] { "windows-1251", "MS1251", - new Object[] { "windows-1250", "MS1250" } }; - ht[4] = new Object[] { "windows-1253", "MS1253", - new Object[] { "windows-1252", "MS1252", - new Object[] { "utf-32be", "UTF_32BE" } } }; - ht[5] = new Object[] { "windows-1254", "MS1254", - new Object[] { "utf-16", "UTF_16" } }; - ht[6] = new Object[] { "windows-1257", "MS1257" }; - ht[7] = new Object[] { "utf-16be", "UTF_16BE" }; - ht[8] = new Object[] { "iso-8859-2", "ISO_8859_2", - new Object[] { "iso-8859-1", "ISO_8859_1" } }; - ht[9] = new Object[] { "iso-8859-4", "ISO_8859_4", - new Object[] { "utf-8", "UTF_8" } }; - ht[10] = new Object[] { "iso-8859-5", "ISO_8859_5" }; - ht[11] = new Object[] { "x-ibm874", "IBM874", - new Object[] { "iso-8859-7", "ISO_8859_7" } }; - ht[12] = new Object[] { "iso-8859-9", "ISO_8859_9" }; - ht[14] = new Object[] { "x-ibm737", "IBM737" }; - ht[15] = new Object[] { "ibm850", "IBM850" }; - ht[16] = new Object[] { "ibm852", "IBM852", - new Object[] { "ibm775", "IBM775" } }; - ht[17] = new Object[] { "iso-8859-13", "ISO_8859_13", - new Object[] { "us-ascii", "US_ASCII" } }; - ht[18] = new Object[] { "ibm855", "IBM855", - new Object[] { "ibm437", "IBM437", - new Object[] { "iso-8859-15", "ISO_8859_15" } } }; - ht[19] = new Object[] { "ibm00858", "IBM858", - new Object[] { "ibm857", "IBM857", - new Object[] { "x-utf-32le-bom", "UTF_32LE_BOM" } } }; - ht[22] = new Object[] { "x-utf-16le-bom", "UTF_16LE_BOM" }; - ht[23] = new Object[] { "cesu-8", "CESU_8" }; - ht[24] = new Object[] { "x-utf-32be-bom", "UTF_32BE_BOM" }; - ht[28] = new Object[] { "koi8-r", "KOI8_R" }; - ht[29] = new Object[] { "koi8-u", "KOI8_U" }; - ht[31] = new Object[] { "utf-32le", "UTF_32LE" }; - } - - } - - private static final class Cache - extends sun.util.PreHashedMap - { - - private static final int ROWS = 32; - private static final int SIZE = 39; - private static final int SHIFT = 1; - private static final int MASK = 0x1f; - - private Cache() { - super(ROWS, SIZE, SHIFT, MASK); - } - - protected void init(Object[] ht) { - // [IKVM] we pre-populate some cache entries to avoid reflection when instantiating the common charsets - ht[0] = new Object[] { "ibm862", null }; - ht[2] = new Object[] { "ibm866", null, - new Object[] { "utf-32", null, - new Object[] { "utf-16le", null } } }; - ht[3] = new Object[] { "windows-1251", null, - new Object[] { "windows-1250", null } }; - ht[4] = new Object[] { "windows-1253", null, - new Object[] { "windows-1252", new MS1252(), - new Object[] { "utf-32be", null } } }; - ht[5] = new Object[] { "windows-1254", null, - new Object[] { "utf-16", null } }; - ht[6] = new Object[] { "windows-1257", null }; - ht[7] = new Object[] { "utf-16be", null }; - ht[8] = new Object[] { "iso-8859-2", null, - new Object[] { "iso-8859-1", null } }; - ht[9] = new Object[] { "iso-8859-4", null, - new Object[] { "utf-8", new UTF_8() } }; - ht[10] = new Object[] { "iso-8859-5", null }; - ht[11] = new Object[] { "x-ibm874", null, - new Object[] { "iso-8859-7", null } }; - ht[12] = new Object[] { "iso-8859-9", null }; - ht[14] = new Object[] { "x-ibm737", null }; - ht[15] = new Object[] { "ibm850", null }; - ht[16] = new Object[] { "ibm852", null, - new Object[] { "ibm775", null } }; - ht[17] = new Object[] { "iso-8859-13", null, - new Object[] { "us-ascii", null } }; - ht[18] = new Object[] { "ibm855", null, - new Object[] { "ibm437", null, - new Object[] { "iso-8859-15", null } } }; - ht[19] = new Object[] { "ibm00858", null, - new Object[] { "ibm857", null, - new Object[] { "x-utf-32le-bom", null } } }; - ht[22] = new Object[] { "x-utf-16le-bom", null }; - ht[23] = new Object[] { "cesu-8", null }; - ht[24] = new Object[] { "x-utf-32be-bom", null }; - ht[28] = new Object[] { "koi8-r", null }; - ht[29] = new Object[] { "koi8-u", null }; - ht[31] = new Object[] { "utf-32le", null }; - } - - } - - public StandardCharsets() { - super("sun.nio.cs", new Aliases(), new Classes(), new Cache()); - } - -} diff --git a/src/IKVM.Java/local/sun/nio/fs/DefaultFileSystemProvider.java b/src/IKVM.Java/local/sun/nio/fs/DefaultFileSystemProvider.java index 6ef8d07d45..e8eea74c82 100644 --- a/src/IKVM.Java/local/sun/nio/fs/DefaultFileSystemProvider.java +++ b/src/IKVM.Java/local/sun/nio/fs/DefaultFileSystemProvider.java @@ -1,35 +1,19 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - package sun.nio.fs; import java.nio.file.spi.FileSystemProvider; -public class DefaultFileSystemProvider -{ - public static FileSystemProvider create() - { - return new NetFileSystemProvider(); +public class DefaultFileSystemProvider { + + public static FileSystemProvider create() { + return new DotNetFileSystemProvider(); + + //if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { + // return new WindowsFileSystemProvider(); + //} else if (cli.IKVM.Runtime.RuntimeUtil.get_IsLinux()) { + // return new LinuxFileSystemProvider(); + //} else { + // return new DotNetFileSystemProvider(); + //} } + } diff --git a/src/IKVM.Java/local/sun/nio/fs/DefaultFileTypeDetector.java b/src/IKVM.Java/local/sun/nio/fs/DefaultFileTypeDetector.java index 5da08123c7..f488b3fc26 100644 --- a/src/IKVM.Java/local/sun/nio/fs/DefaultFileTypeDetector.java +++ b/src/IKVM.Java/local/sun/nio/fs/DefaultFileTypeDetector.java @@ -1,41 +1,32 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - package sun.nio.fs; import java.io.IOException; +import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.spi.FileTypeDetector; +import java.nio.file.spi.FileSystemProvider; + +public class DefaultFileTypeDetector { -public class DefaultFileTypeDetector -{ - public static FileTypeDetector create() - { + public static FileTypeDetector create() { return new AbstractFileTypeDetector() { public String implProbeContentType(Path obj) throws IOException { return null; } }; + + //FileSystemProvider provider = FileSystems.getDefault().provider(); + //if (provider instanceof UnixFileSystemProvider) { + // return ((UnixFileSystemProvider)provider).getFileTypeDetector(); + //} else if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { + // return new RegistryFileTypeDetector(); + //} else { + // return new AbstractFileTypeDetector() { + // public String implProbeContentType(Path obj) throws IOException { + // return null; + // } + // }; + //} } + } diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetBasicFileAttributeView.java b/src/IKVM.Java/local/sun/nio/fs/DotNetBasicFileAttributeView.java new file mode 100644 index 0000000000..4002df5f67 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetBasicFileAttributeView.java @@ -0,0 +1,29 @@ +package sun.nio.fs; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.*; + +class DotNetBasicFileAttributeView extends AbstractBasicFileAttributeView { + + static cli.System.DateTime toDateTime(FileTime fileTime) { + if (fileTime != null) { + return new cli.System.DateTime(fileTime.to(java.util.concurrent.TimeUnit.MICROSECONDS) * 10 + 621355968000000000L); + } else { + return cli.System.DateTime.MinValue; + } + } + + protected final String path; + + DotNetBasicFileAttributeView(String path) { + this.path = path; + } + + public BasicFileAttributes readAttributes() throws IOException { + return DotNetDosFileAttributes.read(path); + } + + public native void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException; + +} diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetDirectoryStream.java b/src/IKVM.Java/local/sun/nio/fs/DotNetDirectoryStream.java new file mode 100644 index 0000000000..e1b07bae1b --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetDirectoryStream.java @@ -0,0 +1,24 @@ +package sun.nio.fs; + +import java.util.*; +import java.nio.file.*; + +final class DotNetDirectoryStream implements DirectoryStream { + + private final DotNetPath path; + private final cli.System.Collections.IEnumerable files; + private final DirectoryStream.Filter filter; + + DotNetDirectoryStream(final DotNetPath path, final cli.System.Collections.IEnumerable files, final DirectoryStream.Filter filter) { + this.path = path; + this.files = files; + this.filter = filter; + } + + @Override + public native Iterator iterator(); + + @Override + public native void close(); + +} diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributeView.java b/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributeView.java new file mode 100644 index 0000000000..13040f5880 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributeView.java @@ -0,0 +1,96 @@ +package sun.nio.fs; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.util.*; + +import cli.System.IO.FileInfo; + +class DotNetDosFileAttributeView extends DotNetBasicFileAttributeView implements DosFileAttributeView { + + private static final String READONLY_NAME = "readonly"; + private static final String ARCHIVE_NAME = "archive"; + private static final String SYSTEM_NAME = "system"; + private static final String HIDDEN_NAME = "hidden"; + private static final Set dosAttributeNames = Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME); + + DotNetDosFileAttributeView(String path) { + super(path); + } + + public String name() { + return "dos"; + } + + public DosFileAttributes readAttributes() throws IOException { + return DotNetDosFileAttributes.read(path); + } + + public void setArchive(boolean value) throws IOException { + setAttribute(cli.System.IO.FileAttributes.Archive, value); + } + + public void setHidden(boolean value) throws IOException { + setAttribute(cli.System.IO.FileAttributes.Hidden, value); + } + + public void setReadOnly(boolean value) throws IOException { + setAttribute(cli.System.IO.FileAttributes.ReadOnly, value); + } + + public void setSystem(boolean value) throws IOException { + setAttribute(cli.System.IO.FileAttributes.System, value); + } + + private void setAttribute(int attr, boolean value) throws IOException { + setAttribute0(path, attr, value); + } + + private static native void setAttribute0(String path, int attr, boolean value) throws IOException; + + public Map readAttributes(String[] attributes) throws IOException { + AttributesBuilder builder = AttributesBuilder.create(dosAttributeNames, attributes); + DotNetDosFileAttributes attrs = DotNetDosFileAttributes.read(path); + addRequestedBasicAttributes(attrs, builder); + + if (builder.match(READONLY_NAME)) { + builder.add(READONLY_NAME, attrs.isReadOnly()); + } + + if (builder.match(ARCHIVE_NAME)) { + builder.add(ARCHIVE_NAME, attrs.isArchive()); + } + + if (builder.match(SYSTEM_NAME)) { + builder.add(SYSTEM_NAME, attrs.isSystem()); + } + + if (builder.match(HIDDEN_NAME)) { + builder.add(HIDDEN_NAME, attrs.isHidden()); + } + + return builder.unmodifiableMap(); + } + + public void setAttribute(String attribute, Object value) throws IOException { + switch (attribute) { + case READONLY_NAME: + setReadOnly((Boolean)value); + break; + case ARCHIVE_NAME: + setArchive((Boolean)value); + break; + case SYSTEM_NAME: + setSystem((Boolean)value); + break; + case HIDDEN_NAME: + setHidden((Boolean)value); + break; + default: + super.setAttribute(attribute, value); + break; + } + } + +} diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributes.java b/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributes.java new file mode 100644 index 0000000000..bd9a97e8ca --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetDosFileAttributes.java @@ -0,0 +1,94 @@ +package sun.nio.fs; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.util.*; + +class DotNetDosFileAttributes implements DosFileAttributes { + + public native static DotNetDosFileAttributes read(String path) throws IOException; + + private final FileTime creationTime; + private final FileTime lastAccessTime; + private final FileTime lastModifiedTime; + private Object fileKey; + private boolean isDirectory; + private boolean isOther; + private boolean isRegularFile; + private boolean isSymbolicLink; + private long size; + private boolean isReadOnly; + private boolean isHidden; + private boolean isArchive; + private boolean isSystem; + + public DotNetDosFileAttributes(FileTime creationTime, FileTime lastAccessTime, FileTime lastModifiedTime, Object fileKey, boolean isDirectory, boolean isOther, boolean isRegularFile, boolean isSymbolicLink, long size, boolean isReadOnly, boolean isHidden, boolean isArchive, boolean isSystem) { + this.creationTime = creationTime; + this.lastAccessTime = lastAccessTime; + this.lastModifiedTime = lastModifiedTime; + this.fileKey = fileKey; + this.isDirectory = isDirectory; + this.isOther = isOther; + this.isRegularFile = isRegularFile; + this.isSymbolicLink = isSymbolicLink; + this.size = size; + this.isReadOnly = isReadOnly; + this.isHidden = isHidden; + this.isArchive = isArchive; + this.isSystem = isSystem; + } + + public FileTime creationTime() { + return creationTime; + } + + public FileTime lastAccessTime() { + return lastAccessTime; + } + + public FileTime lastModifiedTime() { + return lastModifiedTime; + } + + public Object fileKey() { + return fileKey; + } + + public boolean isDirectory() { + return isDirectory; + } + + public boolean isOther() { + return isOther; + } + + public boolean isRegularFile() { + return isRegularFile; + } + + public boolean isSymbolicLink() { + return isSymbolicLink; + } + + public long size() { + return size; + } + + public boolean isReadOnly() { + return isReadOnly; + } + + public boolean isHidden() { + return isHidden; + } + + public boolean isArchive() { + return isArchive; + } + + public boolean isSystem() { + return isSystem; + } + +} diff --git a/src/IKVM.Java/local/sun/nio/fs/NetFileSystem.java b/src/IKVM.Java/local/sun/nio/fs/DotNetFileSystem.java similarity index 93% rename from src/IKVM.Java/local/sun/nio/fs/NetFileSystem.java rename to src/IKVM.Java/local/sun/nio/fs/DotNetFileSystem.java index 9c07f60571..d5d2be8ded 100644 --- a/src/IKVM.Java/local/sun/nio/fs/NetFileSystem.java +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetFileSystem.java @@ -31,6 +31,7 @@ import cli.System.IO.FileSystemEventHandler; import cli.System.IO.FileSystemWatcher; import cli.System.IO.WatcherChangeTypes; + import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.*; @@ -44,46 +45,38 @@ import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import static ikvm.internal.Util.WINDOWS; -final class NetFileSystem extends FileSystem -{ +final class DotNetFileSystem extends FileSystem { + private static final Set attributes = Collections.unmodifiableSet(new HashSet(Arrays.asList("basic"))); - private final NetFileSystemProvider provider; + private final DotNetFileSystemProvider provider; private final String separator = Character.toString(cli.System.IO.Path.DirectorySeparatorChar); - NetFileSystem(NetFileSystemProvider provider) - { + DotNetFileSystem(DotNetFileSystemProvider provider) { this.provider = provider; } - public FileSystemProvider provider() - { + public FileSystemProvider provider() { return provider; } - public void close() throws IOException - { + public void close() throws IOException { throw new UnsupportedOperationException(); } - public boolean isOpen() - { + public boolean isOpen() { return true; } - public boolean isReadOnly() - { + public boolean isReadOnly() { return false; } - public String getSeparator() - { + public String getSeparator() { return separator; } - public Iterable getRootDirectories() - { + public Iterable getRootDirectories() { SecurityManager sm = System.getSecurityManager(); ArrayList list = new ArrayList<>(); for (DriveInfo info : DriveInfo.GetDrives()) @@ -99,8 +92,10 @@ public Iterable getRootDirectories() { continue; } + list.add(getPath(info.get_Name())); } + return list; } @@ -118,6 +113,7 @@ public Iterable getFileStores() return Collections.emptyList(); } } + ArrayList list = new ArrayList<>(); for (DriveInfo info : DriveInfo.GetDrives()) { @@ -138,8 +134,10 @@ public Iterable getFileStores() } catch (IOException _) { + } } + return list; } @@ -152,7 +150,7 @@ public Path getPath(String first, String... more) { if (more.length == 0) { - return new NetPath(this, first); + return new DotNetPath(this, first); } else { @@ -167,7 +165,8 @@ public Path getPath(String first, String... more) sep = separator; } } - return new NetPath(this, sb.toString()); + + return new DotNetPath(this, sb.toString()); } } @@ -177,7 +176,7 @@ public PathMatcher getPathMatcher(String syntaxAndPattern) if (syntaxAndPattern.startsWith("glob:")) { String pattern = syntaxAndPattern.substring(5); - if (WINDOWS) + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { regex = Globs.toWindowsRegexPattern(pattern); } @@ -198,7 +197,7 @@ else if (syntaxAndPattern.indexOf(':') <= 0) { throw new UnsupportedOperationException(); } - final Pattern pattern = Pattern.compile(regex, WINDOWS ? Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE : 0); + final Pattern pattern = Pattern.compile(regex, cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() ? Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE : 0); return new PathMatcher() { @Override public boolean matches(Path path) { @@ -291,13 +290,13 @@ void enqueue(WatchKey key) private final class NetWatchKey implements WatchKey { - private final NetPath path; + private final DotNetPath path; private FileSystemWatcher fsw; private ArrayList> list = new ArrayList<>(); private HashSet modified = new HashSet<>(); private boolean signaled; - NetWatchKey(NetPath path) + NetWatchKey(DotNetPath path) { this.path = path; } @@ -364,7 +363,7 @@ WatchEvent createEvent(final FileSystemEventArgs e) { return new WatchEvent() { public Path context() { - return new NetPath((NetFileSystem)path.getFileSystem(), e.get_Name()); + return new DotNetPath((DotNetFileSystem)path.getFileSystem(), e.get_Name()); } public int count() { return 1; @@ -466,7 +465,7 @@ public Watchable watchable() } } - synchronized WatchKey register(NetPath path, boolean create, boolean delete, boolean modify, boolean overflow, boolean subtree) + synchronized WatchKey register(DotNetPath path, boolean create, boolean delete, boolean modify, boolean overflow, boolean subtree) { if (closed) { @@ -495,4 +494,5 @@ public WatchService newWatchService() throws IOException { return new NetWatchService(); } + } diff --git a/src/IKVM.Java/local/sun/nio/fs/DotNetFileSystemProvider.java b/src/IKVM.Java/local/sun/nio/fs/DotNetFileSystemProvider.java new file mode 100644 index 0000000000..7a1e59ed29 --- /dev/null +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetFileSystemProvider.java @@ -0,0 +1,843 @@ +/* + Copyright (C) 2011 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +package sun.nio.fs; + +import ikvm.internal.NotYetImplementedError; +import ikvm.internal.io.FileStreamExtensions; + +import cli.System.IO.Directory; +import cli.System.IO.DirectoryInfo; +import cli.System.IO.DriveInfo; +import cli.System.IO.File; +import cli.System.IO.FileAttributes; +import cli.System.IO.FileInfo; +import cli.System.IO.FileMode; +import cli.System.IO.FileShare; +import cli.System.IO.FileOptions; +import cli.System.Runtime.InteropServices.DllImportAttribute; +import cli.System.Runtime.InteropServices.Marshal; +import cli.System.Security.AccessControl.FileSystemRights; + +import com.sun.nio.file.ExtendedOpenOption; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.URI; +import java.nio.channels.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.FileSystemProvider; +import java.util.concurrent.ExecutorService; +import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import sun.nio.ch.DotNetAsynchronousFileChannelImpl; +import sun.nio.ch.FileChannelImpl; +import sun.nio.ch.ThreadPool; + +final class DotNetFileSystemProvider extends AbstractFileSystemProvider { + + private final DotNetFileSystem fs = new DotNetFileSystem(this); + private final HashMap stores = new HashMap(); + + final synchronized FileStore getFileStore(DriveInfo drive) throws IOException { + String name = drive.get_Name().toLowerCase(); + FileStore fs = stores.get(name); + if (fs == null) { + fs = new DotNetFileStore(drive); + stores.put(name, fs); + } + + return fs; + } + + public String getScheme() { + return "file"; + } + + public FileSystem newFileSystem(URI uri, Map env) throws IOException { + throw new FileSystemAlreadyExistsException(); + } + + public FileSystem getFileSystem(URI uri) { + return fs; + } + + public Path getPath(URI uri) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { + return DotNetWindowsUriSupport.fromUri(fs, uri); + } else { + return DotNetUnixUriUtils.fromUri(fs, uri); + } + } + + public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set opts, ExecutorService executor, FileAttribute... attrs) throws IOException { + DotNetPath npath = DotNetPath.from(path); + + for (FileAttribute attr : attrs) { + // null check + attr.getClass(); + throw new NotYetImplementedError(); + } + + int mode = FileMode.Open; + int share = FileShare.ReadWrite | FileShare.Delete; + int options = FileOptions.Asynchronous; + + boolean read = false; + boolean write = false; + boolean truncate = false; + + for (OpenOption opt : opts) { + if (opt instanceof StandardOpenOption) { + switch ((StandardOpenOption)opt) { + case CREATE: + mode = FileMode.Create; + break; + case CREATE_NEW: + mode = FileMode.CreateNew; + break; + case DELETE_ON_CLOSE: + options |= FileOptions.DeleteOnClose; + break; + case DSYNC: + options |= FileOptions.WriteThrough; + break; + case READ: + read = true; + break; + case SPARSE: + break; + case SYNC: + options |= FileOptions.WriteThrough; + break; + case TRUNCATE_EXISTING: + truncate = true; + break; + case WRITE: + write = true; + break; + default: + throw new UnsupportedOperationException(); + } + } else if (opt instanceof ExtendedOpenOption) { + switch ((ExtendedOpenOption)opt) { + case NOSHARE_READ: + share &= ~FileShare.Read; + break; + case NOSHARE_WRITE: + share &= ~FileShare.Write; + break; + case NOSHARE_DELETE: + share &= ~FileShare.Delete; + break; + default: + throw new UnsupportedOperationException(); + } + } else { + // null check + opt.getClass(); + throw new UnsupportedOperationException(); + } + } + + if (!read && !write) { + read = true; + } + + if (truncate) { + if (mode == FileMode.Open) { + mode = FileMode.Truncate; + } + } + + int rights = 0; + if (read) { + rights |= FileSystemRights.Read; + } + if (write) { + rights |= FileSystemRights.Write; + } + + ThreadPool pool; + if (executor == null) { + pool = null; + } else { + pool = ThreadPool.wrap(executor, 0); + } + + return DotNetAsynchronousFileChannelImpl.open(open(npath.path, mode, rights, share, options), read, write, pool); + } + + public SeekableByteChannel newByteChannel(Path path, Set opts, FileAttribute... attrs) throws IOException { + return newFileChannel(path, opts, attrs); + } + + public FileChannel newFileChannel(Path path, Set opts, FileAttribute... attrs) throws IOException { + DotNetPath npath = DotNetPath.from(path); + + for (FileAttribute attr : attrs) { + // null check + attr.getClass(); + throw new NotYetImplementedError(); + } + + int mode = FileMode.Open; + int share = FileShare.ReadWrite | FileShare.Delete; + int options = FileOptions.None; + + boolean read = false; + boolean write = false; + boolean append = false; + boolean truncate = false; + + for (OpenOption opt : opts) { + if (opt instanceof StandardOpenOption) { + switch ((StandardOpenOption)opt) { + case APPEND: + append = true; + write = true; + mode = FileMode.Append; + break; + case CREATE: + mode = FileMode.Create; + break; + case CREATE_NEW: + mode = FileMode.CreateNew; + break; + case DELETE_ON_CLOSE: + options |= FileOptions.DeleteOnClose; + break; + case DSYNC: + options |= FileOptions.WriteThrough; + break; + case READ: + read = true; + break; + case SPARSE: + break; + case SYNC: + options |= FileOptions.WriteThrough; + break; + case TRUNCATE_EXISTING: + truncate = true; + break; + case WRITE: + write = true; + break; + default: + throw new UnsupportedOperationException(); + } + } else if (opt instanceof ExtendedOpenOption) { + switch ((ExtendedOpenOption)opt) { + case NOSHARE_READ: + share &= ~FileShare.Read; + break; + case NOSHARE_WRITE: + share &= ~FileShare.Write; + break; + case NOSHARE_DELETE: + share &= ~FileShare.Delete; + break; + default: + throw new UnsupportedOperationException(); + } + } else { + // null check + opt.getClass(); + throw new UnsupportedOperationException(); + } + } + + if (!read && !write) { + read = true; + } + + if (read && append) { + throw new IllegalArgumentException("READ + APPEND not allowed"); + } + + if (truncate) { + if (append) { + throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); + } + if (mode == FileMode.Open) { + mode = FileMode.Truncate; + } + } + + int rights = 0; + if (append) { + // for atomic append to work, we can't set FileSystemRights.Write + rights |= FileSystemRights.AppendData; + } else { + if (read) { + rights |= FileSystemRights.Read; + } + if (write) { + rights |= FileSystemRights.Write; + } + } + + return FileChannelImpl.open(open(npath.path, mode, rights, share, options), npath.path, read, write, append, null); + } + + private static FileDescriptor open(String path, int mode, int rights, int share, int options) throws IOException { + return open0(path, FileMode.wrap(mode), FileSystemRights.wrap(rights), FileShare.wrap(share), 1, FileOptions.wrap(options), System.getSecurityManager()); + } + + public native DirectoryStream newDirectoryStream(Path dir, final DirectoryStream.Filter filter) throws IOException; + + public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { + DotNetPath ndir = DotNetPath.from(dir); + + for (FileAttribute attr : attrs) { + // null check + attr.getClass(); + throw new NotYetImplementedError(); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkWrite(ndir.path); + } + + try { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.IOException(); + if (false) throw new cli.System.Security.SecurityException(); + if (false) throw new cli.System.UnauthorizedAccessException(); + Directory.CreateDirectory(ndir.path); + } catch (cli.System.ArgumentException | cli.System.IO.IOException | cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) { + if (File.Exists(ndir.path)) { + throw new FileAlreadyExistsException(ndir.path); + } + + throw new IOException(x.getMessage()); + } + } + + public void copy(Path source, Path target, CopyOption... options) throws IOException { + DotNetPath nsource = DotNetPath.from(source); + DotNetPath ntarget = DotNetPath.from(target); + + boolean overwrite = false; + boolean copyAttribs = false; + + for (CopyOption opt : options) { + if (opt == StandardCopyOption.REPLACE_EXISTING) { + overwrite = true; + } else if (opt == StandardCopyOption.COPY_ATTRIBUTES) { + copyAttribs = true; + } else { + // null check + opt.getClass(); + throw new UnsupportedOperationException("Unsupported copy option"); + } + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(nsource.path); + sm.checkWrite(ntarget.path); + } + + try { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.FileNotFoundException(); + if (false) throw new cli.System.IO.DirectoryNotFoundException(); + if (false) throw new cli.System.IO.IOException(); + if (false) throw new cli.System.Security.SecurityException(); + if (false) throw new cli.System.UnauthorizedAccessException(); + + if (File.Exists(ntarget.path)) { + if (!overwrite) { + throw new FileAlreadyExistsException(ntarget.path); + } + + File.Delete(ntarget.path); + } + + if (Directory.Exists(ntarget.path)) { + if (!overwrite) { + throw new FileAlreadyExistsException(ntarget.path); + } + + try { + if (false) throw new cli.System.IO.IOException(); + Directory.Delete(ntarget.path); + } catch (cli.System.IO.IOException _) { + // HACK we assume that the IOException is caused by the directory not being empty + throw new DirectoryNotEmptyException(ntarget.path); + } + } + + if (Directory.Exists(nsource.path)) { + Directory.CreateDirectory(ntarget.path); + } else { + File.Copy(nsource.path, ntarget.path, overwrite); + } + + if (copyAttribs) { + if (Directory.Exists(ntarget.path)) { + File.SetAttributes(ntarget.path, File.GetAttributes(nsource.path)); + Directory.SetCreationTimeUtc(ntarget.path, File.GetCreationTimeUtc(nsource.path)); + Directory.SetLastAccessTimeUtc(ntarget.path, File.GetLastAccessTimeUtc(nsource.path)); + Directory.SetLastWriteTimeUtc(ntarget.path, File.GetLastWriteTimeUtc(nsource.path)); + } else { + File.SetAttributes(ntarget.path, File.GetAttributes(nsource.path)); + File.SetCreationTimeUtc(ntarget.path, File.GetCreationTimeUtc(nsource.path)); + File.SetLastAccessTimeUtc(ntarget.path, File.GetLastAccessTimeUtc(nsource.path)); + File.SetLastWriteTimeUtc(ntarget.path, File.GetLastWriteTimeUtc(nsource.path)); + } + } + } catch (cli.System.IO.FileNotFoundException x) { + throw new NoSuchFileException(x.get_FileName()); + } catch (cli.System.IO.DirectoryNotFoundException x) { + throw new NoSuchFileException(nsource.path, ntarget.path, x.getMessage()); + } catch (cli.System.IO.IOException | cli.System.ArgumentException x) { + throw new FileSystemException(nsource.path, ntarget.path, x.getMessage()); + } catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) { + throw new AccessDeniedException(nsource.path, ntarget.path, x.getMessage()); + } + } + + @cli.System.Security.SecuritySafeCriticalAttribute.Annotation + public void move(Path source, Path target, CopyOption... options) throws IOException { + DotNetPath nsource = DotNetPath.from(source); + DotNetPath ntarget = DotNetPath.from(target); + boolean overwrite = false; + boolean atomicMove = false; + + for (CopyOption opt : options) { + if (opt == StandardCopyOption.REPLACE_EXISTING) { + overwrite = true; + } else if (opt == StandardCopyOption.ATOMIC_MOVE) { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { + atomicMove = true; + } else { + throw new AtomicMoveNotSupportedException(nsource.path, ntarget.path, "Unsupported copy option"); + } + } else { + // null check + opt.getClass(); + throw new UnsupportedOperationException("Unsupported copy option"); + } + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(nsource.path); + sm.checkWrite(ntarget.path); + } + + if (atomicMove) { + int MOVEFILE_REPLACE_EXISTING = 1; + if (MoveFileEx(nsource.path, ntarget.path, MOVEFILE_REPLACE_EXISTING) == 0) { + final int ERROR_FILE_NOT_FOUND = 2; + final int ERROR_PATH_NOT_FOUND = 3; + final int ERROR_ACCESS_DENIED = 5; + final int ERROR_NOT_SAME_DEVICE = 17; + final int ERROR_FILE_EXISTS = 80; + final int ERROR_ALREADY_EXISTS = 183; + int lastError = Marshal.GetLastWin32Error(); + switch (lastError) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + throw new NoSuchFileException(nsource.path, ntarget.path, null); + case ERROR_ACCESS_DENIED: + throw new AccessDeniedException(nsource.path, ntarget.path, null); + case ERROR_NOT_SAME_DEVICE: + throw new AtomicMoveNotSupportedException(nsource.path, ntarget.path, "Unsupported copy option"); + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + throw new FileAlreadyExistsException(nsource.path, ntarget.path, null); + default: + throw new FileSystemException(nsource.path, ntarget.path, "Error " + lastError); + } + } + + return; + } + + try { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.FileNotFoundException(); + if (false) throw new cli.System.IO.DirectoryNotFoundException(); + if (false) throw new cli.System.IO.IOException(); + if (false) throw new cli.System.Security.SecurityException(); + if (false) throw new cli.System.UnauthorizedAccessException(); + + if (File.Exists(ntarget.path)) { + if (!overwrite) { + throw new FileAlreadyExistsException(ntarget.path); + } + File.Delete(ntarget.path); + } + + if (Directory.Exists(ntarget.path)) { + if (!overwrite) { + throw new FileAlreadyExistsException(ntarget.path); + } + + try { + if (false) throw new cli.System.IO.IOException(); + Directory.Delete(ntarget.path); + } catch (cli.System.IO.IOException _) { + // HACK we assume that the IOException is caused by the directory not being empty + throw new DirectoryNotEmptyException(ntarget.path); + } + } + + if (Directory.Exists(nsource.path)) { + Directory.Move(nsource.path, ntarget.path); + } else { + File.Move(nsource.path, ntarget.path); + } + } catch (cli.System.IO.FileNotFoundException x) { + throw new NoSuchFileException(x.get_FileName()); + } catch (cli.System.IO.DirectoryNotFoundException x) { + throw new NoSuchFileException(nsource.path, ntarget.path, x.getMessage()); + } catch (cli.System.IO.IOException | cli.System.ArgumentException x) { + throw new FileSystemException(nsource.path, ntarget.path, x.getMessage()); + } catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) { + throw new AccessDeniedException(nsource.path, ntarget.path, x.getMessage()); + } + } + + @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) + private static native int MoveFileEx(String lpExistingFileName, String lpNewFileName, int dwFlags); + + public boolean isSameFile(Path path, Path path2) throws IOException { + if (path.equals(path2)) { + return true; + } + + if (!(path instanceof DotNetPath && path2 instanceof DotNetPath)) { + // null check + path2.getClass(); + return false; + } + + return path.toRealPath().equals(path2.toRealPath()); + } + + public boolean isHidden(Path path) throws IOException { + String npath = DotNetPath.from(path).path; + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(npath); + } + + try { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.FileNotFoundException(); + if (false) throw new cli.System.IO.IOException(); + return (File.GetAttributes(npath).Value & (cli.System.IO.FileAttributes.Hidden | cli.System.IO.FileAttributes.Directory)) == cli.System.IO.FileAttributes.Hidden; + } catch (cli.System.IO.FileNotFoundException x) { + throw new NoSuchFileException(npath); + } catch (cli.System.IO.IOException | cli.System.ArgumentException x) { + throw new IOException(x.getMessage()); + } + } + + private static class DotNetFileStore extends FileStore { + + private final DriveInfo info; + private final String name; + private final String type; + + DotNetFileStore(DriveInfo info) throws IOException { + this.info = info; + try { + if (false) throw new cli.System.IO.IOException(); + name = info.get_VolumeLabel(); + type = info.get_DriveFormat(); + } catch (cli.System.IO.IOException x) { + throw new IOException(x.getMessage()); + } + } + + public Object getAttribute(String attribute) throws IOException { + switch (attribute) { + case "totalSpace": + return getTotalSpace(); + case "unallocatedSpace": + return getUnallocatedSpace(); + case "usableSpace": + return getUsableSpace(); + default: + throw new UnsupportedOperationException(); + } + } + + public V getFileStoreAttributeView(Class type) { + return null; + } + + public long getTotalSpace() throws IOException { + try { + if (false) throw new cli.System.IO.IOException(); + return info.get_TotalSize(); + } catch (cli.System.IO.IOException x) { + throw new IOException(x.getMessage()); + } + } + + public long getUnallocatedSpace() throws IOException { + try { + if (false) throw new cli.System.IO.IOException(); + return info.get_TotalFreeSpace(); + } catch (cli.System.IO.IOException x) { + throw new IOException(x.getMessage()); + } + } + + public long getUsableSpace() throws IOException { + try { + if (false) throw new cli.System.IO.IOException(); + return info.get_AvailableFreeSpace(); + } catch (cli.System.IO.IOException x) { + throw new IOException(x.getMessage()); + } + } + + public boolean isReadOnly() { + return false; + } + + public String name() { + return name; + } + + public boolean supportsFileAttributeView(Class type) { + // null check + type.getClass(); + return type == BasicFileAttributeView.class || type == DosFileAttributeView.class; + } + + public boolean supportsFileAttributeView(String name) { + return name.equals("basic") || name.equals("dos"); + } + + public String type() { + return type; + } + + public String toString() { + return name + " (" + info.get_Name().charAt(0) + ":)"; + } + + } + + public FileStore getFileStore(Path path) throws IOException { + DotNetPath npath = DotNetPath.from(path.toAbsolutePath()); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(npath.path); + } + + return getFileStore(new DriveInfo(npath.path)); + } + + public void checkAccess(Path path, AccessMode... modes) throws IOException { + String npath = DotNetPath.from(path).path; + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (modes.length == 0) { + sm.checkRead(npath); + } + + for (AccessMode m : modes) { + switch (m) { + case READ: + sm.checkRead(npath); + break; + case WRITE: + sm.checkWrite(npath); + break; + case EXECUTE: + sm.checkExec(npath); + break; + default: + throw new UnsupportedOperationException(); + } + } + } + + try { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.DirectoryNotFoundException(); + if (false) throw new cli.System.IO.FileNotFoundException(); + if (false) throw new cli.System.IO.IOException(); + if (false) throw new cli.System.NotSupportedException(); + if (false) throw new cli.System.Security.SecurityException(); + if (false) throw new cli.System.UnauthorizedAccessException(); + // note that File.GetAttributes() works for directories as well + int attr = File.GetAttributes(npath).Value; + for (AccessMode m : modes) { + switch (m) { + case READ: + case EXECUTE: + break; + case WRITE: + if ((attr & (cli.System.IO.FileAttributes.ReadOnly | cli.System.IO.FileAttributes.Directory)) == cli.System.IO.FileAttributes.ReadOnly) { + throw new AccessDeniedException(npath, null, "file has read-only attribute set"); + } + if (getFileStore(path).isReadOnly()) { + throw new AccessDeniedException(npath, null, "volume is read-only"); + } + break; + default: + throw new UnsupportedOperationException(); + } + } + } catch (cli.System.IO.FileNotFoundException | cli.System.IO.DirectoryNotFoundException _) { + throw new NoSuchFileException(npath); + } catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) { + throw new AccessDeniedException(npath, null, x.getMessage()); + } catch (cli.System.ArgumentException | cli.System.IO.IOException | cli.System.NotSupportedException x) { + throw new IOException(x.getMessage()); + } + } + + + private static void validateLinkOption(LinkOption... options) { + for (LinkOption option : options) { + if (option == LinkOption.NOFOLLOW_LINKS) { + // ignored + } else { + // null check + option.getClass(); + throw new UnsupportedOperationException(); + } + } + } + + public V getFileAttributeView(Path path, Class type, LinkOption... options) { + String npath = DotNetPath.from(path).path; + validateLinkOption(options); + if (type == BasicFileAttributeView.class) { + return (V)new DotNetBasicFileAttributeView(npath); + } else if (type == DosFileAttributeView.class) { + return (V)new DotNetDosFileAttributeView(npath); + } else { + // null check + type.getClass(); + return null; + } + } + + public A readAttributes(Path path, Class type, LinkOption... options) throws IOException { + String npath = DotNetPath.from(path).path; + // null check + type.getClass(); + validateLinkOption(options); + if (type != BasicFileAttributes.class && type != DosFileAttributes.class) { + throw new UnsupportedOperationException(); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(npath); + } + + return (A)DotNetDosFileAttributes.read(npath); + } + + DynamicFileAttributeView getFileAttributeView(Path file, String name, LinkOption... options) { + validateLinkOption(options); + if (name.equals("basic")) { + return new DotNetBasicFileAttributeView(DotNetPath.from(file).path); + } else if (name.equals("dos")) { + return new DotNetDosFileAttributeView(DotNetPath.from(file).path); + } else { + return null; + } + } + + boolean implDelete(Path file, boolean failIfNotExists) throws IOException { + String path = DotNetPath.from(file).path; + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkDelete(path); + } + + try + { + if (false) throw new cli.System.ArgumentException(); + if (false) throw new cli.System.IO.FileNotFoundException(); + if (false) throw new cli.System.IO.DirectoryNotFoundException(); + if (false) throw new cli.System.IO.IOException(); + if (false) throw new cli.System.Security.SecurityException(); + if (false) throw new cli.System.UnauthorizedAccessException(); + + int attr = cli.System.IO.File.GetAttributes(path).Value; + if ((attr & cli.System.IO.FileAttributes.Directory) != 0) { + try { + if (false) throw new cli.System.IO.IOException(); + cli.System.IO.Directory.Delete(path); + } catch (cli.System.IO.IOException _) { + // HACK we assume that the IOException is caused by the directory not being empty + throw new DirectoryNotEmptyException(path); + } + + return true; + } else { + cli.System.IO.File.Delete(path); + return true; + } + } catch (cli.System.ArgumentException x) { + throw new FileSystemException(path, null, x.getMessage()); + } catch (cli.System.IO.FileNotFoundException _) { + if (failIfNotExists) { + throw new NoSuchFileException(path); + } else { + return false; + } + } catch (cli.System.IO.DirectoryNotFoundException _) { + if (failIfNotExists) { + throw new NoSuchFileException(path); + } else { + return false; + } + } catch (cli.System.IO.IOException x) { + throw new FileSystemException(path, null, x.getMessage()); + } catch (cli.System.Security.SecurityException _) { + throw new AccessDeniedException(path); + } catch (cli.System.UnauthorizedAccessException _) { + throw new AccessDeniedException(path); + } + } + + static native FileDescriptor open0(String path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, SecurityManager sm) throws java.io.IOException; + +} diff --git a/src/IKVM.Java/local/sun/nio/fs/NetPath.java b/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java similarity index 65% rename from src/IKVM.Java/local/sun/nio/fs/NetPath.java rename to src/IKVM.Java/local/sun/nio/fs/DotNetPath.java index 6861a1e27f..5ee0e320e6 100644 --- a/src/IKVM.Java/local/sun/nio/fs/NetPath.java +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetPath.java @@ -32,18 +32,16 @@ import java.nio.file.*; import java.util.ArrayList; import java.util.Iterator; -import static ikvm.internal.Util.MACOSX; -import static ikvm.internal.Util.WINDOWS; -final class NetPath extends AbstractPath -{ +final class DotNetPath extends AbstractPath { + private static final char[] invalid = cli.System.IO.Path.GetInvalidFileNameChars(); - private final NetFileSystem fs; + private final DotNetFileSystem fs; final String path; - NetPath(NetFileSystem fs, String path) + DotNetPath(DotNetFileSystem fs, String path) { - if (WINDOWS) + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { path = WindowsPathParser.parse(path).path(); } @@ -95,26 +93,21 @@ else if (c == '/') this.path = path; } - public FileSystem getFileSystem() - { + public FileSystem getFileSystem() { return fs; } - public boolean isAbsolute() - { - return cli.System.IO.Path.IsPathRooted(path) - && (!WINDOWS || path.startsWith("\\\\") || (path.length() >= 3 && path.charAt(1) == ':' && path.charAt(2) == '\\')); + public boolean isAbsolute() { + return cli.System.IO.Path.IsPathRooted(path) && (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() == false || path.startsWith("\\\\") || (path.length() >= 3 && path.charAt(1) == ':' && path.charAt(2) == '\\')); } - public Path getRoot() - { + public Path getRoot() { int len = getRootLength(); - return len == 0 ? null : new NetPath(fs, path.substring(0, len)); + return len == 0 ? null : new DotNetPath(fs, path.substring(0, len)); } - private int getRootLength() - { - if (WINDOWS) + private int getRootLength() { + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { if (path.length() >= 2 && path.charAt(1) == ':') { @@ -143,8 +136,7 @@ else if (path.startsWith("\\\\")) } } - public Path getFileName() - { + public Path getFileName() { if (path.length() == 0) { return this; @@ -158,7 +150,7 @@ public Path getFileName() { return null; } - return new NetPath(fs, name); + return new DotNetPath(fs, name); } public Path getParent() @@ -172,7 +164,7 @@ public Path getParent() { return null; } - return new NetPath(fs, parent); + return new DotNetPath(fs, parent); } public int getNameCount() @@ -195,7 +187,7 @@ public int getNameCount() public Path getName(int index) { - return new NetPath(fs, getNameImpl(index)); + return new DotNetPath(fs, getNameImpl(index)); } private String getNameImpl(int index) @@ -231,17 +223,17 @@ public Path subpath(int beginIndex, int endIndex) } sb.append(getNameImpl(i)); } - return new NetPath(fs, sb.toString()); + return new DotNetPath(fs, sb.toString()); } public boolean startsWith(Path other) { - String npath = NetPath.from(other).path; + String npath = DotNetPath.from(other).path; if (npath.length() == 0) { return path.length() == 0; } - return path.regionMatches(WINDOWS, 0, npath, 0, npath.length()) + return path.regionMatches(cli.IKVM.Runtime.RuntimeUtil.get_IsWindows(), 0, npath, 0, npath.length()) && (npath.length() == getRootLength() || (npath.length() > getRootLength() && (path.length() == npath.length() @@ -250,7 +242,7 @@ public boolean startsWith(Path other) public boolean endsWith(Path other) { - NetPath nother = NetPath.from(other); + DotNetPath nother = DotNetPath.from(other); String npath = nother.path; if (npath.length() > path.length()) { @@ -271,7 +263,7 @@ public boolean endsWith(Path other) { if (otherNameCount != nameCount || getRootLength() != otherRootLength - || !path.regionMatches(WINDOWS, 0, npath, 0, otherRootLength)) + || !path.regionMatches(cli.IKVM.Runtime.RuntimeUtil.get_IsWindows(), 0, npath, 0, otherRootLength)) { return false; } @@ -281,7 +273,7 @@ public boolean endsWith(Path other) { String s1 = getNameImpl(i + skip); String s2 = nother.getNameImpl(i); - if (s1.length() != s2.length() || !s1.regionMatches(WINDOWS, 0, s2, 0, s1.length())) + if (s1.length() != s2.length() || !s1.regionMatches(cli.IKVM.Runtime.RuntimeUtil.get_IsWindows(), 0, s2, 0, s1.length())) { return false; } @@ -300,7 +292,7 @@ public Path normalize() { if (list.size() == 0) { - if (rootLength == 0 || (WINDOWS && rootLength == 2)) + if (rootLength == 0 || (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() && rootLength == 2)) { list.add(".."); } @@ -329,12 +321,12 @@ else if (!s.equals(".")) } sb.append(list.get(i)); } - return new NetPath(fs, sb.toString()); + return new DotNetPath(fs, sb.toString()); } public Path resolve(Path other) { - NetPath nother = NetPath.from(other); + DotNetPath nother = DotNetPath.from(other); String npath = nother.path; if (nother.isAbsolute()) { @@ -344,7 +336,7 @@ public Path resolve(Path other) { return this; } - if (WINDOWS) + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { if (nother.getRootLength() == 2 && getRootLength() == 3 && (path.charAt(0) | 0x20) == (npath.charAt(0) | 0x20)) { @@ -356,70 +348,151 @@ else if (nother.getRootLength() == 1 && getRootLength() > 3) { // we're in the case where we have a root "\\host\share\" and other "\", // we have to manually handle this because Path.Combine doesn't do the right thing - return new NetPath(fs, path.substring(0, getRootLength()) + npath); + return new DotNetPath(fs, path.substring(0, getRootLength()) + npath); } } - return new NetPath(fs, cli.System.IO.Path.Combine(path, npath)); + return new DotNetPath(fs, cli.System.IO.Path.Combine(path, npath)); } public Path relativize(Path other) { - NetPath nother = NetPath.from(other); - if (equals(nother)) - { - return new NetPath(fs, ""); - } - int rootLength = getRootLength(); - if (nother.getRootLength() != rootLength || !path.regionMatches(true, 0, nother.path, 0, rootLength)) - { - throw new IllegalArgumentException("'other' has different root"); - } - int nameCount = getNameCount(); - int otherNameCount = nother.getNameCount(); - int count = Math.min(nameCount, otherNameCount); - int i = 0; - // skip the common parts - for (; i < count && getNameImpl(i).equals(nother.getNameImpl(i)); i++) - { - } - // remove the unused parts of our path - StringBuilder sb = new StringBuilder(); - for (int j = i; j < nameCount; j++) - { - sb.append("..\\"); - } - // append the new parts of other - for (int j = i; j < otherNameCount; j++) - { - if (j != i) + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { + DotNetPath nother = DotNetPath.from(other); + if (equals(nother)) + { + return new DotNetPath(fs, ""); + } + int rootLength = getRootLength(); + if (nother.getRootLength() != rootLength || !path.regionMatches(true, 0, nother.path, 0, rootLength)) + { + throw new IllegalArgumentException("'other' has different root"); + } + int nameCount = getNameCount(); + int otherNameCount = nother.getNameCount(); + int count = Math.min(nameCount, otherNameCount); + int i = 0; + // skip the common parts + for (; i < count && getNameImpl(i).equals(nother.getNameImpl(i)); i++) + { + } + // remove the unused parts of our path + StringBuilder sb = new StringBuilder(); + for (int j = i; j < nameCount; j++) { - sb.append("\\"); + sb.append("..\\"); + } + // append the new parts of other + for (int j = i; j < otherNameCount; j++) + { + if (j != i) + { + sb.append("\\"); + } + sb.append(nother.getNameImpl(j)); + } + return new DotNetPath(fs, sb.toString()); + } else { + DotNetPath nother = DotNetPath.from(other); + if (other.equals(this)) + return emptyPath(); + + // can only relativize paths of the same type + if (this.isAbsolute() != other.isAbsolute()) + throw new IllegalArgumentException("'other' is different type of Path"); + + // this path is the empty path + if (this.isEmpty()) + return nother; + + int bn = this.getNameCount(); + int cn = other.getNameCount(); + + // skip matching names + int n = (bn > cn) ? cn : bn; + int i = 0; + while (i < n) { + if (!this.getName(i).equals(nother.getName(i))) + break; + i++; + } + + int dotdots = bn - i; + if (i < cn) { + // remaining name components in other + DotNetPath remainder = DotNetPath.from(nother.subpath(i, cn)); + if (dotdots == 0) + return remainder; + + // other is the empty path + boolean isOtherEmpty = nother.isEmpty(); + + // result is a "../" for each remaining name in base + // followed by the remaining names in other. If the remainder is + // the empty path then we don't add the final trailing slash. + int len = dotdots*3 + remainder.path.length(); + if (isOtherEmpty) { + assert remainder.isEmpty(); + len--; + } + char[] result = new char[len]; + int pos = 0; + while (dotdots > 0) { + result[pos++] = '.'; + result[pos++] = '.'; + if (isOtherEmpty) { + if (dotdots > 1) result[pos++] = '/'; + } else { + result[pos++] = '/'; + } + dotdots--; + } + System.arraycopy(remainder.path.toCharArray(), 0, result, pos, remainder.path.length()); + return new DotNetPath(fs, new String(result)); + } else { + // no remaining names in other so result is simply a sequence of ".." + char[] result = new char[dotdots*3 - 1]; + int pos = 0; + while (dotdots > 0) { + result[pos++] = '.'; + result[pos++] = '.'; + // no tailing slash at the end + if (dotdots > 1) + result[pos++] = '/'; + dotdots--; + } + return new DotNetPath(fs, new String(result)); } - sb.append(nother.getNameImpl(j)); } - return new NetPath(fs, sb.toString()); + } + + private boolean isEmpty() { + return path.length() == 0; + } + + private DotNetPath emptyPath() { + return new DotNetPath(fs, ""); } public URI toUri() { - if (WINDOWS) + if (cli.IKVM.Runtime.RuntimeUtil.get_IsWindows()) { - return WindowsUriSupport.toUri(this); + return DotNetWindowsUriSupport.toUri(this); } else { - return UnixUriUtils.toUri(this); + return DotNetUnixUriUtils.toUri(this); } } - public NetPath toAbsolutePath() + public DotNetPath toAbsolutePath() { if (isAbsolute()) { return this; } // System.getProperty("user.dir") will trigger the specified security check - return new NetPath(fs, cli.System.IO.Path.GetFullPath(cli.System.IO.Path.Combine(System.getProperty("user.dir"), path))); + return new DotNetPath(fs, cli.System.IO.Path.GetFullPath(cli.System.IO.Path.Combine(System.getProperty("user.dir"), path))); } public Path toRealPath(LinkOption... options) throws IOException @@ -433,14 +506,14 @@ public Path toRealPath(LinkOption... options) throws IOException sm.checkPropertyAccess("user.dir"); } } - return new NetPath(fs, toRealPathImpl(path)); + return new DotNetPath(fs, toRealPathImpl(path)); } private static native String toRealPathImpl(String path); public WatchKey register(WatchService watcher, WatchEvent.Kind[] events, WatchEvent.Modifier... modifiers) throws IOException { - if (!(watcher instanceof NetFileSystem.NetWatchService)) + if (!(watcher instanceof DotNetFileSystem.NetWatchService)) { // null check watcher.getClass(); @@ -506,12 +579,12 @@ else if (modifier instanceof SensitivityWatchEventModifier) sm.checkRead(path + cli.System.IO.Path.DirectorySeparatorChar + '-'); } } - return ((NetFileSystem.NetWatchService)watcher).register(this, create, delete, modify, overflow, subtree); + return ((DotNetFileSystem.NetWatchService)watcher).register(this, create, delete, modify, overflow, subtree); } public int compareTo(Path other) { - String path2 = ((NetPath)other).path; + String path2 = ((DotNetPath)other).path; int len1 = path.length(); int len2 = path2.length(); int min = Math.min(len1, len2); @@ -529,11 +602,11 @@ public int compareTo(Path other) public boolean equals(Object other) { - if (!(other instanceof NetPath)) + if (!(other instanceof DotNetPath)) { return false; } - return compareTo((NetPath)other) == 0; + return compareTo((DotNetPath)other) == 0; } public int hashCode() @@ -553,17 +626,17 @@ public String toString() boolean isUnc() { - return WINDOWS && getRootLength() > 3; + return cli.IKVM.Runtime.RuntimeUtil.get_IsWindows() && getRootLength() > 3; } - static NetPath from(Path path) + static DotNetPath from(Path path) { - if (!(path instanceof NetPath)) + if (!(path instanceof DotNetPath)) { // null check path.getClass(); throw new ProviderMismatchException(); } - return (NetPath)path; + return (DotNetPath)path; } } diff --git a/src/IKVM.Java/local/sun/nio/fs/UnixUriUtils.java b/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java similarity index 92% rename from src/IKVM.Java/local/sun/nio/fs/UnixUriUtils.java rename to src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java index 05426811ee..7b4c82543d 100644 --- a/src/IKVM.Java/local/sun/nio/fs/UnixUriUtils.java +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetUnixUriUtils.java @@ -35,13 +35,13 @@ * Unix specific Path <--> URI conversion */ -class UnixUriUtils { - private UnixUriUtils() { } +class DotNetUnixUriUtils { + private DotNetUnixUriUtils() { } /** * Converts URI to Path */ - static Path fromUri(NetFileSystem fs, URI uri) { + static Path fromUri(DotNetFileSystem fs, URI uri) { if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute"); if (uri.isOpaque()) @@ -68,15 +68,15 @@ static Path fromUri(NetFileSystem fs, URI uri) { // transform escaped octets and unescaped characters to bytes if (p.endsWith("/") && len > 1) - p = p.substring(0, len - 1); + len--; - return new NetPath(fs, p); + return new DotNetPath(fs, p); } /** * Converts Path to URI */ - static URI toUri(NetPath up) { + static URI toUri(DotNetPath up) { byte[] path = up.toAbsolutePath().toString().getBytes(); StringBuilder sb = new StringBuilder("file:///"); assert path[0] == '/'; @@ -94,7 +94,7 @@ static URI toUri(NetPath up) { // trailing slash if directory if (sb.charAt(sb.length()-1) != '/') { try { - if (cli.System.IO.Directory.Exists(up.path)) + if (cli.System.IO.Directory.Exists(sb.toString()) || isVfsDirectory(sb.toString())) sb.append('/'); } catch (Throwable x) { // ignore @@ -217,12 +217,15 @@ private static int decode(char c) { private static final long H_PCHAR = H_UNRESERVED | highMask(":@&=+$,"); - // All valid path characters - private static final long L_PATH = L_PCHAR | lowMask(";/"); - private static final long H_PATH = H_PCHAR | highMask(";/"); + // All valid path characters + private static final long L_PATH = L_PCHAR | lowMask(";/"); + private static final long H_PATH = H_PCHAR | highMask(";/"); - private final static char[] hexDigits = { + private final static char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; -} + + static native boolean isVfsDirectory(String path); + +} \ No newline at end of file diff --git a/src/IKVM.Java/local/sun/nio/fs/WindowsUriSupport.java b/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java similarity index 93% rename from src/IKVM.Java/local/sun/nio/fs/WindowsUriSupport.java rename to src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java index 0301bf90e3..fa37311593 100644 --- a/src/IKVM.Java/local/sun/nio/fs/WindowsUriSupport.java +++ b/src/IKVM.Java/local/sun/nio/fs/DotNetWindowsUriSupport.java @@ -32,8 +32,8 @@ * Utility methods to convert between Path and URIs. */ -class WindowsUriSupport { - private WindowsUriSupport() { +class DotNetWindowsUriSupport { + private DotNetWindowsUriSupport() { } // suffix for IPv6 literal address @@ -95,7 +95,7 @@ private static URI toUri(String path, boolean isUnc, boolean addSlash) { /** * Converts given Path to a URI */ - static URI toUri(NetPath path) { + static URI toUri(DotNetPath path) { path = path.toAbsolutePath(); String s = path.toString(); @@ -104,8 +104,9 @@ static URI toUri(NetPath path) { boolean addSlash = false; if (!s.endsWith("\\")) { try { - addSlash = cli.System.IO.Directory.Exists(s); + addSlash = cli.System.IO.Directory.Exists(s) || isVfsDirectory(s); } catch (Throwable x) { + } } @@ -115,7 +116,7 @@ static URI toUri(NetPath path) { /** * Converts given URI to a Path */ - static NetPath fromUri(NetFileSystem fs, URI uri) { + static DotNetPath fromUri(DotNetFileSystem fs, URI uri) { if (!uri.isAbsolute()) throw new IllegalArgumentException("URI is not absolute"); if (uri.isOpaque()) @@ -162,6 +163,9 @@ static NetPath fromUri(NetFileSystem fs, URI uri) { path = path.substring(1); } } - return new NetPath(fs, path); + return new DotNetPath(fs, path); } -} + + static native boolean isVfsDirectory(String path); + +} \ No newline at end of file diff --git a/src/IKVM.Java/local/sun/nio/fs/NetFileSystemProvider.java b/src/IKVM.Java/local/sun/nio/fs/NetFileSystemProvider.java deleted file mode 100644 index 37b4ef7f6f..0000000000 --- a/src/IKVM.Java/local/sun/nio/fs/NetFileSystemProvider.java +++ /dev/null @@ -1,1478 +0,0 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -package sun.nio.fs; - -import ikvm.internal.NotYetImplementedError; -import ikvm.internal.io.FileStreamExtensions; -import static ikvm.internal.Util.WINDOWS; - -import cli.System.IO.Directory; -import cli.System.IO.DirectoryInfo; -import cli.System.IO.DriveInfo; -import cli.System.IO.File; -import cli.System.IO.FileAttributes; -import cli.System.IO.FileInfo; -import cli.System.IO.FileMode; -import cli.System.IO.FileShare; -import cli.System.IO.FileStream; -import cli.System.IO.FileOptions; -import cli.System.Runtime.InteropServices.DllImportAttribute; -import cli.System.Runtime.InteropServices.Marshal; -import cli.System.Security.AccessControl.FileSystemRights; - -import com.sun.nio.file.ExtendedOpenOption; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.URI; -import java.nio.channels.*; -import java.nio.file.*; -import java.nio.file.attribute.*; -import java.nio.file.spi.FileSystemProvider; -import java.util.concurrent.ExecutorService; -import java.util.Iterator; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import sun.nio.ch.WindowsAsynchronousFileChannelImpl; -import sun.nio.ch.FileChannelImpl; -import sun.nio.ch.ThreadPool; - -final class NetFileSystemProvider extends AbstractFileSystemProvider -{ - private final NetFileSystem fs = new NetFileSystem(this); - private final HashMap stores = new HashMap(); - - final synchronized FileStore getFileStore(DriveInfo drive) throws IOException - { - String name = drive.get_Name().toLowerCase(); - FileStore fs = stores.get(name); - if (fs == null) - { - fs = new NetFileStore(drive); - stores.put(name, fs); - } - return fs; - } - - public String getScheme() - { - return "file"; - } - - public FileSystem newFileSystem(URI uri, Map env) throws IOException - { - throw new FileSystemAlreadyExistsException(); - } - - public FileSystem getFileSystem(URI uri) - { - return fs; - } - - public Path getPath(URI uri) - { - if (WINDOWS) - { - return WindowsUriSupport.fromUri(fs, uri); - } - else - { - return UnixUriUtils.fromUri(fs, uri); - } - } - - public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set opts, ExecutorService executor, FileAttribute... attrs) throws IOException - { - NetPath npath = NetPath.from(path); - for (FileAttribute attr : attrs) - { - // null check - attr.getClass(); - throw new NotYetImplementedError(); - } - int mode = FileMode.Open; - int share = FileShare.ReadWrite | FileShare.Delete; - int options = FileOptions.Asynchronous; - boolean read = false; - boolean write = false; - boolean truncate = false; - for (OpenOption opt : opts) - { - if (opt instanceof StandardOpenOption) - { - switch ((StandardOpenOption)opt) - { - case CREATE: - mode = FileMode.Create; - break; - case CREATE_NEW: - mode = FileMode.CreateNew; - break; - case DELETE_ON_CLOSE: - options |= FileOptions.DeleteOnClose; - break; - case DSYNC: - options |= FileOptions.WriteThrough; - break; - case READ: - read = true; - break; - case SPARSE: - break; - case SYNC: - options |= FileOptions.WriteThrough; - break; - case TRUNCATE_EXISTING: - truncate = true; - break; - case WRITE: - write = true; - break; - default: - throw new UnsupportedOperationException(); - } - } - else if (opt instanceof ExtendedOpenOption) - { - switch ((ExtendedOpenOption)opt) - { - case NOSHARE_READ: - share &= ~FileShare.Read; - break; - case NOSHARE_WRITE: - share &= ~FileShare.Write; - break; - case NOSHARE_DELETE: - share &= ~FileShare.Delete; - break; - default: - throw new UnsupportedOperationException(); - } - } - else - { - // null check - opt.getClass(); - throw new UnsupportedOperationException(); - } - } - - if (!read && !write) - { - read = true; - } - - if (truncate) - { - if (mode == FileMode.Open) - { - mode = FileMode.Truncate; - } - } - - int rights = 0; - if (read) - { - rights |= FileSystemRights.Read; - } - if (write) - { - rights |= FileSystemRights.Write; - } - - ThreadPool pool; - if (executor == null) - { - pool = null; - } - else - { - pool = ThreadPool.wrap(executor, 0); - } - - return WindowsAsynchronousFileChannelImpl.open(open(npath.path, mode, rights, share, options), read, write, pool); - } - - public SeekableByteChannel newByteChannel(Path path, Set opts, FileAttribute... attrs) throws IOException - { - return newFileChannel(path, opts, attrs); - } - - public FileChannel newFileChannel(Path path, Set opts, FileAttribute... attrs) throws IOException - { - NetPath npath = NetPath.from(path); - for (FileAttribute attr : attrs) - { - // null check - attr.getClass(); - throw new NotYetImplementedError(); - } - int mode = FileMode.Open; - int share = FileShare.ReadWrite | FileShare.Delete; - int options = FileOptions.None; - boolean read = false; - boolean write = false; - boolean append = false; - boolean truncate = false; - for (OpenOption opt : opts) - { - if (opt instanceof StandardOpenOption) - { - switch ((StandardOpenOption)opt) - { - case APPEND: - append = true; - write = true; - mode = FileMode.Append; - break; - case CREATE: - mode = FileMode.Create; - break; - case CREATE_NEW: - mode = FileMode.CreateNew; - break; - case DELETE_ON_CLOSE: - options |= FileOptions.DeleteOnClose; - break; - case DSYNC: - options |= FileOptions.WriteThrough; - break; - case READ: - read = true; - break; - case SPARSE: - break; - case SYNC: - options |= FileOptions.WriteThrough; - break; - case TRUNCATE_EXISTING: - truncate = true; - break; - case WRITE: - write = true; - break; - default: - throw new UnsupportedOperationException(); - } - } - else if (opt instanceof ExtendedOpenOption) - { - switch ((ExtendedOpenOption)opt) - { - case NOSHARE_READ: - share &= ~FileShare.Read; - break; - case NOSHARE_WRITE: - share &= ~FileShare.Write; - break; - case NOSHARE_DELETE: - share &= ~FileShare.Delete; - break; - default: - throw new UnsupportedOperationException(); - } - } - else - { - // null check - opt.getClass(); - throw new UnsupportedOperationException(); - } - } - - if (!read && !write) - { - read = true; - } - - if (read && append) - { - throw new IllegalArgumentException("READ + APPEND not allowed"); - } - - if (truncate) - { - if (append) - { - throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); - } - if (mode == FileMode.Open) - { - mode = FileMode.Truncate; - } - } - - int rights = 0; - if (append) - { - // for atomic append to work, we can't set FileSystemRights.Write - rights |= FileSystemRights.AppendData; - } - else - { - if (read) - { - rights |= FileSystemRights.Read; - } - if (write) - { - rights |= FileSystemRights.Write; - } - } - - return FileChannelImpl.open(open(npath.path, mode, rights, share, options), npath.path, read, write, append, null); - } - - private static FileDescriptor open(String path, int mode, int rights, int share, int options) throws IOException - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - if ((rights & FileSystemRights.Read) != 0) - { - sm.checkRead(path); - } - if ((rights & (FileSystemRights.Write | FileSystemRights.AppendData)) != 0) - { - sm.checkWrite(path); - } - if ((options & FileOptions.DeleteOnClose) != 0) - { - sm.checkDelete(path); - } - } - - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.PlatformNotSupportedException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - return FileDescriptor.fromStream(FileStreamExtensions.create(path, FileMode.wrap(mode), FileSystemRights.wrap(rights), FileShare.wrap(share), 8, FileOptions.wrap(options))); - } - catch (cli.System.ArgumentException x) - { - throw new FileSystemException(path, null, x.getMessage()); - } - catch (cli.System.IO.FileNotFoundException _) - { - throw new NoSuchFileException(path); - } - catch (cli.System.IO.DirectoryNotFoundException _) - { - throw new NoSuchFileException(path); - } - catch (cli.System.PlatformNotSupportedException x) - { - throw new UnsupportedOperationException(x.getMessage()); - } - catch (cli.System.IO.IOException x) - { - if (mode == FileMode.CreateNew && File.Exists(path)) - { - throw new FileAlreadyExistsException(path); - } - throw new FileSystemException(path, null, x.getMessage()); - } - catch (cli.System.Security.SecurityException _) - { - throw new AccessDeniedException(path); - } - catch (cli.System.UnauthorizedAccessException _) - { - throw new AccessDeniedException(path); - } - } - - public DirectoryStream newDirectoryStream(Path dir, final DirectoryStream.Filter filter) throws IOException - { - final String ndir = NetPath.from(dir).path; - // null check - filter.getClass(); - - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(ndir); - } - - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - final String[] files = Directory.GetFileSystemEntries(ndir); - return new DirectoryStream() { - private boolean closed; - public Iterator iterator() { - if (closed) { - throw new IllegalStateException(); - } - closed = true; - return new Iterator() { - private int pos; - private Path filtered; - public boolean hasNext() { - if (filtered == null) { - while (pos != files.length) { - Path p = new NetPath(fs, cli.System.IO.Path.Combine(ndir, files[pos++])); - try { - if (filter.accept(p)) { - filtered = p; - break; - } - } catch (IOException x) { - throw new DirectoryIteratorException(x); - } - } - } - return filtered != null; - } - public Path next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - Path p = filtered; - filtered = null; - return p; - } - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - public void close() { - closed = true; - } - }; - } - catch (cli.System.ArgumentException - | cli.System.IO.IOException - | cli.System.Security.SecurityException - | cli.System.UnauthorizedAccessException x) - { - if (File.Exists(ndir)) - { - throw new NotDirectoryException(ndir); - } - throw new IOException(x.getMessage()); - } - } - - public void createDirectory(Path dir, FileAttribute... attrs) throws IOException - { - NetPath ndir = NetPath.from(dir); - for (FileAttribute attr : attrs) - { - // null check - attr.getClass(); - throw new NotYetImplementedError(); - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkWrite(ndir.path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - Directory.CreateDirectory(ndir.path); - } - catch (cli.System.ArgumentException - | cli.System.IO.IOException - | cli.System.Security.SecurityException - | cli.System.UnauthorizedAccessException x) - { - if (File.Exists(ndir.path)) - { - throw new FileAlreadyExistsException(ndir.path); - } - throw new IOException(x.getMessage()); - } - } - - public void copy(Path source, Path target, CopyOption... options) throws IOException - { - NetPath nsource = NetPath.from(source); - NetPath ntarget = NetPath.from(target); - boolean overwrite = false; - boolean copyAttribs = false; - for (CopyOption opt : options) - { - if (opt == StandardCopyOption.REPLACE_EXISTING) - { - overwrite = true; - } - else if (opt == StandardCopyOption.COPY_ATTRIBUTES) - { - copyAttribs = true; - } - else - { - // null check - opt.getClass(); - throw new UnsupportedOperationException("Unsupported copy option"); - } - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(nsource.path); - sm.checkWrite(ntarget.path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - if (File.Exists(ntarget.path)) - { - if (!overwrite) - { - throw new FileAlreadyExistsException(ntarget.path); - } - File.Delete(ntarget.path); - } - if (Directory.Exists(ntarget.path)) - { - if (!overwrite) - { - throw new FileAlreadyExistsException(ntarget.path); - } - try - { - if (false) throw new cli.System.IO.IOException(); - Directory.Delete(ntarget.path); - } - catch (cli.System.IO.IOException _) - { - // HACK we assume that the IOException is caused by the directory not being empty - throw new DirectoryNotEmptyException(ntarget.path); - } - } - if (Directory.Exists(nsource.path)) - { - Directory.CreateDirectory(ntarget.path); - } - else - { - File.Copy(nsource.path, ntarget.path, overwrite); - } - if (copyAttribs) - { - if (Directory.Exists(ntarget.path)) - { - File.SetAttributes(ntarget.path, File.GetAttributes(nsource.path)); - Directory.SetCreationTimeUtc(ntarget.path, File.GetCreationTimeUtc(nsource.path)); - Directory.SetLastAccessTimeUtc(ntarget.path, File.GetLastAccessTimeUtc(nsource.path)); - Directory.SetLastWriteTimeUtc(ntarget.path, File.GetLastWriteTimeUtc(nsource.path)); - } - else - { - File.SetAttributes(ntarget.path, File.GetAttributes(nsource.path)); - File.SetCreationTimeUtc(ntarget.path, File.GetCreationTimeUtc(nsource.path)); - File.SetLastAccessTimeUtc(ntarget.path, File.GetLastAccessTimeUtc(nsource.path)); - File.SetLastWriteTimeUtc(ntarget.path, File.GetLastWriteTimeUtc(nsource.path)); - } - } - } - catch (cli.System.IO.FileNotFoundException x) - { - throw new NoSuchFileException(x.get_FileName()); - } - catch (cli.System.IO.DirectoryNotFoundException x) - { - throw new NoSuchFileException(nsource.path, ntarget.path, x.getMessage()); - } - catch (cli.System.IO.IOException | cli.System.ArgumentException x) - { - throw new FileSystemException(nsource.path, ntarget.path, x.getMessage()); - } - catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) - { - throw new AccessDeniedException(nsource.path, ntarget.path, x.getMessage()); - } - } - - @cli.System.Security.SecuritySafeCriticalAttribute.Annotation - public void move(Path source, Path target, CopyOption... options) throws IOException - { - NetPath nsource = NetPath.from(source); - NetPath ntarget = NetPath.from(target); - boolean overwrite = false; - boolean atomicMove = false; - for (CopyOption opt : options) - { - if (opt == StandardCopyOption.REPLACE_EXISTING) - { - overwrite = true; - } - else if (opt == StandardCopyOption.ATOMIC_MOVE) - { - if (WINDOWS) - { - atomicMove = true; - } - else - { - throw new AtomicMoveNotSupportedException(nsource.path, ntarget.path, "Unsupported copy option"); - } - } - else - { - // null check - opt.getClass(); - throw new UnsupportedOperationException("Unsupported copy option"); - } - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(nsource.path); - sm.checkWrite(ntarget.path); - } - if (atomicMove) - { - int MOVEFILE_REPLACE_EXISTING = 1; - if (MoveFileEx(nsource.path, ntarget.path, MOVEFILE_REPLACE_EXISTING) == 0) - { - final int ERROR_FILE_NOT_FOUND = 2; - final int ERROR_PATH_NOT_FOUND = 3; - final int ERROR_ACCESS_DENIED = 5; - final int ERROR_NOT_SAME_DEVICE = 17; - final int ERROR_FILE_EXISTS = 80; - final int ERROR_ALREADY_EXISTS = 183; - int lastError = Marshal.GetLastWin32Error(); - switch (lastError) - { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - throw new NoSuchFileException(nsource.path, ntarget.path, null); - case ERROR_ACCESS_DENIED: - throw new AccessDeniedException(nsource.path, ntarget.path, null); - case ERROR_NOT_SAME_DEVICE: - throw new AtomicMoveNotSupportedException(nsource.path, ntarget.path, "Unsupported copy option"); - case ERROR_FILE_EXISTS: - case ERROR_ALREADY_EXISTS: - throw new FileAlreadyExistsException(nsource.path, ntarget.path, null); - default: - throw new FileSystemException(nsource.path, ntarget.path, "Error " + lastError); - } - } - return; - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - if (File.Exists(ntarget.path)) - { - if (!overwrite) - { - throw new FileAlreadyExistsException(ntarget.path); - } - File.Delete(ntarget.path); - } - if (Directory.Exists(ntarget.path)) - { - if (!overwrite) - { - throw new FileAlreadyExistsException(ntarget.path); - } - try - { - if (false) throw new cli.System.IO.IOException(); - Directory.Delete(ntarget.path); - } - catch (cli.System.IO.IOException _) - { - // HACK we assume that the IOException is caused by the directory not being empty - throw new DirectoryNotEmptyException(ntarget.path); - } - } - if (Directory.Exists(nsource.path)) - { - Directory.Move(nsource.path, ntarget.path); - } - else - { - File.Move(nsource.path, ntarget.path); - } - } - catch (cli.System.IO.FileNotFoundException x) - { - throw new NoSuchFileException(x.get_FileName()); - } - catch (cli.System.IO.DirectoryNotFoundException x) - { - throw new NoSuchFileException(nsource.path, ntarget.path, x.getMessage()); - } - catch (cli.System.IO.IOException | cli.System.ArgumentException x) - { - throw new FileSystemException(nsource.path, ntarget.path, x.getMessage()); - } - catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) - { - throw new AccessDeniedException(nsource.path, ntarget.path, x.getMessage()); - } - } - - @DllImportAttribute.Annotation(value="kernel32", SetLastError=true) - private static native int MoveFileEx(String lpExistingFileName, String lpNewFileName, int dwFlags); - - public boolean isSameFile(Path path, Path path2) throws IOException - { - if (path.equals(path2)) - { - return true; - } - if (!(path instanceof NetPath && path2 instanceof NetPath)) - { - // null check - path2.getClass(); - return false; - } - return path.toRealPath().equals(path2.toRealPath()); - } - - public boolean isHidden(Path path) throws IOException - { - String npath = NetPath.from(path).path; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(npath); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - return (File.GetAttributes(npath).Value & (cli.System.IO.FileAttributes.Hidden | cli.System.IO.FileAttributes.Directory)) == cli.System.IO.FileAttributes.Hidden; - } - catch (cli.System.IO.FileNotFoundException x) - { - throw new NoSuchFileException(npath); - } - catch (cli.System.IO.IOException | cli.System.ArgumentException x) - { - throw new IOException(x.getMessage()); - } - } - - private static class NetFileStore extends FileStore - { - private final DriveInfo info; - private final String name; - private final String type; - - NetFileStore(DriveInfo info) throws IOException - { - this.info = info; - try - { - if (false) throw new cli.System.IO.IOException(); - name = info.get_VolumeLabel(); - type = info.get_DriveFormat(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - } - - public Object getAttribute(String attribute) throws IOException - { - switch (attribute) - { - case "totalSpace": - return getTotalSpace(); - case "unallocatedSpace": - return getUnallocatedSpace(); - case "usableSpace": - return getUsableSpace(); - default: - throw new UnsupportedOperationException(); - } - } - - public V getFileStoreAttributeView(Class type) - { - return null; - } - - public long getTotalSpace() throws IOException - { - try - { - if (false) throw new cli.System.IO.IOException(); - return info.get_TotalSize(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - } - - public long getUnallocatedSpace() throws IOException - { - try - { - if (false) throw new cli.System.IO.IOException(); - return info.get_TotalFreeSpace(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - } - - public long getUsableSpace() throws IOException - { - try - { - if (false) throw new cli.System.IO.IOException(); - return info.get_AvailableFreeSpace(); - } - catch (cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - } - - public boolean isReadOnly() - { - return false; - } - - public String name() - { - return name; - } - - public boolean supportsFileAttributeView(Class type) - { - // null check - type.getClass(); - return type == BasicFileAttributeView.class || type == DosFileAttributeView.class; - } - - public boolean supportsFileAttributeView(String name) - { - return name.equals("basic") || name.equals("dos"); - } - - public String type() - { - return type; - } - - public String toString() - { - return name + " (" + info.get_Name().charAt(0) + ":)"; - } - } - - public FileStore getFileStore(Path path) throws IOException - { - NetPath npath = NetPath.from(path.toAbsolutePath()); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(npath.path); - } - return getFileStore(new DriveInfo(npath.path)); - } - - public void checkAccess(Path path, AccessMode... modes) throws IOException - { - String npath = NetPath.from(path).path; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - if (modes.length == 0) - { - sm.checkRead(npath); - } - for (AccessMode m : modes) - { - switch (m) - { - case READ: - sm.checkRead(npath); - break; - case WRITE: - sm.checkWrite(npath); - break; - case EXECUTE: - sm.checkExec(npath); - break; - default: - throw new UnsupportedOperationException(); - } - } - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - // note that File.GetAttributes() works for directories as well - int attr = File.GetAttributes(npath).Value; - for (AccessMode m : modes) - { - switch (m) - { - case READ: - case EXECUTE: - break; - case WRITE: - if ((attr & (cli.System.IO.FileAttributes.ReadOnly | cli.System.IO.FileAttributes.Directory)) == cli.System.IO.FileAttributes.ReadOnly) - { - throw new AccessDeniedException(npath, null, "file has read-only attribute set"); - } - if (getFileStore(path).isReadOnly()) - { - throw new AccessDeniedException(npath, null, "volume is read-only"); - } - break; - default: - throw new UnsupportedOperationException(); - } - } - } - catch (cli.System.IO.FileNotFoundException | cli.System.IO.DirectoryNotFoundException _) - { - throw new NoSuchFileException(npath); - } - catch (cli.System.Security.SecurityException | cli.System.UnauthorizedAccessException x) - { - throw new AccessDeniedException(npath, null, x.getMessage()); - } - catch (cli.System.ArgumentException | cli.System.IO.IOException | cli.System.NotSupportedException x) - { - throw new IOException(x.getMessage()); - } - } - - private static class BasicFileAttributesViewImpl extends AbstractBasicFileAttributeView - { - protected final String path; - - BasicFileAttributesViewImpl(String path) - { - this.path = path; - } - - public BasicFileAttributes readAttributes() throws IOException - { - return DosFileAttributesViewImpl.readAttributesImpl(path); - } - - public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkWrite(path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.NotSupportedException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - if (File.Exists(path)) - { - if (lastModifiedTime != null) - { - File.SetLastWriteTimeUtc(path, toDateTime(lastModifiedTime)); - } - if (lastAccessTime != null) - { - File.SetLastAccessTimeUtc(path, toDateTime(lastAccessTime)); - } - if (createTime != null) - { - File.SetCreationTimeUtc(path, toDateTime(createTime)); - } - } - else if (Directory.Exists(path)) - { - if (lastModifiedTime != null) - { - Directory.SetLastWriteTimeUtc(path, toDateTime(lastModifiedTime)); - } - if (lastAccessTime != null) - { - Directory.SetLastAccessTimeUtc(path, toDateTime(lastAccessTime)); - } - if (createTime != null) - { - Directory.SetCreationTimeUtc(path, toDateTime(createTime)); - } - } - else - { - throw new NoSuchFileException(path); - } - } - catch (cli.System.ArgumentException - | cli.System.IO.IOException - | cli.System.NotSupportedException - | cli.System.Security.SecurityException - | cli.System.UnauthorizedAccessException x) - { - throw new IOException(x.getMessage()); - } - } - } - - private static class DosFileAttributesViewImpl extends BasicFileAttributesViewImpl implements DosFileAttributeView - { - private static final String READONLY_NAME = "readonly"; - private static final String ARCHIVE_NAME = "archive"; - private static final String SYSTEM_NAME = "system"; - private static final String HIDDEN_NAME = "hidden"; - private static final String ATTRIBUTES_NAME = "attributes"; - private static final Set dosAttributeNames = Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME, ATTRIBUTES_NAME); - - DosFileAttributesViewImpl(String path) - { - super(path); - } - - public String name() - { - return "dos"; - } - - public DosFileAttributes readAttributes() throws IOException - { - return readAttributesImpl(path); - } - - private static class DosFileAttributesImpl implements DosFileAttributes - { - private final FileInfo info; - - DosFileAttributesImpl(FileInfo info) - { - this.info = info; - } - - int attributes() - { - return info.get_Attributes().Value; - } - - public FileTime creationTime() - { - return toFileTime(info.get_CreationTimeUtc()); - } - - public Object fileKey() - { - return null; - } - - public boolean isDirectory() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.Directory) != 0; - } - - public boolean isOther() - { - return false; - } - - public boolean isRegularFile() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.Directory) == 0; - } - - public boolean isSymbolicLink() - { - return false; - } - - public FileTime lastAccessTime() - { - return toFileTime(info.get_LastAccessTimeUtc()); - } - - public FileTime lastModifiedTime() - { - return toFileTime(info.get_LastWriteTimeUtc()); - } - - public long size() - { - return info.get_Exists() ? info.get_Length() : 0; - } - - public boolean isArchive() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.Archive) != 0; - } - - public boolean isHidden() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.Hidden) != 0; - } - - public boolean isReadOnly() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.ReadOnly) != 0; - } - - public boolean isSystem() - { - return (info.get_Attributes().Value & cli.System.IO.FileAttributes.System) != 0; - } - } - - static DosFileAttributesImpl readAttributesImpl(String path) throws IOException - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - FileInfo info = new FileInfo(path); - // We have to rely on the (undocumented) fact that FileInfo.Attributes returns -1 - // when the path does not exist. We need this to work for both files and directories - // and this is the only efficient way to do that. - if (info.get_Attributes().Value == -1) - { - throw new NoSuchFileException(path); - } - return new DosFileAttributesImpl(info); - } - catch (cli.System.IO.FileNotFoundException _) - { - throw new NoSuchFileException(path); - } - catch (cli.System.IO.IOException | cli.System.ArgumentException x) - { - throw new IOException(x.getMessage()); - } - catch (cli.System.UnauthorizedAccessException _) - { - throw new AccessDeniedException(path); - } - } - - public void setArchive(boolean value) throws IOException - { - setAttribute(cli.System.IO.FileAttributes.Archive, value); - } - - public void setHidden(boolean value) throws IOException - { - setAttribute(cli.System.IO.FileAttributes.Hidden, value); - } - - public void setReadOnly(boolean value) throws IOException - { - setAttribute(cli.System.IO.FileAttributes.ReadOnly, value); - } - - public void setSystem(boolean value) throws IOException - { - setAttribute(cli.System.IO.FileAttributes.System, value); - } - - private void setAttribute(int attr, boolean value) throws IOException - { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkWrite(path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.IOException(); - FileInfo info = new FileInfo(path); - if (value) - { - info.set_Attributes(cli.System.IO.FileAttributes.wrap(info.get_Attributes().Value | attr)); - } - else - { - info.set_Attributes(cli.System.IO.FileAttributes.wrap(info.get_Attributes().Value & ~attr)); - } - } - catch (cli.System.IO.FileNotFoundException _) - { - throw new NoSuchFileException(path); - } - catch (cli.System.ArgumentException - | cli.System.IO.IOException x) - { - throw new IOException(x.getMessage()); - } - } - - public Map readAttributes(String[] attributes) throws IOException - { - AttributesBuilder builder = AttributesBuilder.create(dosAttributeNames, attributes); - DosFileAttributesImpl attrs = readAttributesImpl(path); - addRequestedBasicAttributes(attrs, builder); - if (builder.match(READONLY_NAME)) - { - builder.add(READONLY_NAME, attrs.isReadOnly()); - } - if (builder.match(ARCHIVE_NAME)) - { - builder.add(ARCHIVE_NAME, attrs.isArchive()); - } - if (builder.match(SYSTEM_NAME)) - { - builder.add(SYSTEM_NAME, attrs.isSystem()); - } - if (builder.match(HIDDEN_NAME)) - { - builder.add(HIDDEN_NAME, attrs.isHidden()); - } - if (builder.match(ATTRIBUTES_NAME)) - { - builder.add(ATTRIBUTES_NAME, attrs.attributes()); - } - return builder.unmodifiableMap(); - } - - public void setAttribute(String attribute, Object value) throws IOException - { - switch (attribute) - { - case READONLY_NAME: - setReadOnly((Boolean)value); - break; - case ARCHIVE_NAME: - setArchive((Boolean)value); - break; - case SYSTEM_NAME: - setSystem((Boolean)value); - break; - case HIDDEN_NAME: - setHidden((Boolean)value); - break; - default: - super.setAttribute(attribute, value); - break; - } - } - } - - private static void validateLinkOption(LinkOption... options) - { - for (LinkOption option : options) - { - if (option == LinkOption.NOFOLLOW_LINKS) - { - // ignored - } - else - { - // null check - option.getClass(); - throw new UnsupportedOperationException(); - } - } - } - - public V getFileAttributeView(Path path, Class type, LinkOption... options) - { - String npath = NetPath.from(path).path; - validateLinkOption(options); - if (type == BasicFileAttributeView.class) - { - return (V)new BasicFileAttributesViewImpl(npath); - } - else if (type == DosFileAttributeView.class) - { - return (V)new DosFileAttributesViewImpl(npath); - } - else - { - // null check - type.getClass(); - return null; - } - } - - public A readAttributes(Path path, Class type, LinkOption... options) throws IOException - { - String npath = NetPath.from(path).path; - // null check - type.getClass(); - validateLinkOption(options); - if (type != BasicFileAttributes.class && type != DosFileAttributes.class) - { - throw new UnsupportedOperationException(); - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkRead(npath); - } - return (A)DosFileAttributesViewImpl.readAttributesImpl(npath); - } - - DynamicFileAttributeView getFileAttributeView(Path file, String name, LinkOption... options) - { - validateLinkOption(options); - if (name.equals("basic")) - { - return new BasicFileAttributesViewImpl(NetPath.from(file).path); - } - else if (name.equals("dos")) - { - return new DosFileAttributesViewImpl(NetPath.from(file).path); - } - else - { - return null; - } - } - - boolean implDelete(Path file, boolean failIfNotExists) throws IOException - { - String path = NetPath.from(file).path; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - { - sm.checkDelete(path); - } - try - { - if (false) throw new cli.System.ArgumentException(); - if (false) throw new cli.System.IO.FileNotFoundException(); - if (false) throw new cli.System.IO.DirectoryNotFoundException(); - if (false) throw new cli.System.IO.IOException(); - if (false) throw new cli.System.Security.SecurityException(); - if (false) throw new cli.System.UnauthorizedAccessException(); - int attr = cli.System.IO.File.GetAttributes(path).Value; - if ((attr & cli.System.IO.FileAttributes.Directory) != 0) - { - try - { - if (false) throw new cli.System.IO.IOException(); - cli.System.IO.Directory.Delete(path); - } - catch (cli.System.IO.IOException _) - { - // HACK we assume that the IOException is caused by the directory not being empty - throw new DirectoryNotEmptyException(path); - } - return true; - } - else - { - cli.System.IO.File.Delete(path); - return true; - } - } - catch (cli.System.ArgumentException x) - { - throw new FileSystemException(path, null, x.getMessage()); - } - catch (cli.System.IO.FileNotFoundException _) - { - if (failIfNotExists) - { - throw new NoSuchFileException(path); - } - else - { - return false; - } - } - catch (cli.System.IO.DirectoryNotFoundException _) - { - if (failIfNotExists) - { - throw new NoSuchFileException(path); - } - else - { - return false; - } - } - catch (cli.System.IO.IOException x) - { - throw new FileSystemException(path, null, x.getMessage()); - } - catch (cli.System.Security.SecurityException _) - { - throw new AccessDeniedException(path); - } - catch (cli.System.UnauthorizedAccessException _) - { - throw new AccessDeniedException(path); - } - } - - static FileTime toFileTime(cli.System.DateTime dateTime) - { - return FileTime.from((dateTime.get_Ticks() - 621355968000000000L) / 10, java.util.concurrent.TimeUnit.MICROSECONDS); - } - - static cli.System.DateTime toDateTime(FileTime fileTime) - { - return new cli.System.DateTime(fileTime.to(java.util.concurrent.TimeUnit.MICROSECONDS) * 10 + 621355968000000000L); - } -} diff --git a/src/IKVM.Java/local/sun/reflect/Reflection.java b/src/IKVM.Java/local/sun/reflect/Reflection.java index 2df2e2d463..59fbe0f27d 100644 --- a/src/IKVM.Java/local/sun/reflect/Reflection.java +++ b/src/IKVM.Java/local/sun/reflect/Reflection.java @@ -58,9 +58,9 @@ public class Reflection { fieldFilterMap = map; methodFilterMap = new HashMap<>(); - // [IKVM] to avoid initialization order issues, we actually add - // Unsafe.getUnsafe() here, instead of in Unsafe's class initializer - methodFilterMap.put(sun.misc.Unsafe.class, new String[] {"getUnsafe"}); + //// [IKVM] to avoid initialization order issues, we actually add + //// Unsafe.getUnsafe() here, instead of in Unsafe's class initializer + //methodFilterMap.put(sun.misc.Unsafe.class, new String[] {"getUnsafe"}); } /** Returns the class of the caller of the method calling this method, diff --git a/src/IKVM.Java/local/sun/security/provider/NativePRNG.java b/src/IKVM.Java/local/sun/security/provider/NativePRNG.java new file mode 100644 index 0000000000..20e65c63ad --- /dev/null +++ b/src/IKVM.Java/local/sun/security/provider/NativePRNG.java @@ -0,0 +1,36 @@ +package sun.security.provider; + +import java.security.SecureRandomSpi; + +public final class NativePRNG extends SecureRandomSpi { + + public static final class NonBlocking { + static boolean isAvailable() { + return false; + } + } + + public static final class Blocking { + static boolean isAvailable() { + return false; + } + } + + static boolean isAvailable() { + return true; + } + + public NativePRNG() { + super(); + } + + @Override + protected native void engineSetSeed(byte[] seed); + + @Override + protected native void engineNextBytes(byte[] bytes); + + @Override + protected native byte[] engineGenerateSeed(int numBytes); + +} diff --git a/src/IKVM.Java/local/sun/security/provider/NativeSeedGenerator.java b/src/IKVM.Java/local/sun/security/provider/NativeSeedGenerator.java new file mode 100644 index 0000000000..fc2ddab95f --- /dev/null +++ b/src/IKVM.Java/local/sun/security/provider/NativeSeedGenerator.java @@ -0,0 +1,14 @@ +package sun.security.provider; + +import java.io.IOException; + +class NativeSeedGenerator extends SeedGenerator { + + NativeSeedGenerator(String seedFile) throws IOException { + super(); + } + + @Override + native void getSeedBytes(byte[] result); + +} diff --git a/src/IKVM.Java/map.xml b/src/IKVM.Java/map.xml index 3c7d953bab..39eab4a394 100644 --- a/src/IKVM.Java/map.xml +++ b/src/IKVM.Java/map.xml @@ -22,11 +22,27 @@ jeroen@frijters.net --> - + - + + + + + Never + + + + + + + + + + + + @@ -109,10 +125,10 @@ + This means we're Cloneable, but not derived from java.lang.Object or java.lang.Throwable. + It also means that we must have been invoked thru reflection (or by code in the java.lang package), + so it is OK to use reflection to call Object.MemberwiseClone(). + --> @@ -202,13 +218,13 @@ + instead, the compiler contains a hack to lazily override Finalize when a class + overrides our finalize + TODO consider adding code to constructor (in debug builds) to detect that someone + has overriden our finalize from another .NET language (if no intermediate Java base class + has overriden finalize, the lazy override of Object.Finalize hasn't kicked in and finalize + will never be called) + --> @@ -216,7 +232,7 @@ + derived object we just ignore it --> @@ -537,20 +553,20 @@ @@ -679,7 +695,7 @@ - + @@ -1342,10 +1358,6 @@ - - Demand - true - @@ -1363,10 +1375,6 @@ - - Demand - true - @@ -1469,14 +1477,6 @@ - - - - - - - - @@ -3175,8 +3175,8 @@ diff --git a/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj b/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj index 50984ed76e..d0ecb25885 100644 --- a/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj +++ b/src/IKVM.MSBuild.Tasks.Tests/IKVM.MSBuild.Tasks.Tests.csproj @@ -1,7 +1,7 @@  - - - + + + net472;netcoreapp3.1;net6.0 @@ -46,6 +46,11 @@ RuntimeIdentifier=linux-x64 ikvmc\netcoreapp3.1\linux-x64 + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmc\netcoreapp3.1\osx-x64 + diff --git a/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj b/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj index f6532c2037..3f12993f47 100644 --- a/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj +++ b/src/IKVM.MSBuild.Tasks/IKVM.MSBuild.Tasks.csproj @@ -1,6 +1,6 @@  - - + + net472;netcoreapp3.1 diff --git a/src/IKVM.MSBuild.Tasks/IkvmCompiler.cs b/src/IKVM.MSBuild.Tasks/IkvmCompiler.cs index 38cf9f4fda..7cd4b145bb 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmCompiler.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmCompiler.cs @@ -267,6 +267,7 @@ protected override async Task ExecuteAsync(IkvmToolTaskDiagnosticWriter wr options.NoParameterReflection = NoParameterReflection; options.Remap = GetAbsolutePathIfNotNull(Remap); + options.NoLogo = true; if (Input != null) foreach (var i in Input) diff --git a/src/IKVM.MSBuild.Tasks/IkvmExporter.cs b/src/IKVM.MSBuild.Tasks/IkvmExporter.cs index f6d2f180af..33728f5753 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmExporter.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmExporter.cs @@ -39,19 +39,23 @@ public class IkvmExporter : IkvmToolExecTask ///

/// Whether errors should be skipped during export. /// - public bool SkipError { get; set; } + public bool ContinueOnError { get; set; } - public bool Shared { get; set; } + public ITaskItem[] Lib { get; set; } - public bool NoStdLib { get; set; } + public bool IncludeNonPublicTypes { get; set; } - public ITaskItem[] Lib { get; set; } + public bool IncludeNonPublicInterfaces { get; set; } - public bool Forwarders { get; set; } + public bool IncludeNonPublicMembers { get; set; } + + public bool IncludeParameterNames { get; set; } - public bool Parameters { get; set; } + public bool Shared { get; set; } - public bool JApi { get; set; } + public bool NoStdLib { get; set; } + + public bool Forwarders { get; set; } public bool Bootstrap { get; set; } @@ -70,17 +74,20 @@ protected override async Task ExecuteAsync(IkvmToolTaskDiagnosticWriter wr foreach (var resource in Namespaces) options.Namespaces.Add(resource.ItemSpec); - options.SkipError = SkipError; - options.Shared = Shared; - options.NoStdLib = NoStdLib; + options.ContinueOnError = ContinueOnError; if (Lib is not null) foreach (var i in Lib) options.Lib.Add(i.ItemSpec); + options.IncludeNonPublicTypes = IncludeNonPublicTypes; + options.IncludeNonPublicInterfaces = IncludeNonPublicInterfaces; + options.IncludeNonPublicMembers = IncludeNonPublicMembers; + options.IncludeParameterNames = IncludeParameterNames; + + options.Shared = Shared; + options.NoStdLib = NoStdLib; options.Forwarders = Forwarders; - options.Parameters = Parameters; - options.JApi = JApi; options.Bootstrap = Bootstrap; // kick off the launcher with the configured options diff --git a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItem.cs b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItem.cs index 92e3c68758..589c26a995 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItem.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItem.cs @@ -38,8 +38,10 @@ public static IkvmReferenceExportItem[] Import(IEnumerable items) item.Shared = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.Shared), "true", StringComparison.OrdinalIgnoreCase); item.NoStdLib = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.NoStdLib), "true", StringComparison.OrdinalIgnoreCase); item.Forwarders = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.Forwarders), "true", StringComparison.OrdinalIgnoreCase); - item.Parameters = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.Parameters), "true", StringComparison.OrdinalIgnoreCase); - item.JApi = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.JApi), "true", StringComparison.OrdinalIgnoreCase); + item.IncludeNonPublicTypes = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicTypes), "true", StringComparison.OrdinalIgnoreCase); + item.IncludeNonPublicInterfaces = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicInterfaces), "true", StringComparison.OrdinalIgnoreCase); + item.IncludeNonPublicMembers = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicMembers), "true", StringComparison.OrdinalIgnoreCase); + item.IncludeParameterNames = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.IncludeParameterNames), "true", StringComparison.OrdinalIgnoreCase); item.Bootstrap = string.Equals(item.Item.GetMetadata(IkvmReferenceExportItemMetadata.Bootstrap), "true", StringComparison.OrdinalIgnoreCase); item.IkvmIdentity = item.Item.GetMetadata(IkvmReferenceExportItemMetadata.IkvmIdentity); item.RandomIndex = item.Item.GetMetadata(IkvmReferenceExportItemMetadata.RandomIndex) is string s && int.TryParse(s, out var i) ? i : null; @@ -78,7 +80,7 @@ public IkvmReferenceExportItem(ITaskItem item) /// /// Paths to libraries. /// - public List Libraries { get; set; } + public List Libraries { get; set; } = new List(); /// /// Namespaces to export. @@ -91,9 +93,11 @@ public IkvmReferenceExportItem(ITaskItem item) public bool Forwarders { get; set; } - public bool Parameters { get; set; } + public bool IncludeNonPublicTypes { get; set; } + public bool IncludeNonPublicInterfaces { get; set; } + public bool IncludeNonPublicMembers { get; set; } - public bool JApi { get; set; } + public bool IncludeParameterNames { get; set; } public bool Bootstrap { get; set; } @@ -116,8 +120,10 @@ public void Save() Item.SetMetadata(IkvmReferenceExportItemMetadata.Shared, Shared ? "true" : "false"); Item.SetMetadata(IkvmReferenceExportItemMetadata.NoStdLib, NoStdLib ? "true" : "false"); Item.SetMetadata(IkvmReferenceExportItemMetadata.Forwarders, Forwarders ? "true" : "false"); - Item.SetMetadata(IkvmReferenceExportItemMetadata.Parameters, Parameters ? "true" : "false"); - Item.SetMetadata(IkvmReferenceExportItemMetadata.JApi, JApi ? "true" : "false"); + Item.SetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicTypes, IncludeNonPublicTypes ? "true" : "false"); + Item.SetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicInterfaces, IncludeNonPublicInterfaces ? "true" : "false"); + Item.SetMetadata(IkvmReferenceExportItemMetadata.IncludeNonPublicMembers, IncludeNonPublicMembers ? "true" : "false"); + Item.SetMetadata(IkvmReferenceExportItemMetadata.IncludeParameterNames, IncludeParameterNames ? "true" : "false"); Item.SetMetadata(IkvmReferenceExportItemMetadata.Bootstrap, Bootstrap ? "true" : "false"); Item.SetMetadata(IkvmReferenceExportItemMetadata.IkvmIdentity, IkvmIdentity); Item.SetMetadata(IkvmReferenceExportItemMetadata.RandomIndex, RandomIndex?.ToString()); diff --git a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemMetadata.cs b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemMetadata.cs index 0a04c4906e..574e85ab7e 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemMetadata.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemMetadata.cs @@ -13,8 +13,10 @@ static class IkvmReferenceExportItemMetadata public static readonly string NoStdLib = "NoStdLib"; public static readonly string Libraries = "Libraries"; public static readonly string Forwarders = "Forwarders"; - public static readonly string Parameters = "Parameters"; - public static readonly string JApi = "JApi"; + public static readonly string IncludeNonPublicTypes = "IncludeNonPublicTypes"; + public static readonly string IncludeNonPublicInterfaces = "IncludeNonPublicInterfaces"; + public static readonly string IncludeNonPublicMembers = "IncludeNonPublicMembers"; + public static readonly string IncludeParameterNames = "IncludeParameterNames"; public static readonly string Bootstrap = "Bootstrap"; public static readonly string IkvmIdentity = "IkvmIdentity"; public static readonly string RandomIndex = "RandomIndex"; diff --git a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemPrepare.cs b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemPrepare.cs index 8084c2ec09..b6d2e8e2d0 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemPrepare.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmReferenceExportItemPrepare.cs @@ -12,6 +12,7 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; + using System.Threading; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -22,9 +23,40 @@ public class IkvmReferenceExportItemPrepare : Task { + /// + /// Defines the cached information per assembly. + /// + struct AssemblyInfo + { + + /// + /// Initializes a new instance. + /// + /// + /// + public AssemblyInfo(string name, List references) + { + Name = name; + References = references; + } + + /// + /// Name of the assembly. + /// + public string Name { get; set; } + + /// + /// Names of the references of the assembly. + /// + public List References { get; set; } + + } + readonly static RandomNumberGenerator rng = RandomNumberGenerator.Create(); + readonly static MD5 md5 = MD5.Create(); - readonly static ConcurrentDictionary<(string, DateTime), string> fileIdentityCache = new ConcurrentDictionary<(string, DateTime), string>(); + readonly static ConcurrentDictionary<(string, DateTime), System.Threading.Tasks.Task> fileIdentityCache = new ConcurrentDictionary<(string, DateTime), System.Threading.Tasks.Task>(); + readonly static ConcurrentDictionary> assemblyInfoCache = new ConcurrentDictionary>(); /// /// Calculates the hash of the value. @@ -90,9 +122,40 @@ public IkvmReferenceExportItemPrepare() /// /// public override bool Execute() + { + // kick off the launcher with the configured options + var run = ExecuteAsync(CancellationToken.None); + + // return immediately if finished + if (run.IsCompleted) + return run.GetAwaiter().GetResult(); + + // yield and wait for the task to complete + BuildEngine3.Yield(); + + // wait for result, and ensure we reacquire in case of return value or exception + var result = false; + try + { + result = run.GetAwaiter().GetResult(); + } + finally + { + BuildEngine3.Reacquire(); + } + + // check that we exited successfully + return result; + } + + /// + /// Executes the task. + /// + /// + async System.Threading.Tasks.Task ExecuteAsync(CancellationToken cancellationToken) { var items = IkvmReferenceExportItem.Import(Items); - AssignBuildInfo(items); + await AssignBuildInfoAsync(items, cancellationToken); Items = items.OrderBy(i => i.RandomIndex).Select(i => i.Item).ToArray(); // randomize order to allow multiple processes to interleave return true; } @@ -101,9 +164,9 @@ public override bool Execute() /// Assigns build information to the items. /// /// - internal void AssignBuildInfo(IEnumerable items) + internal System.Threading.Tasks.Task AssignBuildInfoAsync(IEnumerable items, CancellationToken cancellationToken) { - items.AsParallel().ForAll(AssignBuildInfo); + return System.Threading.Tasks.Task.WhenAll(items.Select(i => AssignBuildInfoAsync(i, cancellationToken))); } /// @@ -121,19 +184,64 @@ int GetRandomNumber() /// Assigns build information to the item. /// /// - internal void AssignBuildInfo(IkvmReferenceExportItem item) + /// + internal async System.Threading.Tasks.Task AssignBuildInfoAsync(IkvmReferenceExportItem item, CancellationToken cancellationToken) { - item.IkvmIdentity = CalculateIkvmIdentity(item); + item.References = await CalculateReferencesAsync(item, cancellationToken); + item.Libraries = await CalculateLibrariesAsync(item, cancellationToken); + item.IkvmIdentity = await CalculateIkvmIdentityAsync(item, cancellationToken); item.RandomIndex ??= GetRandomNumber(); item.Save(); } + /// + /// Calculates the direct references of the given item. + /// + /// + /// + /// + async System.Threading.Tasks.Task> CalculateReferencesAsync(IkvmReferenceExportItem item, CancellationToken cancellationToken) + { + var referencesList = new HashSet() { item.ItemSpec }; + + if (References != null) + foreach (var reference in References) + referencesList.Add(reference.ItemSpec); + + if (item.References != null) + foreach (var reference in item.References) + referencesList.Add(reference); + + var references = await GetAssemblyReferencesAsync(item.ItemSpec, referencesList, cancellationToken); + return references.OrderBy(i => i).ToList(); + } + + /// + /// Calculates the direct libraries of the given item. + /// + /// + /// + /// + System.Threading.Tasks.Task> CalculateLibrariesAsync(IkvmReferenceExportItem item, CancellationToken cancellationToken) + { + // gather library lines + var libraries = new HashSet(); + if (Libraries != null) + foreach (var library in Libraries) + libraries.Add(library.ItemSpec); + if (item.Libraries != null) + foreach (var library in item.Libraries) + libraries.Add(library); + + return System.Threading.Tasks.Task.FromResult(libraries.OrderBy(i => i).ToList()); + } + /// /// Calculates the hash for the given item. /// /// /// - internal string CalculateIkvmIdentity(IkvmReferenceExportItem item) + internal async System.Threading.Tasks.Task CalculateIkvmIdentityAsync(IkvmReferenceExportItem item, CancellationToken cancellationToken) { if (item is null) throw new ArgumentNullException(nameof(item)); @@ -149,45 +257,98 @@ internal string CalculateIkvmIdentity(IkvmReferenceExportItem item) writer.WriteLine("Shared={0}", item.Shared); writer.WriteLine("NoStdLib={0}", item.NoStdLib); writer.WriteLine("Forwarders={0}", item.Forwarders); - writer.WriteLine("Parameters={0}", item.Parameters); - writer.WriteLine("JApi={0}", item.JApi); + writer.WriteLine("IncludeNonPublicTypes={0}", item.IncludeNonPublicTypes); + writer.WriteLine("IncludeNonPublicInterfaces={0}", item.IncludeNonPublicInterfaces); + writer.WriteLine("IncludeNonPublicMembers={0}", item.IncludeNonPublicMembers); + writer.WriteLine("IncludeParameterNames={0}", item.IncludeParameterNames); writer.WriteLine("Bootstrap={0}", item.Bootstrap); - // gather reference lines - var references = new List(16); - if (References != null) - foreach (var reference in References) - references.Add(GetIdentity(item, reference.ItemSpec)); - if (item.References != null) - foreach (var reference in item.References) - references.Add(GetIdentity(item, reference)); - - // write sorted reference lines - foreach (var reference in references.OrderBy(i => i)) - writer.WriteLine($"Reference={reference}"); + // traverse the reference set for references that are actually referenced + foreach (var reference in item.References) + writer.WriteLine($"Reference={await GetIdentityAsync(item, reference, cancellationToken)}"); // gather library lines - var libraries = new List(16); - if (Libraries != null) - foreach (var library in Libraries) - libraries.Add(GetIdentity(item, library.ItemSpec)); - if (item.Libraries != null) - foreach (var library in item.Libraries) - libraries.Add(GetIdentity(item, library)); - - // write sorted library lines - foreach (var library in libraries.OrderBy(i => i)) - writer.WriteLine($"Library={library}"); + foreach (var library in item.Libraries) + writer.WriteLine($"Library={await GetIdentityAsync(item, library, cancellationToken)}"); // gather namespaces if (item.Namespaces != null) - foreach (var ns in item.Namespaces.OrderBy(i => i)) - libraries.Add($"Namespace={ns}"); + foreach (var ns in item.Namespaces.Distinct().OrderBy(i => i)) + writer.WriteLine($"Namespace={ns}"); // hash the resulting manifest and set the identity return GetHashForString(writer.ToString()); } + /// + /// Finds all of the direct and indirect references of the given assembly. + /// + /// + /// + /// + /// + async System.Threading.Tasks.Task> GetAssemblyReferencesAsync(string path, IEnumerable referencesList, CancellationToken cancellationToken) + { + var hs = new HashSet(); + + // recurse into references of path + await BuildAssemblyReferencesAsync(path, referencesList, hs, cancellationToken); + + // ensure the required libraries are present + foreach (var n in new[] { "mscorlib", "netstandard", "IKVM.Runtime", "IKVM.Java" }) + { + foreach (var i in referencesList) + if ((await GetAssemblyInfoAsync(i)).Name == n) + hs.Add(i); + } + + return hs; + } + + /// + /// Finds all of the direct and indirect references of the given assembly. + /// + /// + /// + /// + /// + /// + async System.Threading.Tasks.Task BuildAssemblyReferencesAsync(string path, IEnumerable referencesList, HashSet hs, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + foreach (var reference in (await GetAssemblyInfoAsync(path)).References) + foreach (var i in referencesList) + if ((await GetAssemblyInfoAsync(i)).Name == reference) + if (hs.Add(i)) + await BuildAssemblyReferencesAsync(i, referencesList, hs, cancellationToken); + } + + /// + /// Gets the assembly info for the given assembly path. + /// + /// + /// + System.Threading.Tasks.Task GetAssemblyInfoAsync(string path) + { + return assemblyInfoCache.GetOrAdd(path, p => System.Threading.Tasks.Task.Run(() => ReadAssemblyInfo(p))); + } + + /// + /// Reads the assembly info from the given assembly path. + /// + /// + /// + /// + AssemblyInfo ReadAssemblyInfo(string path) + { + using var fsstm = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + using var perdr = new PEReader(fsstm); + var mrdr = perdr.GetMetadataReader(); + + return new AssemblyInfo(mrdr.GetString(mrdr.GetAssemblyDefinition().Name), mrdr.AssemblyReferences.Select(i => mrdr.GetString(mrdr.GetAssemblyReference(i).Name)).ToList()); + } + /// /// Gets the hash value for the given file. /// @@ -211,7 +372,7 @@ string GetHashForString(string value) ///
/// /// - string CreateIdentityForFile(string file) + async System.Threading.Tasks.Task CreateIdentityForFileAsync(string file) { if (string.IsNullOrWhiteSpace(file)) throw new ArgumentException($"'{nameof(file)}' cannot be null or whitespace.", nameof(file)); @@ -232,7 +393,7 @@ string CreateIdentityForFile(string file) // if the file is potentially a .NET assembly if (Path.GetExtension(file) == ".dll" || Path.GetExtension(file) == ".exe") - if (TryGetIdentityForAssembly(file) is string h) + if (await TryGetIdentityForAssemblyAsync(file) is string h) return h; // fallback to a standard full MD5 of the file @@ -250,35 +411,39 @@ string CreateIdentityForFile(string file) /// /// /// - string TryGetIdentityForAssembly(string file) + System.Threading.Tasks.Task TryGetIdentityForAssemblyAsync(string file) { - try + return System.Threading.Tasks.Task.Run(() => { - using var fsstm = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); - using var perdr = new PEReader(fsstm); - var mrdr = perdr.GetMetadataReader(); - var mvid = mrdr.GetGuid(mrdr.GetModuleDefinition().Mvid); - return $"MVID:{mvid}"; - } - catch - { - return null; - } + try + { + using var fsstm = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); + using var perdr = new PEReader(fsstm); + var mrdr = perdr.GetMetadataReader(); + var mvid = mrdr.GetGuid(mrdr.GetModuleDefinition().Mvid); + return $"MVID:{mvid}"; + } + catch + { + return null; + } + }); } /// /// Gets the hash value for the given file. /// /// + /// /// - string GetIdentityForFile(string file) + System.Threading.Tasks.Task GetIdentityForFileAsync(string file, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(file)) throw new ArgumentException($"'{nameof(file)}' cannot be null or whitespace.", nameof(file)); if (File.Exists(file) == false) throw new FileNotFoundException($"Could not find file '{file}'."); - return fileIdentityCache.GetOrAdd((file, File.GetLastWriteTimeUtc(file)), _ => CreateIdentityForFile(_.Item1)); + return fileIdentityCache.GetOrAdd((file, File.GetLastWriteTimeUtc(file)), _ => CreateIdentityForFileAsync(_.Item1)); } /// @@ -286,7 +451,8 @@ string GetIdentityForFile(string file) /// /// /// - string GetIdentity(IkvmReferenceExportItem item, string value) + /// + async System.Threading.Tasks.Task GetIdentityAsync(IkvmReferenceExportItem item, string value, CancellationToken cancellationToken) { if (item is null) throw new ArgumentNullException(nameof(item)); @@ -303,7 +469,7 @@ string GetIdentity(IkvmReferenceExportItem item, string value) // others should exist if (File.Exists(value)) - return GetIdentityForFile(value); + return await GetIdentityForFileAsync(value, cancellationToken); throw new Exception($"Could not resolve identity for '{value}'."); } diff --git a/src/IKVM.MSBuild.Tasks/IkvmToolExecTask.cs b/src/IKVM.MSBuild.Tasks/IkvmToolExecTask.cs index 5ebbd20137..0e3224c057 100644 --- a/src/IKVM.MSBuild.Tasks/IkvmToolExecTask.cs +++ b/src/IKVM.MSBuild.Tasks/IkvmToolExecTask.cs @@ -32,6 +32,13 @@ static IkvmToolExecTask() else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) NativeLibrary.Load(Path.Combine(Path.GetDirectoryName(typeof(IkvmToolExecTask).Assembly.Location), "runtimes", "linux-arm64", "native", "libMono.Unix.so")); } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + NativeLibrary.Load(Path.Combine(Path.GetDirectoryName(typeof(IkvmToolExecTask).Assembly.Location), "runtimes", "osx-x64", "native", "libMono.Unix.dylib")); + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + NativeLibrary.Load(Path.Combine(Path.GetDirectoryName(typeof(IkvmToolExecTask).Assembly.Location), "runtimes", "osx-arm64", "native", "libMono.Unix.dylib")); + } } #endif diff --git a/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj b/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj index b15727a8de..2bf9111ad9 100644 --- a/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj +++ b/src/IKVM.MSBuild.Tests/IKVM.MSBuild.Tests.csproj @@ -36,6 +36,7 @@ + @@ -45,6 +46,7 @@ + @@ -52,6 +54,7 @@ + diff --git a/src/IKVM.MSBuild.Tests/Project/Exe/ProjectExe.csproj b/src/IKVM.MSBuild.Tests/Project/Exe/ProjectExe.csproj index 61b8a24f54..2df850eedf 100644 --- a/src/IKVM.MSBuild.Tests/Project/Exe/ProjectExe.csproj +++ b/src/IKVM.MSBuild.Tests/Project/Exe/ProjectExe.csproj @@ -9,7 +9,7 @@ Exe net461;net472;net48;netcoreapp3.1;net6.0 - win7-x86;win7-x64;win81-arm;linux-x64;linux-arm;linux-arm64 + win7-x86;win7-x64;win81-arm;linux-x64;linux-arm;linux-arm64;osx-x64 AnyCPU diff --git a/src/IKVM.MSBuild.Tests/ProjectTests.cs b/src/IKVM.MSBuild.Tests/ProjectTests.cs index 14c2820be6..c4534c2c49 100644 --- a/src/IKVM.MSBuild.Tests/ProjectTests.cs +++ b/src/IKVM.MSBuild.Tests/ProjectTests.cs @@ -72,6 +72,10 @@ public void Can_build_test_project() new XAttribute("key", "globalPackagesFolder"), new XAttribute("value", nugetPackageRoot))), new XElement("packageSources", + new XElement("clear"), + new XElement("add", + new XAttribute("key", "nuget.org"), + new XAttribute("value", "https://api.nuget.org/v3/index.json")), new XElement("add", new XAttribute("key", "dev"), new XAttribute("value", Path.Combine(Path.GetDirectoryName(typeof(ProjectTests).Assembly.Location), @"nuget"))), @@ -125,6 +129,8 @@ public void Can_build_test_project() ("net6.0", "linux-arm"), ("netcoreapp3.1", "linux-arm64"), ("net6.0", "linux-arm64"), + ("netcoreapp3.1", "osx-x64"), + ("net6.0", "osx-x64"), }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) @@ -143,6 +149,8 @@ public void Can_build_test_project() ("net6.0", "linux-arm"), ("netcoreapp3.1", "linux-arm64"), ("net6.0", "linux-arm64"), + ("netcoreapp3.1", "osx-x64"), + ("net6.0", "osx-x64"), }; } diff --git a/src/IKVM.MSBuild.Tools.runtime.linux-arm/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm.props b/src/IKVM.MSBuild.Tools.runtime.linux-arm/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm.props index 2e578d0479..731b5404dc 100644 --- a/src/IKVM.MSBuild.Tools.runtime.linux-arm/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm.props +++ b/src/IKVM.MSBuild.Tools.runtime.linux-arm/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm.props @@ -4,7 +4,9 @@ + + diff --git a/src/IKVM.MSBuild.Tools.runtime.linux-arm64/IKVM.MSBuild.Tools.runtime.linux-arm64.csproj b/src/IKVM.MSBuild.Tools.runtime.linux-arm64/IKVM.MSBuild.Tools.runtime.linux-arm64.csproj index d769c2a39a..7ae22f58b6 100644 --- a/src/IKVM.MSBuild.Tools.runtime.linux-arm64/IKVM.MSBuild.Tools.runtime.linux-arm64.csproj +++ b/src/IKVM.MSBuild.Tools.runtime.linux-arm64/IKVM.MSBuild.Tools.runtime.linux-arm64.csproj @@ -21,21 +21,11 @@ - TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 ikvmc\netcoreapp3.1\linux-arm64 - TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 diff --git a/src/IKVM.MSBuild.Tools.runtime.linux-arm64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm64.props b/src/IKVM.MSBuild.Tools.runtime.linux-arm64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm64.props index 45d8f21c88..7ff817fe3b 100644 --- a/src/IKVM.MSBuild.Tools.runtime.linux-arm64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm64.props +++ b/src/IKVM.MSBuild.Tools.runtime.linux-arm64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-arm64.props @@ -5,6 +5,8 @@ + + diff --git a/src/IKVM.MSBuild.Tools.runtime.linux-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-x64.props b/src/IKVM.MSBuild.Tools.runtime.linux-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-x64.props index 7bedc04bdb..1145de2377 100644 --- a/src/IKVM.MSBuild.Tools.runtime.linux-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-x64.props +++ b/src/IKVM.MSBuild.Tools.runtime.linux-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.linux-x64.props @@ -4,7 +4,9 @@ + + diff --git a/src/IKVM.MSBuild.Tools.runtime.osx-x64/IKVM.MSBuild.Tools.runtime.osx-x64.csproj b/src/IKVM.MSBuild.Tools.runtime.osx-x64/IKVM.MSBuild.Tools.runtime.osx-x64.csproj new file mode 100644 index 0000000000..deb20dcf4b --- /dev/null +++ b/src/IKVM.MSBuild.Tools.runtime.osx-x64/IKVM.MSBuild.Tools.runtime.osx-x64.csproj @@ -0,0 +1,46 @@ + + + net461;netcoreapp3.1 + LICENSE.md + README.md + IKVM MSBuild Tools + + + + true + + + + + + + + + + + + + + + TargetFramework=net461 + RuntimeIdentifier=osx-x64 + ikvmc\net461\osx-x64 + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmc\netcoreapp3.1\osx-x64 + + + TargetFramework=net461 + RuntimeIdentifier=osx-x64 + ikvmstub\net461\osx-x64 + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmstub\netcoreapp3.1\osx-x64 + + + + diff --git a/src/IKVM.MSBuild.Tools.runtime.osx-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.osx-x64.props b/src/IKVM.MSBuild.Tools.runtime.osx-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.osx-x64.props new file mode 100644 index 0000000000..6e4fdf7631 --- /dev/null +++ b/src/IKVM.MSBuild.Tools.runtime.osx-x64/buildTransitive/netstandard2.0/IKVM.MSBuild.Tools.runtime.osx-x64.props @@ -0,0 +1,13 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + + + + + diff --git a/src/IKVM.MSBuild.Tools/IKVM.MSBuild.Tools.csproj b/src/IKVM.MSBuild.Tools/IKVM.MSBuild.Tools.csproj index bd13874a3a..c2c5d77417 100644 --- a/src/IKVM.MSBuild.Tools/IKVM.MSBuild.Tools.csproj +++ b/src/IKVM.MSBuild.Tools/IKVM.MSBuild.Tools.csproj @@ -11,16 +11,19 @@ - + true - + true - + true - + + true + + true diff --git a/src/IKVM.MSBuild/buildTransitive/netstandard2.0/IKVM.MSBuild.targets b/src/IKVM.MSBuild/buildTransitive/netstandard2.0/IKVM.MSBuild.targets index 27926b87d9..89f4d55c01 100644 --- a/src/IKVM.MSBuild/buildTransitive/netstandard2.0/IKVM.MSBuild.targets +++ b/src/IKVM.MSBuild/buildTransitive/netstandard2.0/IKVM.MSBuild.targets @@ -10,6 +10,8 @@ win7-x86 win7-x64 linux-x64 + osx-x64 + osx-arm64 diff --git a/src/IKVM.MSBuild/lib/net47/_._ b/src/IKVM.MSBuild/lib/net47/_._ new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/IKVM.NET.Sdk.Tests/IKVM.NET.Sdk.Tests.csproj b/src/IKVM.NET.Sdk.Tests/IKVM.NET.Sdk.Tests.csproj index 09be051ddd..c682623d53 100644 --- a/src/IKVM.NET.Sdk.Tests/IKVM.NET.Sdk.Tests.csproj +++ b/src/IKVM.NET.Sdk.Tests/IKVM.NET.Sdk.Tests.csproj @@ -27,6 +27,7 @@ + @@ -37,6 +38,7 @@ + @@ -44,6 +46,7 @@ + diff --git a/src/IKVM.NET.Sdk.Tests/Project/Exe/ProjectExe.msbuildproj b/src/IKVM.NET.Sdk.Tests/Project/Exe/ProjectExe.msbuildproj index 5ce2c55bb8..449fa0dd7e 100644 --- a/src/IKVM.NET.Sdk.Tests/Project/Exe/ProjectExe.msbuildproj +++ b/src/IKVM.NET.Sdk.Tests/Project/Exe/ProjectExe.msbuildproj @@ -4,7 +4,7 @@ Exe net461;netcoreapp3.1 - win7-x86;win7-x64;win81-arm;linux-x64;linux-arm;linux-arm64 + win7-x86;win7-x64;win81-arm;linux-x64;linux-arm;linux-arm64;osx-x64 project.exe project.exe.Main diff --git a/src/IKVM.NET.Sdk.Tests/ProjectTests.cs b/src/IKVM.NET.Sdk.Tests/ProjectTests.cs index d8cbce34db..15ea12122a 100644 --- a/src/IKVM.NET.Sdk.Tests/ProjectTests.cs +++ b/src/IKVM.NET.Sdk.Tests/ProjectTests.cs @@ -73,6 +73,10 @@ public void Can_build_test_project() new XAttribute("key", "globalPackagesFolder"), new XAttribute("value", nugetPackageRoot))), new XElement("packageSources", + new XElement("clear"), + new XElement("add", + new XAttribute("key", "nuget.org"), + new XAttribute("value", "https://api.nuget.org/v3/index.json")), new XElement("add", new XAttribute("key", "dev"), new XAttribute("value", Path.Combine(Path.GetDirectoryName(typeof(ProjectTests).Assembly.Location), @"nuget"))), @@ -113,6 +117,7 @@ public void Can_build_test_project() ("netcoreapp3.1", "linux-x64"), ("netcoreapp3.1", "linux-arm"), ("netcoreapp3.1", "linux-arm64"), + ("netcoreapp3.1", "osx-x64"), }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) @@ -125,6 +130,7 @@ public void Can_build_test_project() ("netcoreapp3.1", "linux-x64"), ("netcoreapp3.1", "linux-arm"), ("netcoreapp3.1", "linux-arm64"), + ("netcoreapp3.1", "osx-x64"), }; } diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets index 6f369afa66..7d323d8e47 100644 --- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets +++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.NoTasks.targets @@ -28,14 +28,31 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) + + + + + + + + + i.ItemSpec).ToArray(); + ]]> + + + + - + <_ReferenceExportReference Remove="@(_ReferenceExportReference)" /> - <_ReferenceExportReference Include="@(ReferencePathWithRefAssemblies)" Condition=" '%(Filename)' != '%(ReferenceExport.Filename)' "/> + <_ReferenceExportReference Include="%(ReferenceExport.References)" /> + <_ReferenceExportLibrary Remove="@(_ReferenceExportLibrary)" /> + <_ReferenceExportLibrary Include="%(ReferenceExport.Libraries)" /> @@ -44,10 +61,15 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_IkvmExporterArgs Remove="@(_IkvmExporterArgs)" /> - <_IkvmExporterArgs Include="-bootstrap" Condition=" '%(ReferenceExport.Bootstrap)' == 'true' " /> - <_IkvmExporterArgs Include="@(_ReferenceExportReference->'-r:"%(FullPath)"')" /> - <_IkvmExporterArgs Include="-nostdlib" Condition=" '%(ReferenceExport.NoStdLib)' == 'true' " /> - <_IkvmExporterArgs Include="-out:"%(ReferenceExport.StagePath)"" /> + <_IkvmExporterArgs Include="--bootstrap" Condition=" '%(ReferenceExport.Bootstrap)' == 'true' " /> + <_IkvmExporterArgs Include="--nostdlib" Condition=" '%(ReferenceExport.NoStdLib)' == 'true' " /> + <_IkvmExporterArgs Include="--non-public-types" Condition=" '%(ReferenceExport.IncludeNonPublicTypes)' == 'true' " /> + <_IkvmExporterArgs Include="--non-public-interfaces" Condition=" '%(ReferenceExport.IncludeNonPublicInterfaces)' == 'true' " /> + <_IkvmExporterArgs Include="--non-public-members" Condition=" '%(ReferenceExport.IncludeNonPublicMembers)' == 'true' " /> + <_IkvmExporterArgs Include="--parameters" Condition=" '%(ReferenceExport.IncludeParameterNames)' == 'true' " /> + <_IkvmExporterArgs Include="@(_ReferenceExportReference->'--reference:"%(FullPath)"')" Condition=" '@(_ReferenceExportReference)' != '' " /> + <_IkvmExporterArgs Include="@(_ReferenceExportLibrary->'--library:"%(FullPath)"')" Condition=" '@(_ReferenceExportLibrary)' != '' " /> + <_IkvmExporterArgs Include="--out:"%(ReferenceExport.StagePath)"" /> <_IkvmExporterArgs Include=""%(ReferenceExport.Identity)"" /> @@ -55,6 +77,7 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) + @@ -62,10 +85,18 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) - + + + + + + + + + <_JavaCompilerArgs Remove="@(_JavaCompilerArgs)" /> <_JavaCompilerArgs Include="-g" Condition=" '$(DebugSymbols)' == 'true' Or '$(DebugType)' != 'none' " /> @@ -74,9 +105,9 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_JavaCompilerArgs Include="-parameters" /> <_JavaCompilerArgs Include="-cp" /> <_JavaCompilerArgs Include="null" Condition=" '$(Bootstrap)' == 'true' " /> - <_JavaCompilerArgs Include="@(Classpath, '$([System.IO.Path]::PathSeparator)')" Condition=" '$(Bootstrap)' != 'true' And '@(Classpath)' != '' " /> - <_JavaCompilerArgs Include="-bootclasspath" Condition=" '$(Bootstrap)' == 'true' And '@(Classpath)' != '' " /> - <_JavaCompilerArgs Include="@(Classpath, '$([System.IO.Path]::PathSeparator)')" Condition=" '$(Bootstrap)' == 'true' And '@(Classpath)' != '' " /> + <_JavaCompilerArgs Include="@(_JavaCompilerArgsClasspath, '$([System.IO.Path]::PathSeparator)')" Condition=" '$(Bootstrap)' != 'true' And '@(_JavaCompilerArgsClasspath)' != '' " /> + <_JavaCompilerArgs Include="-bootclasspath" Condition=" '$(Bootstrap)' == 'true' And '@(_JavaCompilerArgsClasspath)' != '' " /> + <_JavaCompilerArgs Include="@(_JavaCompilerArgsClasspath, '$([System.IO.Path]::PathSeparator)')" Condition=" '$(Bootstrap)' == 'true' And '@(_JavaCompilerArgsClasspath)' != '' " /> <_JavaCompilerArgs Include="-source" /> <_JavaCompilerArgs Include="$(LangVersion)" /> <_JavaCompilerArgs Include="-target" /> @@ -84,10 +115,20 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_JavaCompilerArgs Include="-d" /> <_JavaCompilerArgs Include="$([System.IO.Path]::GetFullPath('$(ClassOutputPath)'))" /> - <_JavaCompilerArgs Include="@(Compile->'%(FullPath)')" /> + <_JavaCompilerArgs Include="@(_JavaCompilerArgsCompile->'%(FullPath)')" /> - + + + + + _CompileJavaResponseFile; + $(CompileJavaDependsOn) + + + + + <_CompileJavaStartTime>$([System.DateTime]::Now.Ticks) @@ -100,24 +141,21 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_CompileJavaClassFiles Include="$(ClassOutputPath)**\*.class" /> - <_CompileJavaStaleFiles Include="@(_CompileJavaClassFiles)" Condition="$([System.DateTime]::Parse('%(ModifiedTime)').Ticks) < $(_CompileJavaStartTime) " /> + <_CompileJavaStaleFiles Include="@(_CompileJavaClassFiles)" Condition=" '%(ModifiedTime)' == '' Or $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) < $(_CompileJavaStartTime) " /> - + - - - - + - - + + <_ClassToCompile Include="@(Class)" /> @@ -126,8 +164,26 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_ClassToCompile Remove="@(_AssemblyAttributesClass)" /> + + <_Target>$(OutputType.ToLowerInvariant()) + <_Target Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netcoreapp3.1'))">library + + + + + + + + + + + + + + <_IkvmCompilerArgs Remove="@(_IkvmCompilerArgs)" /> + <_IkvmCompilerArgs Include="-nologo" /> <_IkvmCompilerArgs Include="-debug" Condition=" '$(DebugType)' != 'none' " /> <_IkvmCompilerArgs Include="-assembly:$(AssemblyName)" /> <_IkvmCompilerArgs Include="-version:$(AssemblyVersion)" /> @@ -138,7 +194,7 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_IkvmCompilerArgs Include="-opt:fields" /> <_IkvmCompilerArgs Include="-strictfinalfieldsemantics" Condition=" '$(StrictFinalFieldSemantics)' == 'true' " /> <_IkvmCompilerArgs Include="-removeassertions" Condition=" '$(RemoveAssertions)' == 'true' " /> - <_IkvmCompilerArgs Include="-target:$(OutputType.ToLowerInvariant())" /> + <_IkvmCompilerArgs Include="-target:$(_Target)" Condition=" '$(_Target)' != '' " /> <_IkvmCompilerArgs Include="-platform:$(PlatformTarget.ToLowerInvariant())" Condition=" '$(PlatformTarget)' != '' " /> <_IkvmCompilerArgs Include="-main:$(StartupObject)" Condition=" '$(StartupObject)' != '' " /> <_IkvmCompilerArgs Include="-nostdlib" Condition=" '$(NoCompilerStandardLib)' == 'true' " /> @@ -148,16 +204,25 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) <_IkvmCompilerArgs Include="-exclude:$([System.IO.Path]::GetFullPath('$(_ExcludeFilePath)'))" Condition="Exists('$(_ExcludeFilePath)')" /> <_IkvmCompilerArgs Include="@(_AssemblyAttributesClass->'-assemblyattributes:%(FullPath)')" /> <_IkvmCompilerReferencePath Remove="@(_IkvmCompilerReferencePath)" /> - <_IkvmCompilerReferencePath Include="@(ReferencePathWithRefAssemblies)" Condition=" '%(ReferencePathWithRefAssemblies.HideFromJava)' != 'true' " /> + <_IkvmCompilerReferencePath Include="@(_IkvmCompilerReferencePathWithRefAssemblies)" Condition=" '%(_IkvmCompilerReferencePathWithRefAssemblies.HideFromJava)' != 'true' " /> <_IkvmCompilerArgs Include="@(_IkvmCompilerReferencePath->'-reference:%(FullPath)')" /> <_IkvmCompilerResourceItem Remove="@(_IkvmCompilerResourceItem)" /> - <_IkvmCompilerResourceItem Include="@(JavaResource)" ResourcePath="$([System.String]::new('%(JavaResource.ResourcePath)').Replace('\', '/'))" Condition=" '%(Identity)' != '' " /> + <_IkvmCompilerResourceItem Include="@(_IkvmCompilerJavaResource)" ResourcePath="$([System.String]::new('%(_IkvmCompilerJavaResource.ResourcePath)').Replace('\', '/'))" Condition=" '%(Identity)' != '' " /> <_IkvmCompilerArgs Include="@(_IkvmCompilerResourceItem->'-resource:%(ResourcePath)=%(FullPath)')" /> <_IkvmCompilerArgs Include="-out:$(_AssemblyTempPath)$(TargetName)$(TargetExt)" /> - <_IkvmCompilerArgs Include="@(_ClassToCompile->'%(FullPath)')" /> + <_IkvmCompilerArgs Include="@(_IkvmCompilerClassToCompile->'%(FullPath)')" /> - + + + + + + _CoreCompileResponseFile; + $(CoreCompileDependsOn); + + + @@ -169,11 +234,9 @@ $([MSBuild]::Unescape('$(___IkvmReferenceExportItemPrepareCs)')) - - - \ No newline at end of file + diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets index 53d6de0b89..0b422cf3ba 100644 --- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets +++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.Tasks.targets @@ -21,8 +21,21 @@ - + + + @@ -56,7 +69,9 @@ + <_CompileJavaClassFiles Remove="@(_CompileJavaClassFiles)" /> <_CompileJavaClassFiles Include="$(ClassOutputPath)**\*.class" /> + <_CompileJavaStaleFiles Remove="@(_CompileJavaStaleFiles)" /> <_CompileJavaStaleFiles Include="@(_CompileJavaClassFiles)" Condition="$([System.DateTime]::Parse('%(ModifiedTime)').Ticks) < $(_CompileJavaStartTime)" /> @@ -70,6 +85,7 @@ + @@ -77,17 +93,17 @@ <_AssemblyAttributesClass Include="@(_ClassToCompile)" Condition=" '%(Filename)%(Extension)' == '__AssemblyAttributes.class' " /> <_AssemblyAttributesClass Include="@(_ClassToCompile)" Condition=" '%(Filename)%(Extension)' == '__AssemblyInfo.class' " /> <_ClassToCompile Remove="@(_AssemblyAttributesClass)" /> + <_IkvmCompilerReferencePath Include="@(ReferencePathWithRefAssemblies)" Condition=" '%(ReferencePathWithRefAssemblies.HideFromJava)' != 'true' " /> + <_IkvmCompilerResource Include="@(JavaResource)" ResourcePath="$([System.String]::new('%(JavaResource.ResourcePath)').Replace('\', '/'))" Condition=" '%(Identity)' != '' " /> <_IkvmCompilerDebug Condition=" '$(DebugType)' != 'none' ">true <_IkvmCompilerExclude Condition="Exists('$(_ExcludeFilePath)')">$(_ExcludeFilePath) + <_Target>$(OutputType.ToLowerInvariant()) + <_Target Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netcoreapp3.1'))">library - - <_IkvmCompilerReferencePath Include="@(ReferencePathWithRefAssemblies)" Condition=" '%(ReferencePathWithRefAssemblies.HideFromJava)' != 'true' " /> - <_IkvmCompilerResource Include="@(JavaResource)" ResourcePath="$([System.String]::new('%(JavaResource.ResourcePath)').Replace('\', '/'))" Condition=" '%(Identity)' != '' " /> - - + @@ -103,7 +119,7 @@ Version="$(AssemblyVersion)" FileVersion="$(FileVersion)" Runtime="$(IkvmRuntimeAssembly)" - Target="$(OutputType.ToLowerInvariant())" + Target="$(_Target)" Platform="$(PlatformTarget.ToLowerInvariant())" Main="$(StartupObject)" Debug="$(_IkvmCompilerDebug)" diff --git a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.targets b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.targets index fad16b3ace..f7a68c736e 100644 --- a/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.targets +++ b/src/IKVM.NET.Sdk/targets/IKVM.Java.Core.targets @@ -16,18 +16,20 @@ --> - + - <_ReferenceExport Include="@(ReferencePath)" Condition=" '%(ReferencePath.HideFromJava)' != 'true' And '%(ReferencePath.JavaClasspath)' == '' And '%(Filename)' != 'IKVM.Java' And '%(Filename)' != 'System.Runtime.Serialization' "> + <_ReferenceExport Include="@(ReferencePathWithRefAssemblies)" Condition=" '%(ReferencePathWithRefAssemblies.HideFromJava)' != 'true' And '%(ReferencePathWithRefAssemblies.JavaClasspath)' == '' And '%(Filename)' != 'IKVM.Java' And '%(Filename)' != 'System.Runtime.Serialization' "> $(Bootstrap) $(NoCompilerStandardLib) + true + @@ -65,7 +67,7 @@ - + diff --git a/src/IKVM.OpenJDK.Tests/IKVM.OpenJDK.Tests.csproj b/src/IKVM.OpenJDK.Tests/IKVM.OpenJDK.Tests.csproj index 2b851380b4..192070e6a5 100644 --- a/src/IKVM.OpenJDK.Tests/IKVM.OpenJDK.Tests.csproj +++ b/src/IKVM.OpenJDK.Tests/IKVM.OpenJDK.Tests.csproj @@ -1,7 +1,7 @@  - - + + net461;netcoreapp3.1 diff --git a/src/IKVM.OpenJDK.Tests/jdk/ExcludeList.txt b/src/IKVM.OpenJDK.Tests/jdk/ExcludeList.txt index 43e29472ff..2aef821a47 100644 --- a/src/IKVM.OpenJDK.Tests/jdk/ExcludeList.txt +++ b/src/IKVM.OpenJDK.Tests/jdk/ExcludeList.txt @@ -69,6 +69,7 @@ com/sun/jdi/InstanceFilter.java com/sun/jdi/InstancesTest.java generic-all com/sun/jdi/InterfaceMethodsTest.java generic-all com/sun/jdi/InterruptHangTest.java generic-all +com/sun/jdi/InvokeVarArgs.java generic-all com/sun/jdi/InvokeHangTest.java generic-all com/sun/jdi/InvokeTest.java generic-all com/sun/jdi/JITDebug.sh generic-all @@ -181,16 +182,6 @@ com/sun/management/ThreadMXBean/ThreadAllocatedMemoryArray.java com/sun/management/ThreadMXBean/ThreadCpuTimeArray.java generic-all com/sun/management/UnixOperatingSystemMXBean/GetMaxFileDescriptorCount.sh generic-all com/sun/management/UnixOperatingSystemMXBean/GetOpenFileDescriptorCount.sh generic-all -com/sun/net/httpserver/bugs/B6886436.java generic-all -com/sun/net/httpserver/SelCacheTest.java generic-all -com/sun/net/httpserver/Test1.java generic-all -com/sun/net/httpserver/Test12.java generic-all -com/sun/net/httpserver/Test13.java generic-all -com/sun/net/httpserver/Test14.java generic-all -com/sun/net/httpserver/Test9.java generic-all -com/sun/net/httpserver/Test9a.java generic-all -com/sun/net/httpserver/TestLogging.java generic-all -com/sun/net/httpserver/bugs/FixedLengthInputStream.java generic-all com/sun/nio/sctp/SctpChannel/CommUp.java generic-all com/sun/nio/sctp/SctpChannel/Receive.java generic-all com/sun/nio/sctp/SctpChannel/ReceiveIntoDirect.java generic-all @@ -234,6 +225,26 @@ demo/jvmti/mtrace/TraceJFrame.java demo/jvmti/versionCheck/FailsWhenJvmtiVersionDiffers.java generic-all demo/jvmti/waiters/WaitersTest.java generic-all demo/zipfs/basic.sh generic-all +java/awt/Checkbox/SetStateExcessEvent/SetStateExcessEvent.java generic-all +java/awt/Component/DimensionEncapsulation/DimensionEncapsulation.java generic-all +java/awt/Component/InsetsEncapsulation/InsetsEncapsulation.java generic-all +java/awt/Component/SetEnabledPerformance/SetEnabledPerformance.java generic-all +java/awt/datatransfer/DragImage/MultiResolutionDragImageTest.java generic-all +java/awt/Desktop/8064934/bug8064934.java generic-all +java/awt/event/MouseWheelEvent/WheelModifier/WheelModifier.java generic-all +java/awt/Focus/8073453/AWTFocusTransitionTest.java generic-all +java/awt/Focus/8073453/SwingFocusTransitionTest.java generic-all +java/awt/FontClass/DebugFonts.java generic-all +java/awt/FullScreen/MultimonFullscreenTest/MultimonDeadlockTest.java generic-all +java/awt/GraphicsEnvironment/TestDetectHeadless/TestDetectHeadless.sh generic-all +java/awt/image/BufferedImage/GetPropertyNames.java generic-all +java/awt/image/DrawImage/IncorrectClipXorModeSurface2Surface.java generic-all +java/awt/MenuBar/RemoveHelpMenu/RemoveHelpMenu.java generic-all +java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java generic-all +java/awt/Mouse/RemovedComponentMouseListener/RemovedComponentMouseListener.java generic-all +java/awt/ScrollPane/bug8077409Test.java generic-all +java/awt/Toolkit/GetImage/bug8078165.java generic-all +java/awt/TrayIcon/8072769/bug8072769.java generic-all java/awt/AlphaComposite/TestAlphaCompositeForNaN.java generic-all java/awt/Choice/ChoiceKeyEventReaction/ChoiceKeyEventReaction.html generic-all java/awt/Choice/ChoiceLocationTest/ChoiceLocationTest.java generic-all @@ -508,6 +519,7 @@ java/awt/Multiscreen/TranslucencyThrowsExceptionWhenFullScreen/TranslucencyThrow java/awt/Multiscreen/UpdateGCTest/UpdateGCTest.java generic-all java/awt/Multiscreen/WPanelPeerPerf/WPanelPeerPerf.java generic-all java/awt/Multiscreen/WindowGCChangeTest/WindowGCChangeTest.html generic-all +java/awt/Multiscreen/MultiScreenInsetsTest/MultiScreenInsetsTest.java linux-all java/awt/Paint/ButtonRepaint.java generic-all java/awt/Paint/CheckboxRepaint.java generic-all java/awt/Paint/ExposeOnEDT.java generic-all @@ -615,6 +627,7 @@ java/awt/datatransfer/HTMLDataFlavors/PutAllHtmlFlavorsOnClipboard.java java/awt/datatransfer/HTMLDataFlavors/PutOnlyAllHtmlFlavorOnClipboard.java generic-all java/awt/datatransfer/HTMLDataFlavors/PutSelectionAndFragmentHtmlFlavorsOnClipboard.java generic-all java/awt/datatransfer/MappingGenerationTest/MappingGenerationTest.java generic-all +java/awt/datatransfer/ClipboardInterVMTest/ClipboardInterVMTest.java generic-all java/awt/dnd/AcceptDropMultipleTimes/AcceptDropMultipleTimes.java generic-all java/awt/dnd/BadSerializaionTest/BadSerializationTest.java generic-all java/awt/dnd/Button2DragTest/Button2DragTest.html generic-all @@ -1119,10 +1132,6 @@ java/lang/ProcessBuilder/InheritIO/InheritIO.sh java/lang/ProcessBuilder/InheritIOEHandle.java generic-all java/lang/ProcessBuilder/SecurityManagerClinit.java generic-all java/lang/ProcessBuilder/SiblingIOEHandle.java generic-all -java/lang/Runtime/exec/ArgWithSpaceAndFinalBackslash.java generic-all -java/lang/Runtime/exec/Duped.java generic-all -java/lang/Runtime/exec/ExecWithLotsOfArgs.java generic-all -java/lang/Runtime/exec/ExitValue.java generic-all java/lang/Runtime/exec/SetCwd.java generic-all java/lang/Runtime/shutdown/ShutdownHooks.sh generic-all java/lang/SecurityManager/CheckPackageAccess.java generic-all @@ -1161,6 +1170,7 @@ java/lang/annotation/loaderLeak/Main.java java/lang/annotation/typeAnnotations/BadCPIndex.java generic-all java/lang/annotation/typeAnnotations/GetAnnotatedReceiverType.java generic-all java/lang/annotation/typeAnnotations/TestExecutableGetAnnotatedType.java generic-all +java/lang/instrument/ManyMethodsBenchmarkAgent.java generic-all java/lang/instrument/AddTransformerTest.java generic-all java/lang/instrument/AppendToBootstrapClassPathTest.java generic-all java/lang/instrument/AppendToClassPathTest.java generic-all @@ -1218,6 +1228,7 @@ java/lang/instrument/VerifyLocalVariableTableOnRetransformTest.sh java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh generic-all java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh generic-all java/lang/instrument/appendToClassLoaderSearch/run_tests.sh generic-all +java/lang/instrument/ManyMethodsBenchmarkAgent.sh generic-all java/lang/invoke/6987555/Test6987555.java generic-all java/lang/invoke/6991596/Test6991596.java generic-all java/lang/invoke/6998541/Test6998541.java generic-all @@ -1318,6 +1329,7 @@ java/lang/management/ThreadMXBean/ThreadLists.java java/lang/management/ThreadMXBean/ThreadMXBeanStateTest.java generic-all java/lang/management/ThreadMXBean/ThreadStackTrace.java generic-all java/lang/management/ThreadMXBean/ThreadUserTime.java generic-all +java/lang/management/ThreadMXBean/ThreadInfoArray.java generic-all java/lang/ref/EarlyTimeout.java generic-all java/lang/ref/FinalizeOverride.java generic-all java/lang/ref/NullQueue.java generic-all @@ -1336,128 +1348,76 @@ java/lang/reflect/Proxy/ClassRestrictions.java java/lang/reflect/Proxy/returnTypes/Test.java generic-all java/math/BigInteger/ModPowPowersof2.java generic-all java/net/Authenticator/B4933582.sh generic-all -java/net/Authenticator/B4962064.java generic-all java/net/Authenticator/B6870935.java generic-all java/net/Authenticator/Deadlock.java generic-all -java/net/DatagramSocket/B6411513.java generic-all +java/net/DatagramSocket/B6411513.java linux-all java/net/DatagramSocket/InheritHandle.java generic-all java/net/DatagramSocket/SetDatagramSocketImplFactory/ADatagramSocket.java generic-all java/net/InetAddress/ptr/lookup.sh generic-all java/net/NetworkInterface/IPv4Only.java generic-all java/net/NetworkInterface/Test.java generic-all -java/net/ProxySelector/B6737819.java generic-all java/net/ServerSocket/AcceptCauseFileDescriptorLeak.sh generic-all java/net/ServerSocket/ThreadStop.java generic-all -java/net/Socket/GetLocalAddress.java generic-all -java/net/Socket/HttpProxy.java generic-all java/net/Socket/OldSocketImpl.sh generic-all -java/net/Socket/SocksConnectTimeout.java generic-all java/net/Socket/asyncClose/Race.java generic-all -java/net/Socks/BadProxySelector.java generic-all -java/net/Socks/SocksV4Test.java generic-all -java/net/URL/PerConnectionProxy.java generic-all +java/net/Socks/SocksV4Test.java linux-all java/net/URLClassLoader/B5077773.sh generic-all -java/net/URLClassLoader/ClassPathTest.java generic-all java/net/URLClassLoader/closetest/CloseTest.java generic-all java/net/URLClassLoader/closetest/GetResourceAsStream.java generic-all java/net/URLClassLoader/getresourceasstream/test.sh generic-all java/net/URLClassLoader/sealing/CheckSealed.java generic-all java/net/URLConnection/UNCTest.sh generic-all java/net/URLPermission/OpenURL.java generic-all -java/net/URLPermission/URLTest.java generic-all java/net/URLPermission/nstest/lookup.sh generic-all java/nio/Buffer/Chew.java generic-all java/nio/Buffer/LimitDirectMemory.sh generic-all -java/nio/MappedByteBuffer/Basic.java generic-all -java/nio/MappedByteBuffer/Force.java generic-all -java/nio/MappedByteBuffer/Truncate.java generic-all -java/nio/MappedByteBuffer/ZeroMap.java generic-all +java/nio/MappedByteBuffer/Truncate.java linux-all java/nio/channels/AsyncCloseAndInterrupt.java generic-all -java/nio/channels/AsynchronousChannelGroup/BadProperties.java generic-all java/nio/channels/AsynchronousChannelGroup/Basic.java generic-all java/nio/channels/AsynchronousChannelGroup/GroupOfOne.java generic-all java/nio/channels/AsynchronousChannelGroup/Identity.java generic-all java/nio/channels/AsynchronousChannelGroup/Restart.java generic-all java/nio/channels/AsynchronousChannelGroup/run_any_task.sh generic-all java/nio/channels/AsynchronousFileChannel/Lock.java generic-all -java/nio/channels/AsynchronousFileChannel/LotsOfWrites.java generic-all java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java generic-all java/nio/channels/DatagramChannel/ChangingAddress.java generic-all -java/nio/channels/DatagramChannel/Connect.java generic-all java/nio/channels/DatagramChannel/Disconnect.java generic-all java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java generic-all -java/nio/channels/DatagramChannel/SelectWhenRefused.java generic-all java/nio/channels/DatagramChannel/SocketOptionTests.java generic-all -java/nio/channels/DatagramChannel/UseDGWithIPv6.java generic-all -java/nio/channels/FileChannel/Args.java generic-all +java/nio/channels/DatagramChannel/SelectWhenRefused.java generic-all java/nio/channels/FileChannel/AtomicAppend.java generic-all -java/nio/channels/FileChannel/ExpandingMap.java generic-all -java/nio/channels/FileChannel/InterruptMapDeadlock.java generic-all java/nio/channels/FileChannel/Lock.java generic-all -java/nio/channels/FileChannel/MapAssertions.java generic-all -java/nio/channels/FileChannel/MapOverEnd.java generic-all -java/nio/channels/FileChannel/MapReadOnly.java generic-all -java/nio/channels/FileChannel/MapTest.java generic-all -java/nio/channels/FileChannel/Mode.java generic-all -java/nio/channels/FileChannel/ScatteringRead.java generic-all -java/nio/channels/FileChannel/Size.java generic-all -java/nio/channels/FileChannel/Transfer.java generic-all +java/nio/channels/FileChannel/MapOverEnd.java linux-all +java/nio/channels/FileChannel/Size.java linux-all java/nio/channels/FileChannel/Truncate.java generic-all java/nio/channels/FileChannel/Write.java generic-all -java/nio/channels/Pipe/EmptyRead.java generic-all -java/nio/channels/Pipe/NonBlocking.java generic-all -java/nio/channels/Pipe/PipeChannel.java generic-all -java/nio/channels/Pipe/PipeInterrupt.java generic-all -java/nio/channels/Pipe/ScatteringRead.java generic-all -java/nio/channels/Pipe/SelectPipe.java generic-all -java/nio/channels/SelectionKey/AtomicAttachTest.java generic-all java/nio/channels/Selector/CloseThenRegister.java generic-all -java/nio/channels/Selector/LotsOfChannels.java generic-all java/nio/channels/Selector/OutOfBand.java generic-all java/nio/channels/Selector/RegAfterPreClose.java generic-all java/nio/channels/Selector/TemporarySelector.java generic-all -java/nio/channels/Selector/WakeupNow.java generic-all java/nio/channels/Selector/lots_of_updates.sh generic-all java/nio/channels/SocketChannel/IsConnectable.java generic-all -java/nio/channels/SocketChannel/Open.java generic-all java/nio/channels/SocketChannel/Open.sh generic-all java/nio/channels/SocketChannel/OpenLeak.java generic-all -java/nio/channels/SocketChannel/ShortWrite.java generic-all java/nio/channels/SocketChannel/SocketInheritance.java generic-all java/nio/channels/SocketChannel/SocketOptionTests.java generic-all -java/nio/channels/SocketChannel/VectorParams.java generic-all java/nio/channels/spi/AsynchronousChannelProvider/custom_provider.sh generic-all java/nio/channels/spi/SelectorProvider/inheritedChannel/run_tests.sh generic-all java/nio/charset/Charset/default.sh generic-all -java/nio/charset/coders/Check.java generic-all java/nio/charset/coders/CheckSJISMappingProp.sh generic-all java/nio/charset/spi/basic.sh generic-all java/nio/file/DirectoryStream/Basic.java generic-all -java/nio/file/Files/BytesAndLines.java generic-all java/nio/file/Files/CheckPermissions.java generic-all -java/nio/file/Files/CopyAndMove.java generic-all -java/nio/file/Files/CustomOptions.java generic-all +java/nio/file/Files/CopyAndMove.java linux-all java/nio/file/Files/InterruptCopy.java generic-all -java/nio/file/Files/Misc.java generic-all +java/nio/file/Files/Misc.java linux-all java/nio/file/Files/NameLimits.java generic-all -java/nio/file/Files/SBC.java generic-all -java/nio/file/Files/StreamTest.java generic-all +java/nio/file/Files/SBC.java linux-all java/nio/file/Files/delete_on_close.sh generic-all -java/nio/file/Files/probeContentType/ForceLoad.java generic-all java/nio/file/Files/walkFileTree/find.sh generic-all -java/nio/file/Path/MacPathTest.java generic-all -java/nio/file/Path/Misc.java generic-all -java/nio/file/Path/PathOps.java generic-all -java/nio/file/WatchService/Basic.java generic-all -java/nio/file/WatchService/FileTreeModifier.java generic-all -java/nio/file/WatchService/LotsOfEvents.java generic-all -java/nio/file/WatchService/MayFlies.java generic-all -java/nio/file/WatchService/SensitivityModifier.java generic-all -java/nio/file/WatchService/WithSecurityManager.java generic-all -java/nio/file/attribute/AclEntry/EmptySet.java generic-all -java/nio/file/attribute/AclFileAttributeView/Basic.java generic-all -java/nio/file/etc/Exceptions.java generic-all -java/nio/file/spi/SetDefaultProvider.java generic-all +java/nio/file/Path/Misc.java linux-all +java/nio/file/Path/PathOps.java windows-all +java/nio/file/WatchService/Basic.java linux-all java/rmi/Naming/DefaultRegistryPort.java generic-all java/rmi/Naming/LookupIPv6.java generic-all java/rmi/Naming/LookupNameWithColon.java generic-all @@ -1616,174 +1576,8 @@ java/text/Bidi/BidiEmbeddingTest.java java/text/Format/DateFormat/Bug6645292.java generic-all java/text/Format/DateFormat/Bug7130335.java generic-all java/text/Format/DateFormat/WeekDateTest.java generic-all -java/time/tck/java/time/AbstractDateTimeTest.java generic-all -java/time/tck/java/time/AbstractTCKTest.java generic-all -java/time/tck/java/time/MockSimplePeriod.java generic-all -java/time/tck/java/time/TCKClock.java generic-all -java/time/tck/java/time/TCKClock_Fixed.java generic-all -java/time/tck/java/time/TCKClock_Offset.java generic-all -java/time/tck/java/time/TCKClock_System.java generic-all -java/time/tck/java/time/TCKClock_Tick.java generic-all -java/time/tck/java/time/TCKDayOfWeek.java generic-all -java/time/tck/java/time/TCKDuration.java generic-all -java/time/tck/java/time/TCKInstant.java generic-all -java/time/tck/java/time/TCKLocalDate.java generic-all -java/time/tck/java/time/TCKLocalDateTime.java generic-all -java/time/tck/java/time/TCKLocalTime.java generic-all -java/time/tck/java/time/TCKMonth.java generic-all -java/time/tck/java/time/TCKMonthDay.java generic-all -java/time/tck/java/time/TCKOffsetDateTime.java generic-all -java/time/tck/java/time/TCKOffsetTime.java generic-all -java/time/tck/java/time/TCKPeriod.java generic-all -java/time/tck/java/time/TCKYear.java generic-all -java/time/tck/java/time/TCKYearMonth.java generic-all -java/time/tck/java/time/TCKZoneId.java generic-all -java/time/tck/java/time/TCKZoneOffset.java generic-all -java/time/tck/java/time/TCKZonedDateTime.java generic-all -java/time/tck/java/time/TestIsoChronology.java generic-all -java/time/tck/java/time/chrono/CopticChronology.java generic-all -java/time/tck/java/time/chrono/CopticDate.java generic-all -java/time/tck/java/time/chrono/CopticEra.java generic-all -java/time/tck/java/time/chrono/TCKChronoLocalDate.java generic-all -java/time/tck/java/time/chrono/TCKChronoLocalDateTime.java generic-all -java/time/tck/java/time/chrono/TCKChronoPeriod.java generic-all -java/time/tck/java/time/chrono/TCKChronoZonedDateTime.java generic-all -java/time/tck/java/time/chrono/TCKChronology.java generic-all -java/time/tck/java/time/chrono/TCKHijrahChronology.java generic-all -java/time/tck/java/time/chrono/TCKHijrahEra.java generic-all -java/time/tck/java/time/chrono/TCKIsoChronology.java generic-all -java/time/tck/java/time/chrono/TCKIsoEra.java generic-all -java/time/tck/java/time/chrono/TCKJapaneseChronology.java generic-all -java/time/tck/java/time/chrono/TCKJapaneseEra.java generic-all -java/time/tck/java/time/chrono/TCKMinguoChronology.java generic-all -java/time/tck/java/time/chrono/TCKMinguoEra.java generic-all -java/time/tck/java/time/chrono/TCKTestServiceLoader.java generic-all -java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java generic-all -java/time/tck/java/time/chrono/TCKThaiBuddhistEra.java generic-all -java/time/tck/java/time/chrono/serial/TCKChronoLocalDateSerialization.java generic-all -java/time/tck/java/time/chrono/serial/TCKChronoLocalDateTimeSerialization.java generic-all -java/time/tck/java/time/chrono/serial/TCKChronoZonedDateTimeSerialization.java generic-all -java/time/tck/java/time/chrono/serial/TCKChronologySerialization.java generic-all -java/time/tck/java/time/chrono/serial/TCKCopticSerialization.java generic-all -java/time/tck/java/time/chrono/serial/TCKEraSerialization.java generic-all -java/time/tck/java/time/format/TCKChronoPrinterParser.java generic-all -java/time/tck/java/time/format/TCKDateTimeFormatter.java generic-all -java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java generic-all -java/time/tck/java/time/format/TCKDateTimeFormatters.java generic-all -java/time/tck/java/time/format/TCKDateTimeParseResolver.java generic-all -java/time/tck/java/time/format/TCKDateTimeTextPrinting.java generic-all -java/time/tck/java/time/format/TCKDecimalStyle.java generic-all -java/time/tck/java/time/format/TCKFormatStyle.java generic-all -java/time/tck/java/time/format/TCKInstantPrinterParser.java generic-all -java/time/tck/java/time/format/TCKLocalizedFieldParser.java generic-all -java/time/tck/java/time/format/TCKLocalizedFieldPrinter.java generic-all -java/time/tck/java/time/format/TCKLocalizedPrinterParser.java generic-all -java/time/tck/java/time/format/TCKOffsetPrinterParser.java generic-all -java/time/tck/java/time/format/TCKPadPrinterParser.java generic-all -java/time/tck/java/time/format/TCKResolverStyle.java generic-all -java/time/tck/java/time/format/TCKSignStyle.java generic-all -java/time/tck/java/time/format/TCKTextStyle.java generic-all -java/time/tck/java/time/format/TCKZoneIdPrinterParser.java generic-all -java/time/tck/java/time/serial/TCKClockSerialization.java generic-all -java/time/tck/java/time/serial/TCKDurationSerialization.java generic-all -java/time/tck/java/time/serial/TCKInstantSerialization.java generic-all -java/time/tck/java/time/serial/TCKLocalDateSerialization.java generic-all -java/time/tck/java/time/serial/TCKLocalDateTimeSerialization.java generic-all -java/time/tck/java/time/serial/TCKLocalTimeSerialization.java generic-all -java/time/tck/java/time/serial/TCKMonthDaySerialization.java generic-all -java/time/tck/java/time/serial/TCKOffsetDateTimeSerialization.java generic-all -java/time/tck/java/time/serial/TCKOffsetTimeSerialization.java generic-all -java/time/tck/java/time/serial/TCKPeriodSerialization.java generic-all -java/time/tck/java/time/serial/TCKYearMonthSerialization.java generic-all -java/time/tck/java/time/serial/TCKYearSerialization.java generic-all -java/time/tck/java/time/serial/TCKZoneIdSerialization.java generic-all -java/time/tck/java/time/serial/TCKZoneOffsetSerialization.java generic-all -java/time/tck/java/time/serial/TCKZonedDateTimeSerialization.java generic-all -java/time/tck/java/time/temporal/TCKChronoField.java generic-all -java/time/tck/java/time/temporal/TCKChronoUnit.java generic-all -java/time/tck/java/time/temporal/TCKIsoFields.java generic-all -java/time/tck/java/time/temporal/TCKJulianFields.java generic-all -java/time/tck/java/time/temporal/TCKTemporalAdjusters.java generic-all -java/time/tck/java/time/temporal/TCKWeekFields.java generic-all -java/time/tck/java/time/temporal/serial/TCKChronoFieldSerialization.java generic-all -java/time/tck/java/time/temporal/serial/TCKChronoUnitSerialization.java generic-all -java/time/tck/java/time/temporal/serial/TCKJulianFieldsSerialization.java generic-all -java/time/tck/java/time/temporal/serial/TCKValueRangeSerialization.java generic-all -java/time/tck/java/time/temporal/serial/TCKWeekFieldsSerialization.java generic-all -java/time/tck/java/time/zone/TCKFixedZoneRules.java generic-all -java/time/tck/java/time/zone/TCKZoneOffsetTransition.java generic-all -java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java generic-all -java/time/tck/java/time/zone/TCKZoneRules.java generic-all -java/time/tck/java/time/zone/TCKZoneRulesProvider.java generic-all -java/time/tck/java/time/zone/serial/TCKFixedZoneRulesSerialization.java generic-all -java/time/tck/java/time/zone/serial/TCKZoneOffsetTransitionRuleSerialization.java generic-all -java/time/tck/java/time/zone/serial/TCKZoneOffsetTransitionSerialization.java generic-all -java/time/tck/java/time/zone/serial/TCKZoneRulesSerialization.java generic-all -java/time/test/java/time/AbstractTest.java generic-all -java/time/test/java/time/MockSimplePeriod.java generic-all -java/time/test/java/time/TestClock_Fixed.java generic-all -java/time/test/java/time/TestClock_Offset.java generic-all -java/time/test/java/time/TestClock_System.java generic-all -java/time/test/java/time/TestClock_Tick.java generic-all -java/time/test/java/time/TestDuration.java generic-all -java/time/test/java/time/TestInstant.java generic-all -java/time/test/java/time/TestLocalDate.java generic-all -java/time/test/java/time/TestLocalDateTime.java generic-all -java/time/test/java/time/TestLocalTime.java generic-all -java/time/test/java/time/TestMonthDay.java generic-all -java/time/test/java/time/TestOffsetDateTime.java generic-all -java/time/test/java/time/TestOffsetDateTime_instants.java generic-all -java/time/test/java/time/TestOffsetTime.java generic-all -java/time/test/java/time/TestPeriod.java generic-all -java/time/test/java/time/TestYear.java generic-all -java/time/test/java/time/TestYearMonth.java generic-all -java/time/test/java/time/TestZoneId.java generic-all -java/time/test/java/time/TestZoneOffset.java generic-all -java/time/test/java/time/TestZonedDateTime.java generic-all -java/time/test/java/time/chrono/TestChronoLocalDate.java generic-all -java/time/test/java/time/chrono/TestChronologyPerf.java generic-all -java/time/test/java/time/chrono/TestExampleCode.java generic-all -java/time/test/java/time/chrono/TestIsoChronoImpl.java generic-all -java/time/test/java/time/chrono/TestJapaneseChronoImpl.java generic-all -java/time/test/java/time/chrono/TestJapaneseChronology.java generic-all -java/time/test/java/time/chrono/TestServiceLoader.java generic-all -java/time/test/java/time/chrono/TestThaiBuddhistChronoImpl.java generic-all -java/time/test/java/time/chrono/TestUmmAlQuraChronology.java generic-all -java/time/test/java/time/format/AbstractTestPrinterParser.java generic-all -java/time/test/java/time/format/MockIOExceptionAppendable.java generic-all -java/time/test/java/time/format/TestCharLiteralParser.java generic-all -java/time/test/java/time/format/TestCharLiteralPrinter.java generic-all -java/time/test/java/time/format/TestDateTimeFormatter.java generic-all -java/time/test/java/time/format/TestDateTimeFormatterBuilder.java generic-all -java/time/test/java/time/format/TestDateTimeParsing.java generic-all -java/time/test/java/time/format/TestDateTimeTextProvider.java generic-all -java/time/test/java/time/format/TestDecimalStyle.java generic-all -java/time/test/java/time/format/TestFractionPrinterParser.java generic-all -java/time/test/java/time/format/TestNonIsoFormatter.java generic-all -java/time/test/java/time/format/TestNumberParser.java generic-all -java/time/test/java/time/format/TestNumberPrinter.java generic-all -java/time/test/java/time/format/TestPadPrinterDecorator.java generic-all -java/time/test/java/time/format/TestReducedParser.java generic-all -java/time/test/java/time/format/TestReducedPrinter.java generic-all -java/time/test/java/time/format/TestSettingsParser.java generic-all -java/time/test/java/time/format/TestStringLiteralParser.java generic-all -java/time/test/java/time/format/TestStringLiteralPrinter.java generic-all -java/time/test/java/time/format/TestTextParser.java generic-all -java/time/test/java/time/format/TestTextPrinter.java generic-all -java/time/test/java/time/format/TestZoneOffsetParser.java generic-all -java/time/test/java/time/format/TestZoneOffsetPrinter.java generic-all -java/time/test/java/time/format/TestZoneTextPrinterParser.java generic-all -java/time/test/java/time/format/ZoneName.java generic-all -java/time/test/java/time/temporal/MockFieldNoValue.java generic-all -java/time/test/java/time/temporal/MockFieldValue.java generic-all -java/time/test/java/time/temporal/TestChronoField.java generic-all -java/time/test/java/time/temporal/TestChronoUnit.java generic-all -java/time/test/java/time/temporal/TestDateTimeBuilderCombinations.java generic-all -java/time/test/java/time/temporal/TestDateTimeValueRange.java generic-all -java/time/test/java/time/temporal/TestIsoWeekFields.java generic-all -java/time/test/java/time/temporal/TestJulianFields.java generic-all -java/time/test/java/time/zone/TestFixedZoneRules.java generic-all -java/time/test/java/util/TestFormatter.java generic-all +java/time/test/java/util/TestFormatter.java linux-all +java/time/test/java/time/TestZoneId.java linux-all java/util/AbstractMap/AbstractMapClone.java generic-all java/util/AbstractMap/SimpleEntries.java generic-all java/util/AbstractMap/ToString.java generic-all @@ -2052,6 +1846,7 @@ java/util/zip/ZipFile/MultiThreadedReadTest.java java/util/zip/ZipFile/ReadLongZipFileName.java generic-all java/util/zip/ZipFile/StreamZipEntriesTest.java generic-all javax/accessibility/6986385/bug6986385.java generic-all +javax/accessibility/8069268/bug8069268.java generic-all javax/crypto/JceSecurity/SunJCE_BC_LoadOrdering.java generic-all javax/crypto/SecretKeyFactory/FailOverTest.sh generic-all javax/crypto/sanity/CheckManifestForRelease.java generic-all @@ -2081,8 +1876,10 @@ javax/imageio/plugins/png/PngOutputTypeTest.java javax/imageio/plugins/png/ShortHistogramTest.java generic-all javax/imageio/plugins/shared/BitDepth.java generic-all javax/imageio/plugins/wbmp/CanDecodeTest.java generic-all +javax/imageio/plugins/shared/WriteAfterAbort.java generic-all javax/imageio/stream/StreamCloserLeak/run_test.java generic-all javax/imageio/stream/StreamCloserLeak/run_test.sh generic-all +javax/management/ThreadMXBeanTest.java generic-all javax/management/ImplementationVersion/ImplVersionTest.java generic-all javax/management/Introspector/AnnotationSecurityTest.java generic-all javax/management/Introspector/ClassLeakTest.java generic-all @@ -2178,6 +1975,7 @@ javax/print/attribute/PSCopiesFlavorTest.java javax/print/attribute/SidesPageRangesTest.java generic-all javax/print/attribute/SupportedPrintableAreas.java generic-all javax/print/attribute/autosense/PrintAutoSenseData.java generic-all +javax/print/PrintSEUmlauts/PrintSEUmlauts.java generic-all javax/rmi/ssl/SSLSocketParametersTest.sh generic-all javax/script/ProviderTest.sh generic-all javax/security/auth/Subject/doAs/Test.sh generic-all @@ -2189,6 +1987,7 @@ javax/security/auth/login/LoginContext/ConfigConstructor.java javax/security/auth/login/LoginContext/ConfigConstructorNoPerm.java generic-all javax/security/auth/login/LoginContext/DefaultHandler.java generic-all javax/security/auth/login/LoginContext/ResetConfigModule.java generic-all +javax/sound/midi/Devices/InitializationHang.java generic-all javax/sound/midi/File/SMPTESequence.java generic-all javax/sound/midi/Gervill/AudioFloatConverter/GetFormat.java generic-all javax/sound/midi/Gervill/AudioFloatConverter/ToFloatArray.java generic-all @@ -2452,6 +2251,7 @@ javax/sound/sampled/DirectAudio/bug6400879.java javax/sound/sampled/FileWriter/AlawEncoderSync.java generic-all javax/sound/sampled/FileWriter/WriterCloseInput.java generic-all javax/swing/AbstractButton/6711682/bug6711682.java generic-all +javax/swing/AbstractButton/AnimatedIcon/AnimatedIcon.java generic-all javax/swing/AncestorNotifier/7193219/bug7193219.java generic-all javax/swing/DataTransfer/6456844/bug6456844.java generic-all javax/swing/DataTransfer/8059739/bug8059739.java generic-all @@ -2495,6 +2295,8 @@ javax/swing/JComboBox/7195179/Test7195179.java javax/swing/JComboBox/8015300/Test8015300.java generic-all javax/swing/JComboBox/8019180/Test8019180.java generic-all javax/swing/JComboBox/8032878/bug8032878.java generic-all +javax/swing/JComboBox/8033069/bug8033069NoScrollBar.java generic-all +javax/swing/JComboBox/8033069/bug8033069ScrollBar.java generic-all javax/swing/JComboBox/8057893/bug8057893.java generic-all javax/swing/JComboBox/ConsumedKeyTest/ConsumedKeyTest.java generic-all javax/swing/JComboBox/ShowPopupAfterHidePopupTest/ShowPopupAfterHidePopupTest.java generic-all @@ -2535,6 +2337,7 @@ javax/swing/JFileChooser/8013442/Test8013442.java javax/swing/JFileChooser/8021253/bug8021253.java generic-all javax/swing/JFileChooser/8046391/bug8046391.java generic-all javax/swing/JFileChooser/8062561/bug8062561.java generic-all +javax/swing/JFileChooser/8080628/bug8080628.java generic-all javax/swing/JFileChooser/FileFilterDescription/FileFilterDescription.java generic-all javax/swing/JFormattedTextField/Test6462562.java generic-all javax/swing/JFrame/4962534/bug4962534.html generic-all @@ -2565,7 +2368,10 @@ javax/swing/JList/6462008/bug6462008.java javax/swing/JList/6823603/bug6823603.java generic-all javax/swing/JMenu/4515762/bug4515762.java generic-all javax/swing/JMenu/4692443/bug4692443.java generic-all +javax/swing/JMenu/8071705/bug8071705.java generic-all +javax/swing/JMenu/8072900/WrongSelectionOnMouseOver.java generic-all javax/swing/JMenuBar/4750590/bug4750590.java generic-all +javax/swing/JMenuBar/MisplacedBorder/MisplacedBorder.java generic-all javax/swing/JMenuItem/4171437/bug4171437.java generic-all javax/swing/JMenuItem/4654927/bug4654927.java generic-all javax/swing/JMenuItem/6209975/bug6209975.java generic-all @@ -2594,6 +2400,7 @@ javax/swing/JPopupMenu/7160604/bug7160604.java javax/swing/JPopupMenu/Separator/6547087/bug6547087.java generic-all javax/swing/JProgressBar/7141573/bug7141573.java generic-all javax/swing/JRadioButton/8033699/bug8033699.java generic-all +javax/swing/JRadioButton/8075609/bug8075609.java generic-all javax/swing/JRootPane/4670486/bug4670486.java generic-all javax/swing/JScrollBar/4708809/bug4708809.java generic-all javax/swing/JScrollBar/4865918/bug4865918.java generic-all @@ -2605,6 +2412,7 @@ javax/swing/JScrollBar/bug4202954/bug4202954.java javax/swing/JScrollPane/6559589/bug6559589.java generic-all javax/swing/JScrollPane/6612531/bug6612531.java generic-all javax/swing/JScrollPane/Test6526631.java generic-all +javax/swing/JScrollPane/8033000/bug8033000.java generic-all javax/swing/JSlider/4252173/bug4252173.java generic-all javax/swing/JSlider/4987336/bug4987336.java generic-all javax/swing/JSlider/6278700/bug6278700.java generic-all @@ -2661,6 +2469,7 @@ javax/swing/JTextArea/6925473/bug6925473.java javax/swing/JTextArea/6940863/bug6940863.java generic-all javax/swing/JTextArea/7049024/bug7049024.java generic-all javax/swing/JTextArea/Test6593649.java generic-all +javax/swing/JTextArea/TextViewOOM/TextViewOOM.java generic-all javax/swing/JTextField/8036819/bug8036819.java generic-all javax/swing/JToolBar/4247996/bug4247996.java generic-all javax/swing/JToolTip/4846413/bug4846413.java generic-all @@ -2689,6 +2498,7 @@ javax/swing/PopupFactory/8048506/bug8048506.java javax/swing/RepaintManager/6608456/bug6608456.java generic-all javax/swing/RepaintManager/7013453/bug7013453.java generic-all javax/swing/RepaintManager/IconifyTest/IconifyTest.java generic-all +javax/swing/RepaintManager/DisplayListenerLeak/DisplayListenerLeak.java generic-all javax/swing/Security/6657138/ComponentTest.java generic-all javax/swing/Security/6657138/bug6657138.java generic-all javax/swing/Security/6938813/bug6938813.java generic-all @@ -2700,6 +2510,7 @@ javax/swing/SwingUtilities/7088744/bug7088744.java javax/swing/SwingUtilities/7146377/bug7146377.java generic-all javax/swing/SwingUtilities/7170657/bug7170657.java generic-all javax/swing/SwingUtilities/8032219/DrawRect.java generic-all +javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java generic-all javax/swing/SwingWorker/6432565/bug6432565.java generic-all javax/swing/SwingWorker/6493680/bug6493680.java generic-all javax/swing/SwingWorker/6880336/NestedWorkers.java generic-all @@ -2710,6 +2521,7 @@ javax/swing/UIDefaults/6622002/bug6622002.java javax/swing/UIDefaults/6795356/SwingLazyValueTest.java generic-all javax/swing/UIDefaults/6795356/TableTest.java generic-all javax/swing/UIDefaults/6795356/bug6795356.java generic-all +javax/swing/UIDefaults/7180976/Pending.java generic-all javax/swing/UIManager/Test6657026.java generic-all javax/swing/UITest/UITest.java generic-all javax/swing/border/Test4120351.java generic-all @@ -2730,6 +2542,7 @@ javax/swing/border/Test7034614.java javax/swing/border/Test7149090.java generic-all javax/swing/dnd/7171812/JListWithScroll.java generic-all javax/swing/dnd/7171812/bug7171812.java generic-all +javax/swing/plaf/aqua/CustomComboBoxFocusTest.java generic-all javax/swing/plaf/basic/BasicComboBoxEditor/Test8015336.java generic-all javax/swing/plaf/basic/BasicHTML/4251579/bug4251579.java generic-all javax/swing/plaf/basic/BasicMenuUI/4983388/bug4983388.java generic-all @@ -2738,6 +2551,7 @@ javax/swing/plaf/basic/BasicSplitPaneUI/Test6657026.java javax/swing/plaf/basic/BasicTabbedPaneUI/Test6943780.java generic-all javax/swing/plaf/basic/BasicTreeUI/8023474/bug8023474.java generic-all javax/swing/plaf/basic/Test6984643.java generic-all +javax/swing/plaf/gtk/crash/RenderBadPictureCrash.java generic-all javax/swing/plaf/metal/MetalBorders/Test6657026.java generic-all javax/swing/plaf/metal/MetalBumps/Test6657026.java generic-all javax/swing/plaf/metal/MetalInternalFrameUI/Test6657026.java generic-all @@ -2750,6 +2564,7 @@ javax/swing/plaf/nimbus/Test6741426.java javax/swing/plaf/nimbus/Test6849805.java generic-all javax/swing/plaf/nimbus/Test6919629.java generic-all javax/swing/plaf/nimbus/Test7048204.java generic-all +javax/swing/plaf/nimbus/8041642/bug8041642.java generic-all javax/swing/plaf/synth/6771547/SynthTest.java generic-all javax/swing/plaf/synth/7032791/bug7032791.java generic-all javax/swing/plaf/synth/7143614/bug7143614.java generic-all @@ -2996,20 +2811,6 @@ sun/net/www/protocol/jar/B5105410.sh sun/net/www/protocol/jar/B6449504.java generic-all sun/net/www/protocol/jar/GetContentType.java generic-all sun/net/www/protocol/jar/jarbug/run.sh generic-all -sun/nio/ch/Basic.java generic-all -sun/nio/ch/TempBuffer.java generic-all -sun/nio/cs/FindASCIICodingBugs.java generic-all -sun/nio/cs/FindASCIIRangeCodingBugs.java generic-all -sun/nio/cs/FindASCIIReplBugs.java generic-all -sun/nio/cs/FindCanEncodeBugs.java generic-all -sun/nio/cs/FindDecoderBugs.java generic-all -sun/nio/cs/FindEncoderBugs.java generic-all -sun/nio/cs/ISO8859x.java generic-all -sun/nio/cs/OLD/TestIBMDB.java generic-all -sun/nio/cs/Test4200310.sh generic-all -sun/nio/cs/TestEUC_TW.java generic-all -sun/nio/cs/TestX11CNS.java generic-all -sun/nio/cs/UkrainianIsNotRussian.java generic-all sun/pisces/DashStrokeTest.java generic-all sun/pisces/JoinMiterTest.java generic-all sun/reflect/AnonymousNewInstance/ManyNewInstanceAnonTest.java generic-all @@ -3119,61 +2920,8 @@ sun/security/mscapi/RSAEncryptDecrypt.sh sun/security/mscapi/ShortRSAKey1024.sh generic-all sun/security/mscapi/SignUsingNONEwithRSA.sh generic-all sun/security/mscapi/SignUsingSHA2withRSA.sh generic-all +sun/security/mscapi/SmallPrimeExponentP.java generic-all sun/security/pkcs/pkcs9/UnstructuredName.java generic-all -sun/security/pkcs11/Cipher/ReinitCipher.java generic-all -sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java generic-all -sun/security/pkcs11/Cipher/TestRSACipher.java generic-all -sun/security/pkcs11/Cipher/TestRSACipherWrap.java generic-all -sun/security/pkcs11/Cipher/TestRawRSACipher.java generic-all -sun/security/pkcs11/Cipher/TestSymmCiphers.java generic-all -sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java generic-all -sun/security/pkcs11/KeyAgreement/TestDH.java generic-all -sun/security/pkcs11/KeyAgreement/TestInterop.java generic-all -sun/security/pkcs11/KeyAgreement/TestShort.java generic-all -sun/security/pkcs11/KeyGenerator/DESParity.java generic-all -sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java generic-all -sun/security/pkcs11/KeyPairGenerator/TestDH2048.java generic-all -sun/security/pkcs11/KeyStore/SecretKeysBasic.sh generic-all -sun/security/pkcs11/Mac/ReinitMac.java generic-all -sun/security/pkcs11/MessageDigest/ByteBuffers.java generic-all -sun/security/pkcs11/MessageDigest/DigestKAT.java generic-all -sun/security/pkcs11/MessageDigest/ReinitDigest.java generic-all -sun/security/pkcs11/MessageDigest/TestCloning.java generic-all -sun/security/pkcs11/Provider/ConfigQuotedString.sh generic-all -sun/security/pkcs11/Provider/Login.sh generic-all -sun/security/pkcs11/SampleTest.java generic-all -sun/security/pkcs11/Secmod/AddPrivateKey.java generic-all -sun/security/pkcs11/Secmod/AddTrustedCert.java generic-all -sun/security/pkcs11/Secmod/Crypto.java generic-all -sun/security/pkcs11/Secmod/GetPrivateKey.java generic-all -sun/security/pkcs11/Secmod/JksSetPrivateKey.java generic-all -sun/security/pkcs11/SecureRandom/Basic.java generic-all -sun/security/pkcs11/SecureRandom/TestDeserialization.java generic-all -sun/security/pkcs11/Serialize/SerializeProvider.java generic-all -sun/security/pkcs11/Signature/ByteBuffers.java generic-all -sun/security/pkcs11/Signature/ReinitSignature.java generic-all -sun/security/pkcs11/Signature/TestDSA.java generic-all -sun/security/pkcs11/Signature/TestDSAKeyLength.java generic-all -sun/security/pkcs11/Signature/TestRSAKeyLength.java generic-all -sun/security/pkcs11/ec/ReadCertificates.java generic-all -sun/security/pkcs11/ec/ReadPKCS12.java generic-all -sun/security/pkcs11/ec/TestCurves.java generic-all -sun/security/pkcs11/ec/TestECDH.java generic-all -sun/security/pkcs11/ec/TestECDH2.java generic-all -sun/security/pkcs11/ec/TestECDSA.java generic-all -sun/security/pkcs11/ec/TestECDSA2.java generic-all -sun/security/pkcs11/ec/TestECGenSpec.java generic-all -sun/security/pkcs11/rsa/KeyWrap.java generic-all -sun/security/pkcs11/rsa/TestCACerts.java generic-all -sun/security/pkcs11/rsa/TestKeyFactory.java generic-all -sun/security/pkcs11/rsa/TestKeyPairGenerator.java generic-all -sun/security/pkcs11/rsa/TestSignatures.java generic-all -sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java generic-all -sun/security/pkcs11/tls/TestKeyMaterial.java generic-all -sun/security/pkcs11/tls/TestLeadingZeroesP11.java generic-all -sun/security/pkcs11/tls/TestMasterSecret.java generic-all -sun/security/pkcs11/tls/TestPRF.java generic-all -sun/security/pkcs11/tls/TestPremaster.java generic-all sun/security/pkcs12/Bug6415637.java generic-all sun/security/pkcs12/StorePasswordTest.java generic-all sun/security/pkcs12/StoreSecretKeyTest.java generic-all @@ -3408,6 +3156,12 @@ vm/verifier/TestStaticIF.java vm/verifier/VerifyProtectedConstructor.java generic-all vm/verifier/defaultMethods/DefaultMethodRegressionTestsRun.java generic-all +# these test try to fill up memory but catch an exception that isn't going to happen on .NET +# the tests should be fixed +java/beans/Introspector/Test7172865.java generic-all +java/beans/Introspector/Test7195106.java generic-all +java/beans/Introspector/Test8027905.java generic-all + # we never implemented memory MX stuff for Linux com/sun/management/OperatingSystemMXBean/GetCommittedVirtualMemorySize.java linux-all com/sun/management/OperatingSystemMXBean/GetFreePhysicalMemorySize.java linux-all @@ -3473,7 +3227,9 @@ java/nio/channels/SocketChannel/Connect.java java/nio/charset/RemovingSunIO/TestCOMP.java linux-all java/nio/charset/Charset/AvailableCharsetNames.java linux-all -java/nio/charset/Charset/NIOCharsetAvailabilityTest.java linux-all + +# COMPOUND_TEXT is compiled in all cases, but only available on !Windows, which breaks tests on Windows +java/nio/charset/Charset/NIOCharsetAvailabilityTest.java windows-all java/nio/file/attribute/DosFileAttributeView/Basic.java linux-all java/nio/file/Files/FileAttributes.java linux-all @@ -3510,3 +3266,86 @@ sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/AsyncSSLSocketClose. sun/tools/native2ascii/Permission.java linux-all tools/jar/UpdateJar.java linux-all + +# uses System.Drawing, not available on Linux +javax/imageio/plugins/png/MergeStdCommentTest.java linux-all +javax/imageio/EmptyFileTest.java linux-all +sun/java2d/cmm/ColorConvertOp/InvalidRenderIntentTest.java linux-all + +# uses Win32 specific stuff, not available on Linux +javax/print/PrintServiceLookup/CountPrintServices.java linux-all + +# PKCS 11 is disabled because Cryptoki isn't built +sun/security/pkcs11/Cipher/ReinitCipher.java generic-all +sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java generic-all +sun/security/pkcs11/Cipher/TestRSACipher.java generic-all +sun/security/pkcs11/Cipher/TestRSACipherWrap.java generic-all +sun/security/pkcs11/Cipher/TestRawRSACipher.java generic-all +sun/security/pkcs11/Cipher/TestSymmCiphers.java generic-all +sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java generic-all +sun/security/pkcs11/KeyAgreement/TestDH.java generic-all +sun/security/pkcs11/KeyAgreement/TestInterop.java generic-all +sun/security/pkcs11/KeyAgreement/TestShort.java generic-all +sun/security/pkcs11/KeyGenerator/DESParity.java generic-all +sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java generic-all +sun/security/pkcs11/KeyPairGenerator/TestDH2048.java generic-all +sun/security/pkcs11/KeyStore/SecretKeysBasic.sh generic-all +sun/security/pkcs11/Mac/ReinitMac.java generic-all +sun/security/pkcs11/MessageDigest/ByteBuffers.java generic-all +sun/security/pkcs11/MessageDigest/DigestKAT.java generic-all +sun/security/pkcs11/MessageDigest/ReinitDigest.java generic-all +sun/security/pkcs11/MessageDigest/TestCloning.java generic-all +sun/security/pkcs11/Provider/ConfigQuotedString.sh generic-all +sun/security/pkcs11/Provider/Login.sh generic-all +sun/security/pkcs11/SampleTest.java generic-all +sun/security/pkcs11/Secmod/AddPrivateKey.java generic-all +sun/security/pkcs11/Secmod/AddTrustedCert.java generic-all +sun/security/pkcs11/Secmod/Crypto.java generic-all +sun/security/pkcs11/Secmod/GetPrivateKey.java generic-all +sun/security/pkcs11/Secmod/JksSetPrivateKey.java generic-all +sun/security/pkcs11/Secmod/TrustAnchors.java generic-all +sun/security/pkcs11/SecureRandom/Basic.java generic-all +sun/security/pkcs11/SecureRandom/TestDeserialization.java generic-all +sun/security/pkcs11/Serialize/SerializeProvider.java generic-all +sun/security/pkcs11/Signature/ByteBuffers.java generic-all +sun/security/pkcs11/Signature/ReinitSignature.java generic-all +sun/security/pkcs11/Signature/TestDSA.java generic-all +sun/security/pkcs11/Signature/TestDSAKeyLength.java generic-all +sun/security/pkcs11/Signature/TestRSAKeyLength.java generic-all +sun/security/pkcs11/ec/ReadCertificates.java generic-all +sun/security/pkcs11/ec/ReadPKCS12.java generic-all +sun/security/pkcs11/ec/TestCurves.java generic-all +sun/security/pkcs11/ec/TestECDH.java generic-all +sun/security/pkcs11/ec/TestECDH2.java generic-all +sun/security/pkcs11/ec/TestECDSA.java generic-all +sun/security/pkcs11/ec/TestECDSA2.java generic-all +sun/security/pkcs11/ec/TestECGenSpec.java generic-all +sun/security/pkcs11/rsa/KeyWrap.java generic-all +sun/security/pkcs11/rsa/TestCACerts.java generic-all +sun/security/pkcs11/rsa/TestKeyFactory.java generic-all +sun/security/pkcs11/rsa/TestKeyPairGenerator.java generic-all +sun/security/pkcs11/rsa/TestSignatures.java generic-all +sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java generic-all +sun/security/pkcs11/tls/TestKeyMaterial.java generic-all +sun/security/pkcs11/tls/TestLeadingZeroesP11.java generic-all +sun/security/pkcs11/tls/TestMasterSecret.java generic-all +sun/security/pkcs11/tls/TestPRF.java generic-all +sun/security/pkcs11/tls/TestPremaster.java generic-all + +# points NativePRNG at a specific file: our implementations don't support this +sun/security/provider/SecureRandom/StrongSeedReader.java generic-all + +# verifies ECC but we don't have ECC yet +javax/net/ssl/ciphersuites/DisabledAlgorithms.java generic-all + +# requires a specific tools.jar, not going to work on IKVM +tools/pack200/DefaultTimeZoneTest.java generic-all + +# this never worked, and might not be possible to make work on .NET +java/net/ServerSocket/AcceptInheritHandle.java generic-all + +# too slow +java/lang/Class/getDeclaredField/FieldSetAccessibleTest.java generic-all + +# test tries to access across assemblies (package private) +sun/util/calendar/zi/TestZoneInfo310.java generic-all diff --git a/src/IKVM.OpenJDK.Tests/langtools/ExcludeList.txt b/src/IKVM.OpenJDK.Tests/langtools/ExcludeList.txt index 5396a2d573..6cb2a28f30 100644 --- a/src/IKVM.OpenJDK.Tests/langtools/ExcludeList.txt +++ b/src/IKVM.OpenJDK.Tests/langtools/ExcludeList.txt @@ -1,799 +1,77 @@ # unknown errors (FIXME) com/sun/javadoc/testGeneratedBy/TestGeneratedBy.java generic-all -lib/combo/tools/javac/combo/Diagnostics.java generic-all -lib/combo/tools/javac/combo/JavacTemplateTestBase.java generic-all -lib/combo/tools/javac/combo/Template.java generic-all -lib/combo/tools/javac/combo/TemplateTest.java generic-all -tools/apt/Basics/CheckAptIsRemovedTest.java generic-all -tools/doclint/AccessTest.java generic-all -tools/doclint/AccessibilityTest.java generic-all -tools/doclint/AnchorTest.java generic-all -tools/doclint/AnchorTest2.java generic-all -tools/doclint/BadPackageCommentTest.java generic-all -tools/doclint/CoverageExtras.java generic-all -tools/doclint/CustomTagTest.java generic-all -tools/doclint/EmptyAuthorTest.java generic-all -tools/doclint/EmptyExceptionTest.java generic-all -tools/doclint/EmptyParamTest.java generic-all -tools/doclint/EmptyPreTest.java generic-all -tools/doclint/EmptyReturnTest.java generic-all -tools/doclint/EmptySerialDataTest.java generic-all -tools/doclint/EmptySerialFieldTest.java generic-all -tools/doclint/EmptySinceTest.java generic-all -tools/doclint/EmptyVersionTest.java generic-all -tools/doclint/EndTagsTest.java generic-all -tools/doclint/EndWithIdentifierTest.java generic-all -tools/doclint/HtmlAttrsTest.java generic-all -tools/doclint/HtmlTagsTest.java generic-all -tools/doclint/LiteralTest.java generic-all -tools/doclint/MissingCommentTest.java generic-all -tools/doclint/MissingParamsTest.java generic-all -tools/doclint/MissingReturnTest.java generic-all -tools/doclint/MissingThrowsTest.java generic-all -tools/doclint/OverridesTest.java generic-all -tools/doclint/ParaTagTest.java generic-all -tools/doclint/ReferenceTest.java generic-all -tools/doclint/ResourceTest.java generic-all -tools/doclint/RunTest.java generic-all -tools/doclint/SyntaxTest.java generic-all -tools/doclint/SyntheticTest.java generic-all -tools/doclint/UnfinishedInlineTagTest.java generic-all -tools/doclint/ValidTest.java generic-all -tools/doclint/ValueTest.java generic-all -tools/doclint/anchorTests/p/Test.java generic-all -tools/doclint/anchorTests/p/package-info.java generic-all -tools/doclint/html/BlockTagsTest.java generic-all -tools/doclint/html/EntitiesTest.java generic-all -tools/doclint/html/InlineTagsTest.java generic-all -tools/doclint/html/ListTagsTest.java generic-all -tools/doclint/html/OtherTagsTest.java generic-all -tools/doclint/html/TableTagsTest.java generic-all -tools/doclint/html/TagNotAllowed.java generic-all -tools/doclint/html/TextNotAllowed.java generic-all -tools/doclint/packageTests/bad/Test.java generic-all -tools/doclint/packageTests/bad/package-info.java generic-all -tools/doclint/packageTests/good/Test.java generic-all -tools/doclint/packageTests/good/package-info.java generic-all -tools/doclint/tidy/AnchorAlreadyDefined.java generic-all -tools/doclint/tidy/BadEnd.java generic-all -tools/doclint/tidy/InsertImplicit.java generic-all -tools/doclint/tidy/InvalidEntity.java generic-all -tools/doclint/tidy/InvalidName.java generic-all -tools/doclint/tidy/InvalidTag.java generic-all -tools/doclint/tidy/InvalidURI.java generic-all -tools/doclint/tidy/MissingGT.java generic-all -tools/doclint/tidy/MissingTag.java generic-all -tools/doclint/tidy/NestedTag.java generic-all -tools/doclint/tidy/ParaInPre.java generic-all -tools/doclint/tidy/RepeatedAttr.java generic-all -tools/doclint/tidy/TextNotAllowed.java generic-all -tools/doclint/tidy/TrimmingEmptyTag.java generic-all -tools/doclint/tidy/UnescapedOrUnknownEntity.java generic-all -tools/doclint/tool/HelpTest.java generic-all -tools/doclint/tool/MaxDiagsTest.java generic-all -tools/doclint/tool/PathsTest.java generic-all -tools/doclint/tool/RunTest.java generic-all -tools/doclint/tool/StatsTest.java generic-all tools/javac/4846262/CheckEBCDICLocaleTest.java generic-all tools/javac/4917091/Test255.java generic-all -tools/javac/6302184/HiddenOptionsShouldUseGivenEncodingTest.java generic-all -tools/javac/6304921/TestLog.java generic-all -tools/javac/6330997/T6330997.java generic-all -tools/javac/6341866/T6341866.java generic-all -tools/javac/6400872/T6400872.java generic-all -tools/javac/6402516/CheckClass.java generic-all -tools/javac/6402516/CheckIsAccessible.java generic-all -tools/javac/6402516/CheckLocalElements.java generic-all -tools/javac/6402516/CheckMethod.java generic-all -tools/javac/6440583/T6440583.java generic-all -tools/javac/6457284/T6457284.java generic-all tools/javac/6508981/TestInferBinaryName.java generic-all -tools/javac/6567415/T6567415.java generic-all tools/javac/6589361/T6589361.java generic-all -tools/javac/6627362/T6627362.java generic-all -tools/javac/6863465/TestCircularClassfile.java generic-all -tools/javac/6889255/T6889255.java generic-all -tools/javac/6902720/Test.java generic-all -tools/javac/7003595/T7003595.java generic-all -tools/javac/7079713/TestCircularClassfile.java generic-all -tools/javac/7118412/ShadowingTest.java generic-all -tools/javac/7129225/TestImportStar.java generic-all -tools/javac/7142086/T7142086.java generic-all -tools/javac/7144981/IgnoreIgnorableCharactersInInput.java generic-all -tools/javac/7153958/CPoolRefClassContainingInlinedCts.java generic-all -tools/javac/7166455/CheckACC_STRICTFlagOnclinitTest.java generic-all -tools/javac/7199823/InnerClassCannotBeVerified.java generic-all -tools/javac/8000518/DuplicateConstantPoolEntry.java generic-all -tools/javac/8005931/CheckACC_STRICTFlagOnPkgAccessClassTest.java generic-all -tools/javac/8009170/RedundantByteCodeInArrayTest.java generic-all -tools/javac/AnonymousSubclassTest.java generic-all tools/javac/ArrayCloneCodeGen.java generic-all -tools/javac/BadOptimization/DeadCode3.java generic-all -tools/javac/BadOptimization/DeadCode4.java generic-all -tools/javac/BadOptimization/DeadCode5.java generic-all tools/javac/ClassPathTest/ClassPathTest.java generic-all -tools/javac/Diagnostics/6769027/T6769027.java generic-all -tools/javac/Diagnostics/7010608/Test.java generic-all -tools/javac/Diagnostics/7116676/T7116676.java generic-all tools/javac/EarlyAssert.java generic-all -tools/javac/ExtDirs/ExtDirTest.java generic-all -tools/javac/IncorrectInheritance/IncorrectInheritanceTest.java generic-all -tools/javac/InnerMethSig.java generic-all -tools/javac/MethodParameters/AnnotationTest.java generic-all -tools/javac/MethodParameters/AnonymousClass.java generic-all -tools/javac/MethodParameters/Constructors.java generic-all -tools/javac/MethodParameters/EnumTest.java generic-all -tools/javac/MethodParameters/InstanceMethods.java generic-all -tools/javac/MethodParameters/LambdaTest.java generic-all -tools/javac/MethodParameters/LocalClassTest.java generic-all -tools/javac/MethodParameters/MemberClassTest.java generic-all -tools/javac/MethodParameters/StaticMethods.java generic-all -tools/javac/MethodParameters/UncommonParamNames.java generic-all -tools/javac/MethodParametersTest.java generic-all -tools/javac/MissingInclude/MissingIncludeTest.java generic-all -tools/javac/NoStringToLower.java generic-all -tools/javac/Paths/6638501/JarFromManifestFailure.java generic-all tools/javac/Paths/Class-Path.sh generic-all tools/javac/Paths/Class-Path2.sh generic-all tools/javac/Paths/Diagnostics.sh generic-all tools/javac/Paths/Help.sh generic-all tools/javac/Paths/MineField.sh generic-all -tools/javac/Paths/TestCompileJARInClassPath.java generic-all tools/javac/Paths/wcMineField.sh generic-all -tools/javac/ProtectedInnerClass/ProtectedInnerClassesTest.java generic-all -tools/javac/T5090006/AssertionFailureTest.java generic-all -tools/javac/T6214885.java generic-all -tools/javac/T6265400.java generic-all -tools/javac/T6340549.java generic-all -tools/javac/T6351767.java generic-all -tools/javac/T6358024.java generic-all -tools/javac/T6358166.java generic-all -tools/javac/T6358168.java generic-all -tools/javac/T6361619.java generic-all -tools/javac/T6395974.java generic-all -tools/javac/T6397044.java generic-all -tools/javac/T6397286.java generic-all -tools/javac/T6403466.java generic-all -tools/javac/T6406771.java generic-all -tools/javac/T6407066.java generic-all -tools/javac/T6410706.java generic-all -tools/javac/T6411379.java generic-all -tools/javac/T6423583.java generic-all -tools/javac/T6435291/T6435291.java generic-all -tools/javac/T6458823/T6458823.java generic-all -tools/javac/T6472751.java generic-all tools/javac/T6558476.java generic-all -tools/javac/T6625520.java generic-all -tools/javac/T6654037.java generic-all -tools/javac/T6665791.java generic-all -tools/javac/T6705935.java generic-all -tools/javac/T6855236.java generic-all tools/javac/T6873845.java generic-all -tools/javac/T6900149.java generic-all -tools/javac/T6956462/T6956462.java generic-all -tools/javac/T6956638.java generic-all -tools/javac/T6970173/DebugPointerAtBadPositionTest.java generic-all -tools/javac/T6972327.java generic-all -tools/javac/T6993301.java generic-all -tools/javac/T7008643/InlinedFinallyConfuseDebuggersTest.java generic-all tools/javac/T7093325.java generic-all -tools/javac/T7142672/Bug.java generic-all -tools/javac/T7159016.java generic-all -tools/javac/T7165659/InnerClassAttrMustNotHaveStrictFPFlagTest.java generic-all tools/javac/T8003967/DetectMutableStaticFields.java generic-all -tools/javac/T8004969.java generic-all -tools/javac/T8009640/CheckRejectProfileBCPOptionsIfUsedTogetherTest.java generic-all -tools/javac/T8010659/CompilerCrashWhenMixingBinariesAndSourcesTest.java generic-all -tools/javac/T8010737/ParameterNamesAreNotCopiedToAnonymousInitTest.java generic-all -tools/javac/T8011181/EmptyUTF8ForInnerClassNameTest.java generic-all -tools/javac/T8013394/CompileErrorWithIteratorTest.java generic-all -tools/javac/T8019486/WrongLNTForLambdaTest.java generic-all -tools/javac/T8022162/IncorrectSignatureDeterminationForInnerClassesTest.java generic-all -tools/javac/T8022186/DeadCodeGeneratedForEmptyTryTest.java generic-all -tools/javac/T8023112/SkipLazyConstantCreationForMethodRefTest.java generic-all -tools/javac/T8024039/NoDeadCodeGenerationOnTrySmtTest.java generic-all tools/javac/T8024207/FlowCrashTest.java generic-all -tools/javac/T8024437/ExceptionInferenceFromClassFileTest.java generic-all tools/javac/TryWithResources/InterruptedExceptionTest.java generic-all -tools/javac/TryWithResources/UnusedResourcesTest.java generic-all +tools/javac/TryWithResources/UnusedResourcesTest.java linux-all tools/javac/VarDeclarationWithAssignment.java generic-all -tools/javac/annotations/6550655/T6550655.java generic-all -tools/javac/annotations/TestAnnotationPackageInfo.java generic-all -tools/javac/annotations/neg/8022765/T8022765.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/BasicSyntaxCombo.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/DeprecatedAnnoCombo.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/DocumentedAnnoCombo.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/InheritedAnnoCombo.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/ReflectionTest.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/RetentionAnnoCombo.java generic-all -tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java generic-all -tools/javac/annotations/typeAnnotations/TypeProcOnly.java generic-all -tools/javac/annotations/typeAnnotations/api/AnnotatedArrayOrder.java generic-all -tools/javac/annotations/typeAnnotations/api/ArrayCreationTree.java generic-all -tools/javac/annotations/typeAnnotations/api/ArrayPositionConsistency.java generic-all -tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest1.java generic-all -tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java generic-all -tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest3.java generic-all -tools/javac/annotations/typeAnnotations/classfile/DeadCode.java generic-all -tools/javac/annotations/typeAnnotations/classfile/NewTypeArguments.java generic-all -tools/javac/annotations/typeAnnotations/classfile/Scopes.java generic-all -tools/javac/annotations/typeAnnotations/classfile/T8008762.java generic-all -tools/javac/annotations/typeAnnotations/classfile/T8008769.java generic-all -tools/javac/annotations/typeAnnotations/classfile/T8010015.java generic-all -tools/javac/annotations/typeAnnotations/classfile/TestAnonInnerClasses.java generic-all -tools/javac/annotations/typeAnnotations/classfile/TypeCasts.java generic-all -tools/javac/annotations/typeAnnotations/classfile/Wildcards.java generic-all -tools/javac/annotations/typeAnnotations/failures/CheckErrorsForSource7.java generic-all -tools/javac/annotations/typeAnnotations/failures/T8020715.java generic-all -tools/javac/annotations/typeAnnotations/newlocations/AfterMethodTypeParams.java generic-all -tools/javac/annotations/typeAnnotations/newlocations/Lambda.java generic-all -tools/javac/annotations/typeAnnotations/newlocations/NestedTypes.java generic-all -tools/javac/annotations/typeAnnotations/packageanno/PackageProcessor.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/ClassExtends.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/ClassTypeParam.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/Constructors.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/ExceptionParameters.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/Fields.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/FromSpecification.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/Initializers.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/Lambda.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MethodParameters.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MethodReceivers.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MethodReturns.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MethodThrows.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MethodTypeParam.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/MultiCatch.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/NestedTypes.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/NewObjects.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/RepeatingTypeAnnotations.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/TypeCasts.java generic-all -tools/javac/annotations/typeAnnotations/referenceinfos/TypeTests.java generic-all -tools/javac/api/6400303/T6400303.java generic-all -tools/javac/api/6406133/T6406133.java generic-all -tools/javac/api/6410643/T6410643.java generic-all +tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest1.java linux-all +tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java linux-all tools/javac/api/6411310/T6411310.java generic-all tools/javac/api/6411310/Test.java generic-all -tools/javac/api/6411333/T6411333.java generic-all -tools/javac/api/6412656/T6412656.java generic-all -tools/javac/api/6415780/T6415780.java generic-all -tools/javac/api/6418694/T6418694.java generic-all -tools/javac/api/6420409/T6420409.java generic-all -tools/javac/api/6420464/T6420464.java generic-all -tools/javac/api/6421111/T6421111.java generic-all -tools/javac/api/6421756/T6421756.java generic-all -tools/javac/api/6422215/T6422215.java generic-all -tools/javac/api/6422327/T6422327.java generic-all -tools/javac/api/6423003/T6423003.java generic-all -tools/javac/api/6431257/T6431257.java generic-all -tools/javac/api/6431435/T6431435.java generic-all -tools/javac/api/6437349/T6437349.java generic-all -tools/javac/api/6437999/T6437999.java generic-all -tools/javac/api/6440333/T6440333.java generic-all -tools/javac/api/6440528/T6440528.java generic-all -tools/javac/api/6468404/T6468404.java generic-all -tools/javac/api/6471599/Main.java generic-all -tools/javac/api/6557752/T6557752.java generic-all -tools/javac/api/6598108/T6598108.java generic-all -tools/javac/api/6608214/T6608214.java generic-all -tools/javac/api/6731573/T6731573.java generic-all -tools/javac/api/6733837/T6733837.java generic-all -tools/javac/api/6852595/T6852595.java generic-all -tools/javac/api/7086261/T7086261.java generic-all -tools/javac/api/8007344/Test.java generic-all -tools/javac/api/EndPositions.java generic-all -tools/javac/api/Sibling.java generic-all -tools/javac/api/T6257235.java generic-all -tools/javac/api/T6258271.java generic-all -tools/javac/api/T6265137.java generic-all -tools/javac/api/T6306137.java generic-all -tools/javac/api/T6345974.java generic-all -tools/javac/api/T6357331.java generic-all -tools/javac/api/T6358786.java generic-all -tools/javac/api/T6358955.java generic-all -tools/javac/api/T6392782.java generic-all -tools/javac/api/T6395981.java generic-all -tools/javac/api/T6397104.java generic-all -tools/javac/api/T6400205.java generic-all -tools/javac/api/T6400207.java generic-all -tools/javac/api/T6412669.java generic-all -tools/javac/api/T6419926.java generic-all tools/javac/api/T6430241.java generic-all -tools/javac/api/T6431879.java generic-all -tools/javac/api/T6437138.java generic-all -tools/javac/api/T6483788.java generic-all -tools/javac/api/T6838467.java generic-all tools/javac/api/T6877206.java generic-all -tools/javac/api/TestClientCodeWrapper.java generic-all tools/javac/api/TestContainTypes.java generic-all -tools/javac/api/TestDocComments.java generic-all -tools/javac/api/TestEvalExpression.java generic-all tools/javac/api/TestGetElement.java generic-all -tools/javac/api/TestGetElementReference.java generic-all -tools/javac/api/TestGetScope.java generic-all -tools/javac/api/TestGetTree.java generic-all -tools/javac/api/TestJavacTask.java generic-all -tools/javac/api/TestJavacTaskScanner.java generic-all -tools/javac/api/TestJavacTask_Lock.java generic-all -tools/javac/api/TestJavacTask_Multiple.java generic-all -tools/javac/api/TestJavacTask_ParseAttrGen.java generic-all -tools/javac/api/TestOperators.java generic-all -tools/javac/api/TestResolveError.java generic-all -tools/javac/api/TestResolveIdent.java generic-all -tools/javac/api/TestTreePath.java generic-all -tools/javac/api/TestTrees.java generic-all tools/javac/api/ToolProvider/HelloWorldTest.java generic-all tools/javac/api/ToolProvider/ToolProviderTest1.java generic-all tools/javac/api/ToolProvider/ToolProviderTest2.java generic-all -tools/javac/api/guide/Test.java generic-all -tools/javac/api/taskListeners/EventsBalancedTest.java generic-all -tools/javac/api/taskListeners/TestSimpleAddRemove.java generic-all -tools/javac/assert/Position.java generic-all -tools/javac/cast/intersection/IntersectionTypeCastTest.java generic-all -tools/javac/cast/intersection/IntersectionTypeParserTest.java generic-all -tools/javac/cast/intersection/model/Model01.java generic-all -tools/javac/classfiles/InnerClasses/SyntheticClasses.java generic-all -tools/javac/classreader/T7031108.java generic-all -tools/javac/conditional/T8016702.java generic-all tools/javac/defaultMethods/Assertions.java generic-all -tools/javac/defaultMethods/BadClassfile.java generic-all -tools/javac/defaultMethods/CheckACC_STRICTFlagOnDefaultMethodTest.java generic-all -tools/javac/defaultMethods/DefaultMethodFlags.java generic-all -tools/javac/defaultMethods/Pos01.java generic-all -tools/javac/defaultMethods/TestDefaultBody.java generic-all tools/javac/defaultMethods/static/hiding/InterfaceMethodHidingTest.java generic-all tools/javac/defaultMethods/super/TestDefaultSuperCall.java generic-all -tools/javac/defaultMethods/super/TestDirectSuperInterfaceInvoke.java generic-all tools/javac/defaultMethods/syntax/TestDefaultMethodsSyntax.java generic-all -tools/javac/defaultMethodsVisibility/DefaultMethodsNotVisibleForSourceLessThan8Test.java generic-all tools/javac/diags/CheckExamples.java generic-all tools/javac/diags/CheckResourceKeys.java generic-all -tools/javac/diags/MessageInfo.java generic-all tools/javac/diags/RunExamples.java generic-all -tools/javac/doclint/DocLintTest.java generic-all -tools/javac/doctree/AttrTest.java generic-all -tools/javac/doctree/AuthorTest.java generic-all -tools/javac/doctree/BadTest.java generic-all -tools/javac/doctree/CodeTest.java generic-all -tools/javac/doctree/DeprecatedTest.java generic-all -tools/javac/doctree/DocRootTest.java generic-all -tools/javac/doctree/DocTreePathScannerTest.java generic-all -tools/javac/doctree/ElementTest.java generic-all -tools/javac/doctree/EntityTest.java generic-all -tools/javac/doctree/ExceptionTest.java generic-all -tools/javac/doctree/FirstSentenceTest.java generic-all -tools/javac/doctree/InheritDocTest.java generic-all -tools/javac/doctree/LinkPlainTest.java generic-all -tools/javac/doctree/LinkTest.java generic-all -tools/javac/doctree/LiteralTest.java generic-all -tools/javac/doctree/ParamTest.java generic-all -tools/javac/doctree/ReferenceTest.java generic-all -tools/javac/doctree/ReturnTest.java generic-all -tools/javac/doctree/SeeTest.java generic-all -tools/javac/doctree/SerialDataTest.java generic-all -tools/javac/doctree/SerialFieldTest.java generic-all -tools/javac/doctree/SerialTest.java generic-all -tools/javac/doctree/SimpleDocTreeVisitorTest.java generic-all -tools/javac/doctree/SinceTest.java generic-all -tools/javac/doctree/TagTest.java generic-all -tools/javac/doctree/ThrowableTest.java generic-all -tools/javac/doctree/ValueTest.java generic-all -tools/javac/doctree/VersionTest.java generic-all -tools/javac/doctree/positions/TestPosition.java generic-all -tools/javac/enum/6350057/T6350057.java generic-all -tools/javac/enum/6424358/T6424358.java generic-all -tools/javac/expression/_super/NonDirectSuper/NonDirectSuper.java generic-all tools/javac/failover/CheckAttributedTree.java generic-all tools/javac/fatalErrors/NoJavaLangTest.java generic-all -tools/javac/file/T7018098.java generic-all -tools/javac/file/T7068437.java generic-all -tools/javac/file/T7068451.java generic-all tools/javac/file/zip/T6865530.java generic-all -tools/javac/flow/LVTHarness.java generic-all -tools/javac/flow/T8042741/LambdaArgumentsTest.java generic-all -tools/javac/flow/T8042741/PositionTest.java generic-all -tools/javac/flow/T8062747.java generic-all -tools/javac/generics/6413682/TestPos.java generic-all -tools/javac/generics/T5094318.java generic-all -tools/javac/generics/bridges/BridgeHarness.java generic-all -tools/javac/generics/diamond/6996914/T6996914a.java generic-all tools/javac/generics/diamond/7030150/GenericConstructorAndDiamondTest.java generic-all -tools/javac/generics/diamond/7030687/ParserTest.java generic-all tools/javac/generics/diamond/7046778/DiamondAndInnerClassTest.java generic-all tools/javac/generics/inference/7086601/T7086601b.java generic-all -tools/javac/generics/inference/8020149/T8020149.java generic-all -tools/javac/generics/inference/EagerReturnTypeResolution/PrimitiveTypeBoxingTest.java generic-all -tools/javac/generics/inference/T8028503/PrimitiveTypeInBoundForMethodRefTest.java generic-all -tools/javac/generics/inference/T8044546/CrashImplicitLambdaTest.java generic-all tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java generic-all -tools/javac/generics/wildcards/pos/RvalConversion.java generic-all tools/javac/inheritedAccess/MethodReferenceQualification_1.java generic-all -tools/javac/innerClassFile/InnerClassFileTest.java generic-all -tools/javac/javazip/JavaZipTest.java generic-all tools/javac/jvm/T7024096.java generic-all tools/javac/jvm/T8020689.java generic-all -tools/javac/lambda/8012557/PrivateLambdas.java generic-all -tools/javac/lambda/8016081/T8016081.java generic-all -tools/javac/lambda/8016177/T8016177d.java generic-all -tools/javac/lambda/8016177/T8016177e.java generic-all -tools/javac/lambda/8016177/T8016177f.java generic-all -tools/javac/lambda/8016320/IllegalBridgeModifier.java generic-all -tools/javac/lambda/8020804/T8020804.java generic-all -tools/javac/lambda/8021567/T8021567b.java generic-all -tools/javac/lambda/8023389/T8023389.java generic-all -tools/javac/lambda/8023558/T8023558a.java generic-all -tools/javac/lambda/8023558/T8023558b.java generic-all -tools/javac/lambda/8023558/T8023558c.java generic-all -tools/javac/lambda/8024497/CrashUsingReturningThisRefLambdaFromDefaultMetTest.java generic-all -tools/javac/lambda/8051958/T8051958.java generic-all -tools/javac/lambda/BadLambdaExpr.java generic-all -tools/javac/lambda/ByteCodeTest.java generic-all -tools/javac/lambda/Defender01.java generic-all -tools/javac/lambda/EffectivelyFinalThrows.java generic-all -tools/javac/lambda/FunctionalInterfaceConversionTest.java generic-all -tools/javac/lambda/GenericMethodRefImplClass.java generic-all -tools/javac/lambda/InnerConstructor.java generic-all -tools/javac/lambda/Intersection01.java generic-all -tools/javac/lambda/LambdaCapture01.java generic-all -tools/javac/lambda/LambdaCapture02.java generic-all -tools/javac/lambda/LambdaCapture03.java generic-all -tools/javac/lambda/LambdaCapture04.java generic-all -tools/javac/lambda/LambdaCapture05.java generic-all -tools/javac/lambda/LambdaCapture06.java generic-all -tools/javac/lambda/LambdaCapture07.java generic-all -tools/javac/lambda/LambdaConv01.java generic-all -tools/javac/lambda/LambdaConv03.java generic-all -tools/javac/lambda/LambdaConv05.java generic-all -tools/javac/lambda/LambdaConv06.java generic-all tools/javac/lambda/LambdaConv08.java generic-all -tools/javac/lambda/LambdaConv11.java generic-all -tools/javac/lambda/LambdaConv12.java generic-all -tools/javac/lambda/LambdaConv13.java generic-all tools/javac/lambda/LambdaConv16.java generic-all -tools/javac/lambda/LambdaConv17.java generic-all -tools/javac/lambda/LambdaConv19.java generic-all -tools/javac/lambda/LambdaConv20.java generic-all -tools/javac/lambda/LambdaConv22.java generic-all -tools/javac/lambda/LambdaConv23.java generic-all -tools/javac/lambda/LambdaConv24.java generic-all -tools/javac/lambda/LambdaConv26.java generic-all -tools/javac/lambda/LambdaConv27.java generic-all -tools/javac/lambda/LambdaExpr01.java generic-all -tools/javac/lambda/LambdaExpr02.java generic-all -tools/javac/lambda/LambdaExpr04.java generic-all -tools/javac/lambda/LambdaExpr05.java generic-all -tools/javac/lambda/LambdaExpr06.java generic-all -tools/javac/lambda/LambdaExpr07.java generic-all -tools/javac/lambda/LambdaExpr08.java generic-all -tools/javac/lambda/LambdaExpr09.java generic-all -tools/javac/lambda/LambdaExpr11.java generic-all -tools/javac/lambda/LambdaExpr12.java generic-all -tools/javac/lambda/LambdaExpr13.java generic-all -tools/javac/lambda/LambdaExpr14.java generic-all -tools/javac/lambda/LambdaExpr15.java generic-all -tools/javac/lambda/LambdaExpr16.java generic-all -tools/javac/lambda/LambdaExpr17.java generic-all -tools/javac/lambda/LambdaExpr18.java generic-all -tools/javac/lambda/LambdaExpr20.java generic-all -tools/javac/lambda/LambdaExpr21.java generic-all -tools/javac/lambda/LambdaExprLeadsToMissingClassFilesTest.java generic-all -tools/javac/lambda/LambdaInnerTypeVarArgs.java generic-all -tools/javac/lambda/LambdaInnerTypeVarArgsSerialize.java generic-all -tools/javac/lambda/LambdaInnerTypeVarReflect.java generic-all -tools/javac/lambda/LambdaInnerTypeVarSerialize.java generic-all -tools/javac/lambda/LambdaInterfaceStaticField.java generic-all -tools/javac/lambda/LambdaLambdaSerialized.java generic-all -tools/javac/lambda/LambdaLocalTest.java generic-all -tools/javac/lambda/LambdaMultiCatchTest.java generic-all -tools/javac/lambda/LambdaOuterLocalTest.java generic-all -tools/javac/lambda/LambdaParenGeneric.java generic-all -tools/javac/lambda/LambdaParenGenericOrig.java generic-all tools/javac/lambda/LambdaParserTest.java generic-all -tools/javac/lambda/LambdaScope01.java generic-all -tools/javac/lambda/LambdaScope02.java generic-all -tools/javac/lambda/LambdaScope03.java generic-all -tools/javac/lambda/LambdaTestStrictFP.java generic-all -tools/javac/lambda/LambdaTestStrictFPFlag.java generic-all -tools/javac/lambda/LambdaTestStrictFPMethod.java generic-all -tools/javac/lambda/LambdaWithInterfaceSuper.java generic-all -tools/javac/lambda/LocalBreakAndContinue.java generic-all -tools/javac/lambda/LocalVariableTable.java generic-all -tools/javac/lambda/MethodReference01.java generic-all -tools/javac/lambda/MethodReference02.java generic-all -tools/javac/lambda/MethodReference03.java generic-all -tools/javac/lambda/MethodReference05.java generic-all -tools/javac/lambda/MethodReference06.java generic-all -tools/javac/lambda/MethodReference07.java generic-all -tools/javac/lambda/MethodReference08.java generic-all -tools/javac/lambda/MethodReference10.java generic-all -tools/javac/lambda/MethodReference11.java generic-all -tools/javac/lambda/MethodReference12.java generic-all -tools/javac/lambda/MethodReference13.java generic-all -tools/javac/lambda/MethodReference14.java generic-all -tools/javac/lambda/MethodReference15.java generic-all -tools/javac/lambda/MethodReference16.java generic-all -tools/javac/lambda/MethodReference17.java generic-all -tools/javac/lambda/MethodReference18.java generic-all -tools/javac/lambda/MethodReference19.java generic-all -tools/javac/lambda/MethodReference24.java generic-all -tools/javac/lambda/MethodReference26.java generic-all -tools/javac/lambda/MethodReference27.java generic-all -tools/javac/lambda/MethodReference29.java generic-all -tools/javac/lambda/MethodReference30.java generic-all -tools/javac/lambda/MethodReference31.java generic-all -tools/javac/lambda/MethodReference33.java generic-all -tools/javac/lambda/MethodReference34.java generic-all -tools/javac/lambda/MethodReference35.java generic-all -tools/javac/lambda/MethodReference36.java generic-all -tools/javac/lambda/MethodReference41.java generic-all -tools/javac/lambda/MethodReference43.java generic-all -tools/javac/lambda/MethodReference46.java generic-all -tools/javac/lambda/MethodReference49.java generic-all -tools/javac/lambda/MethodReference57.java generic-all -tools/javac/lambda/MethodReference59.java generic-all -tools/javac/lambda/MethodReference61.java generic-all -tools/javac/lambda/MethodReference63.java generic-all -tools/javac/lambda/MethodReference65.java generic-all tools/javac/lambda/MethodReference66.java generic-all -tools/javac/lambda/MethodReference68.java generic-all -tools/javac/lambda/MethodReferenceArrayClone.java generic-all tools/javac/lambda/MethodReferenceParserTest.java generic-all -tools/javac/lambda/MostSpecific10.java generic-all -tools/javac/lambda/MostSpecific11.java generic-all -tools/javac/lambda/NakedThis.java generic-all -tools/javac/lambda/SerializedLambdaInInit.java generic-all -tools/javac/lambda/SingleLocalTest.java generic-all -tools/javac/lambda/T8025290/ExplicitVSImplicitLambdaTest.java generic-all -tools/javac/lambda/T8025816/CrashMethodReferenceWithSiteTypeVarTest.java generic-all -tools/javac/lambda/T8031967.java generic-all -tools/javac/lambda/T8037935/LambdaWithBinOpConstRefToConstString.java generic-all -tools/javac/lambda/T8038420/LambdaIncrement.java generic-all -tools/javac/lambda/T8057800/NPEMethodReferenceAndGenericsTest.java generic-all -tools/javac/lambda/TargetType03.java generic-all -tools/javac/lambda/TargetType05.java generic-all -tools/javac/lambda/TargetType06.java generic-all -tools/javac/lambda/TargetType07.java generic-all -tools/javac/lambda/TargetType08.java generic-all -tools/javac/lambda/TargetType10.java generic-all -tools/javac/lambda/TargetType11.java generic-all -tools/javac/lambda/TargetType12.java generic-all -tools/javac/lambda/TargetType15.java generic-all -tools/javac/lambda/TargetType16.java generic-all -tools/javac/lambda/TargetType18.java generic-all -tools/javac/lambda/TargetType19.java generic-all -tools/javac/lambda/TargetType20.java generic-all -tools/javac/lambda/TargetType25.java generic-all -tools/javac/lambda/TargetType29.java generic-all -tools/javac/lambda/TargetType30.java generic-all -tools/javac/lambda/TargetType31.java generic-all -tools/javac/lambda/TargetType32.java generic-all -tools/javac/lambda/TargetType35.java generic-all -tools/javac/lambda/TargetType42.java generic-all -tools/javac/lambda/TargetType45.java generic-all -tools/javac/lambda/TargetType47.java generic-all -tools/javac/lambda/TargetType48.java generic-all -tools/javac/lambda/TargetType50.java generic-all -tools/javac/lambda/TargetType51.java generic-all -tools/javac/lambda/TargetType53.java generic-all -tools/javac/lambda/TargetType54.java generic-all -tools/javac/lambda/TargetType55.java generic-all -tools/javac/lambda/TargetType58.java generic-all -tools/javac/lambda/TargetType59.java generic-all -tools/javac/lambda/TargetType61.java generic-all -tools/javac/lambda/TargetType62.java generic-all -tools/javac/lambda/TargetType64.java generic-all -tools/javac/lambda/TargetType65.java generic-all -tools/javac/lambda/TargetType70.java generic-all -tools/javac/lambda/TargetType71.java generic-all -tools/javac/lambda/TargetType72.java generic-all -tools/javac/lambda/TargetType73.java generic-all -tools/javac/lambda/TargetType75.java generic-all -tools/javac/lambda/TargetType76.java generic-all -tools/javac/lambda/TestInvokeDynamic.java generic-all -tools/javac/lambda/TestLambdaToMethodStats.java generic-all -tools/javac/lambda/TestSelfRef.java generic-all -tools/javac/lambda/VoidCompatibility.java generic-all -tools/javac/lambda/abort/Abort.java generic-all -tools/javac/lambda/abort/CompletionFailure.java generic-all -tools/javac/lambda/badMemberRefBytecode/TestBadMemberRefBytecode.java generic-all +tools/javac/lambda/TestInvokeDynamic.java linux-all +tools/javac/lambda/TestSelfRef.java linux-all tools/javac/lambda/bridge/TestMetafactoryBridges.java generic-all -tools/javac/lambda/bridge/template_tests/BridgeMethodTestCase.java generic-all tools/javac/lambda/bridge/template_tests/BridgeMethodsTemplateTest.java generic-all -tools/javac/lambda/bytecode/TestLambdaBytecode.java generic-all -tools/javac/lambda/funcInterfaces/LambdaTest1.java generic-all tools/javac/lambda/funcInterfaces/LambdaTest2_SAM1.java generic-all tools/javac/lambda/funcInterfaces/LambdaTest2_SAM2.java generic-all -tools/javac/lambda/inaccessibleMref02/InaccessibleMref02.java generic-all tools/javac/lambda/intersection/IntersectionTargetTypeTest.java generic-all -tools/javac/lambda/lambdaExecution/InInterface.java generic-all -tools/javac/lambda/lambdaExecution/InnerConstructor.java generic-all -tools/javac/lambda/lambdaExecution/LambdaTranslationTest1.java generic-all -tools/javac/lambda/lambdaExecution/LambdaTranslationTest2.java generic-all -tools/javac/lambda/lambdaExpression/LambdaTest1.java generic-all -tools/javac/lambda/lambdaExpression/LambdaTest2.java generic-all -tools/javac/lambda/lambdaExpression/LambdaTest3.java generic-all -tools/javac/lambda/lambdaExpression/LambdaTest4.java generic-all tools/javac/lambda/lambdaExpression/LambdaTest5.java generic-all -tools/javac/lambda/lambdaExpression/LambdaTest6.java generic-all -tools/javac/lambda/lambdaExpression/SamConversion.java generic-all tools/javac/lambda/lambdaExpression/SamConversionComboTest.java generic-all -tools/javac/lambda/lambdaNaming/TestSerializedLambdaNameStability.java generic-all -tools/javac/lambda/methodReference/BridgeMethod.java generic-all -tools/javac/lambda/methodReference/MethodRef1.java generic-all -tools/javac/lambda/methodReference/MethodRef2.java generic-all -tools/javac/lambda/methodReference/MethodRef3.java generic-all -tools/javac/lambda/methodReference/MethodRef4.java generic-all -tools/javac/lambda/methodReference/MethodRef5.java generic-all -tools/javac/lambda/methodReference/MethodRef6.java generic-all -tools/javac/lambda/methodReference/MethodRef7.java generic-all -tools/javac/lambda/methodReference/MethodRef8.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerBootstrap.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerInLambdaNPE1.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerInLambdaNPE2.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerInLambdaVerify1.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerInLambdaVerify2.java generic-all -tools/javac/lambda/methodReference/MethodRefNewInnerInLambdaVerify2simple.java generic-all -tools/javac/lambda/methodReference/MethodRefQualifier1.java generic-all -tools/javac/lambda/methodReference/MethodRefSingleRefEvalBridge.java generic-all -tools/javac/lambda/methodReference/MethodRefToInner.java generic-all -tools/javac/lambda/methodReference/MethodReferenceComplexNullCheckTest.java generic-all -tools/javac/lambda/methodReference/SamConversion.java generic-all tools/javac/lambda/methodReference/SamConversionComboTest.java generic-all -tools/javac/lambda/methodReference/TreeMakerParamsIsGoofy.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection1.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection2.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceIntersection3.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceNullCheckTest.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestFDCCE.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestInnerDefault.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestInnerInstance.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestInnerVarArgsThis.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestInstance.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestKinds.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestMethodHandle.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNew.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInner.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestSueCase1.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestSueCase2.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestSueCase4.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestSuper.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestSuperDefault.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestTypeConversion.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestVarArgs.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestVarArgsExt.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestVarArgsSuper.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestVarArgsSuperDefault.java generic-all -tools/javac/lambda/methodReferenceExecution/MethodReferenceTestVarArgsThis.java generic-all -tools/javac/lambda/mostSpecific/StructuralMostSpecificTest.java generic-all -tools/javac/lambda/privateMethodReferences/MethodInvoker.java generic-all -tools/javac/lambda/privateMethodReferences/MethodSupplier.java generic-all -tools/javac/lambda/privateMethodReferences/ThirdClass.java generic-all -tools/javac/lambda/separate/Test.java generic-all -tools/javac/lambda/typeInference/InferenceTest11.java generic-all -tools/javac/lambda/typeInference/InferenceTest2.java generic-all -tools/javac/lambda/typeInference/InferenceTest2b.java generic-all -tools/javac/lambda/typeInference/InferenceTest3.java generic-all -tools/javac/lambda/typeInference/InferenceTest4.java generic-all -tools/javac/lambda/typeInference/InferenceTest6.java generic-all -tools/javac/lambda/typeInference/InferenceTest789.java generic-all -tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java generic-all tools/javac/lambdaShapes/org/openjdk/tests/javac/FDTest.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/AttributeInjector.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/ClassFile.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/ClassFilePreprocessor.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/ClassToInterfaceConverter.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/Compiler.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/DirectedClassLoader.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/SourceModel.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/separate/TestHarness.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/ClassCase.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/Hierarchy.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/HierarchyGenerator.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/Rule.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/RuleGroup.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/TTNode.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/TTParser.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/shapegen/TTShape.java generic-all tools/javac/lambdaShapes/org/openjdk/tests/vm/DefaultMethodsTest.java generic-all -tools/javac/lambdaShapes/org/openjdk/tests/vm/FDSeparateCompilationTest.java generic-all -tools/javac/limits/NestedClassConstructorArgs.java generic-all -tools/javac/limits/NestedClassMethodArgs.java generic-all -tools/javac/limits/StaticNestedClassConstructorArgs.java generic-all -tools/javac/limits/TopLevelClassConstructorArgs.java generic-all -tools/javac/limits/TopLevelClassMethodArgs.java generic-all -tools/javac/limits/TopLevelClassStaticMethodArgs.java generic-all -tools/javac/links/LinksTest.java generic-all -tools/javac/meth/TestCP.java generic-all -tools/javac/multicatch/7005371/T7005371.java generic-all tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java generic-all -tools/javac/multicatch/Pos05.java generic-all -tools/javac/multicatch/model/ModelChecker.java generic-all -tools/javac/nativeHeaders/NativeHeaderTest.java generic-all tools/javac/newlines/NewLineTest.java generic-all tools/javac/nio/compileTest/CompileTest.java generic-all -tools/javac/parser/ExtraSemiTest.java generic-all -tools/javac/parser/JavacParserTest.java generic-all -tools/javac/parser/StringFoldingTest.java generic-all -tools/javac/plugin/showtype/Test.java generic-all -tools/javac/positions/T6402077.java generic-all -tools/javac/positions/T6404194.java generic-all -tools/javac/positions/TreeEndPosTest.java generic-all -tools/javac/processing/6348193/T6348193.java generic-all -tools/javac/processing/6348499/T6348499.java generic-all -tools/javac/processing/6350124/T6350124.java generic-all -tools/javac/processing/6365040/T6365040.java generic-all -tools/javac/processing/6378728/T6378728.java generic-all -tools/javac/processing/6414633/T6414633.java generic-all -tools/javac/processing/6430209/T6430209.java generic-all -tools/javac/processing/6499119/ClassProcessor.java generic-all -tools/javac/processing/6994946/SemanticErrorTest.java generic-all -tools/javac/processing/6994946/SyntaxErrorTest.java generic-all -tools/javac/processing/T6439826.java generic-all -tools/javac/processing/T6920317.java generic-all -tools/javac/processing/T7196462.java generic-all tools/javac/processing/TestWarnErrorCount.java generic-all -tools/javac/processing/environment/TestSourceVersion.java generic-all -tools/javac/processing/environment/round/TestContext.java generic-all -tools/javac/processing/errors/CrashOnNonExistingAnnotation/Source.java generic-all -tools/javac/processing/errors/EnsureAnnotationTypeMismatchException/Source.java generic-all -tools/javac/processing/errors/EnsureMirroredTypeException/Source.java generic-all -tools/javac/processing/errors/StopOnInapplicableAnnotations/Source.java generic-all -tools/javac/processing/errors/TestClassNames.java generic-all -tools/javac/processing/errors/TestErrorCount.java generic-all -tools/javac/processing/errors/TestFatalityOfParseErrors.java generic-all -tools/javac/processing/errors/TestOptionSyntaxErrors.java generic-all -tools/javac/processing/errors/TestReturnCode.java generic-all -tools/javac/processing/errors/TestSuppression.java generic-all -tools/javac/processing/filer/TestFilerConstraints.java generic-all -tools/javac/processing/filer/TestGetResource.java generic-all -tools/javac/processing/filer/TestGetResource2.java generic-all -tools/javac/processing/filer/TestInvalidRelativeNames.java generic-all -tools/javac/processing/filer/TestPackageInfo.java generic-all -tools/javac/processing/filer/TestValidRelativeNames.java generic-all -tools/javac/processing/loader/testClose/TestClose.java generic-all -tools/javac/processing/loader/testClose/TestClose2.java generic-all -tools/javac/processing/messager/6362067/T6362067.java generic-all -tools/javac/processing/messager/MessagerBasics.java generic-all -tools/javac/processing/messager/MessagerDiags.java generic-all -tools/javac/processing/model/6194785/T6194785.java generic-all -tools/javac/processing/model/6341534/T6341534.java generic-all -tools/javac/processing/model/TestSymtabItems.java generic-all -tools/javac/processing/model/element/TestAnonClassNames.java generic-all -tools/javac/processing/model/element/TestElement.java generic-all -tools/javac/processing/model/element/TestExecutableElement.java generic-all -tools/javac/processing/model/element/TestMissingElement/TestMissingElement.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingClass.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingGenericClass1.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingGenericClass2.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingGenericInterface1.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingGenericInterface2.java generic-all -tools/javac/processing/model/element/TestMissingElement2/TestMissingInterface.java generic-all -tools/javac/processing/model/element/TestNames.java generic-all -tools/javac/processing/model/element/TestPackageElement.java generic-all -tools/javac/processing/model/element/TestResourceElement.java generic-all -tools/javac/processing/model/element/TestResourceVariable.java generic-all -tools/javac/processing/model/element/TestTypeElement.java generic-all -tools/javac/processing/model/element/TestTypeParameter.java generic-all -tools/javac/processing/model/element/TestTypeParameterAnnotations.java generic-all -tools/javac/processing/model/inheritedByType/EnsureOrder.java generic-all tools/javac/processing/model/testgetallmembers/Main.java generic-all -tools/javac/processing/model/type/BoundsTest.java generic-all -tools/javac/processing/model/type/IntersectionPropertiesTest.java generic-all tools/javac/processing/model/type/MirroredTypeEx/OverEager.java generic-all -tools/javac/processing/model/type/TestUnionType.java generic-all -tools/javac/processing/model/util/deprecation/TestDeprecation.java generic-all -tools/javac/processing/model/util/elements/TestGetConstantExpression.java generic-all -tools/javac/processing/model/util/elements/TestGetPackageOf.java generic-all -tools/javac/processing/model/util/elements/TestIsFunctionalInterface.java generic-all -tools/javac/processing/model/util/elements/doccomments/TestDocComments.java generic-all -tools/javac/processing/model/util/filter/TestIterables.java generic-all -tools/javac/processing/options/testCommandLineClasses/Test.java generic-all -tools/javac/processing/options/testPrintProcessorInfo/Test.java generic-all -tools/javac/processing/options/testPrintProcessorInfo/TestWithXstdout.java generic-all -tools/javac/processing/warnings/TestSourceVersionWarnings.java generic-all -tools/javac/processing/warnings/UseImplicit/TestProcUseImplicitWarning.java generic-all -tools/javac/processing/werror/WError1.java generic-all -tools/javac/processing/werror/WErrorLast.java generic-all tools/javac/profiles/ProfileOptionTest.java generic-all tools/javac/proprietary/WarnClass.java generic-all tools/javac/proprietary/WarnImport.java generic-all @@ -801,119 +79,27 @@ tools/javac/proprietary/WarnMethod.java tools/javac/proprietary/WarnStaticImport.java generic-all tools/javac/proprietary/WarnVariable.java generic-all tools/javac/proprietary/WarnWildcard.java generic-all -tools/javac/resolve/ResolveHarness.java generic-all -tools/javac/scope/6225935/StaticImportAccess.java generic-all -tools/javac/scope/6225935/T6381787.java generic-all -tools/javac/scope/7017664/CompoundScopeTest.java generic-all -tools/javac/scope/7017664/ImplementationCacheTest.java generic-all -tools/javac/scope/7046348/EagerInterfaceCompletionTest.java generic-all -tools/javac/scope/HashCollisionTest.java generic-all -tools/javac/scope/StarImportTest.java generic-all -tools/javac/stackmap/StackMapTest.java generic-all -tools/javac/synthesize/Main.java generic-all -tools/javac/tree/ClassTreeTest.java generic-all -tools/javac/tree/DocCommentToplevelTest.java generic-all -tools/javac/tree/JavacTreeScannerTest.java generic-all -tools/javac/tree/MakeLiteralTest.java generic-all -tools/javac/tree/MakeQualIdent.java generic-all -tools/javac/tree/NewArrayPretty.java generic-all -tools/javac/tree/NoPrivateTypesExported.java generic-all -tools/javac/tree/PrettySimpleStringTest.java generic-all -tools/javac/tree/ScopeTest.java generic-all -tools/javac/tree/SourceTreeScannerTest.java generic-all -tools/javac/tree/T6963934.java generic-all -tools/javac/tree/T6993305.java generic-all -tools/javac/tree/T8024415.java generic-all -tools/javac/tree/TestToString.java generic-all -tools/javac/tree/TreeKindTest.java generic-all -tools/javac/tree/TreePosRoundsTest.java generic-all -tools/javac/tree/TreePosTest.java generic-all -tools/javac/tree/TypeAnnotationsPretty.java generic-all -tools/javac/treeannotests/AnnoTreeTests.java generic-all -tools/javac/types/BoxingConversionTest.java generic-all -tools/javac/types/CastTest.java generic-all -tools/javac/types/GenericTypeWellFormednessTest.java generic-all -tools/javac/types/PrimitiveConversionTest.java generic-all +tools/javac/tree/JavacTreeScannerTest.java linux-all +tools/javac/tree/SourceTreeScannerTest.java linux-all +tools/javac/tree/TreePosTest.java linux-all tools/javac/types/TestComparisons.java generic-all -tools/javac/unit/T6198196.java generic-all -tools/javac/util/T6597678.java generic-all -tools/javac/util/context/T7021650.java generic-all -tools/javac/util/list/TList.java generic-all tools/javac/varargs/6199075/T6199075.java generic-all tools/javac/varargs/7042566/T7042566.java generic-all tools/javac/varargs/7043922/T7043922.java generic-all -tools/javac/varargs/MethodHandleCrash.java generic-all tools/javac/varargs/warning/Warn4.java generic-all tools/javac/varargs/warning/Warn5.java generic-all tools/javac/versions/check.sh generic-all tools/javac/warnings/6594914/T6594914b.java generic-all -tools/javac/warnings/suppress/Overridden.java generic-all -tools/javac/warnings/suppress/T6480588.java generic-all -tools/javac/warnings/suppress/T8021112b.java generic-all -tools/javac/warnings/suppress/TypeAnnotations.java generic-all -tools/javadoc/6176978/T6176978.java generic-all tools/javadoc/6942366/T6942366.java generic-all tools/javadoc/6964914/TestStdDoclet.java generic-all tools/javadoc/6964914/TestUserDoclet.java generic-all tools/javadoc/CheckResourceKeys.java generic-all -tools/javadoc/annotations/annotateMethodsFields/Main.java generic-all -tools/javadoc/annotations/annotatePackage/Main.java generic-all -tools/javadoc/annotations/annotateParams/Main.java generic-all -tools/javadoc/annotations/badVals/Main.java generic-all -tools/javadoc/annotations/defaults/Main.java generic-all -tools/javadoc/annotations/elementTypes/Main.java generic-all -tools/javadoc/annotations/missing/Main.java generic-all -tools/javadoc/annotations/shortcuts/Main.java generic-all -tools/javadoc/api/basic/DocletPathTest.java generic-all -tools/javadoc/api/basic/DocumentationToolLocationTest.java generic-all -tools/javadoc/api/basic/GetSourceVersionsTest.java generic-all -tools/javadoc/api/basic/GetTask_DiagListenerTest.java generic-all -tools/javadoc/api/basic/GetTask_DocletClassTest.java generic-all tools/javadoc/api/basic/GetTask_FileManagerTest.java generic-all -tools/javadoc/api/basic/GetTask_FileObjectsTest.java generic-all -tools/javadoc/api/basic/GetTask_OptionsTest.java generic-all -tools/javadoc/api/basic/GetTask_WriterTest.java generic-all -tools/javadoc/api/basic/IsSupportedOptionTest.java generic-all -tools/javadoc/api/basic/JavadocTaskImplTest.java generic-all -tools/javadoc/api/basic/RunTest.java generic-all -tools/javadoc/api/basic/TagletPathTest.java generic-all -tools/javadoc/api/basic/Task_reuseTest.java generic-all -tools/javadoc/doclint/DocLintTest.java generic-all -tools/javadoc/enum/docComments/Main.java generic-all -tools/javadoc/enum/enumType/Main.java generic-all -tools/javadoc/generics/genericClass/Main.java generic-all -tools/javadoc/generics/genericInnerAndOuter/Main.java generic-all -tools/javadoc/generics/genericInterface/Main.java generic-all -tools/javadoc/generics/genericMethod/Main.java generic-all -tools/javadoc/generics/genericSuper/Main.java generic-all -tools/javadoc/generics/supertypes/Main.java generic-all -tools/javadoc/generics/throwsGeneric/Main.java generic-all -tools/javadoc/generics/tparamCycle/Main.java generic-all -tools/javadoc/generics/tparamTagOnMethod/Main.java generic-all -tools/javadoc/generics/tparamTagOnType/Main.java generic-all -tools/javadoc/generics/wildcards/Main.java generic-all -tools/javadoc/varArgs/Main.java generic-all -tools/javah/6257087/T6257087.java generic-all tools/javah/ReadOldClass.sh generic-all -tools/javah/T4942232/MissingParamClassTest.java generic-all -tools/javah/T5070898.java generic-all tools/javah/T6893943.java generic-all tools/javah/T6994608.java generic-all -tools/javah/constMacroTest/ConstMacroTest.java generic-all -tools/javap/4798312/JavapShouldLoadClassesFromRTJarTest.java generic-all -tools/javap/4866831/PublicInterfaceTest.java generic-all -tools/javap/8006334/JavapTaskCtorFailWithNPE.java generic-all -tools/javap/T4777949.java generic-all tools/javap/T6729471.java generic-all tools/javap/T6863746.java generic-all -tools/javap/T6866657.java generic-all -tools/javap/T7186925.java generic-all -tools/javap/T7190862.java generic-all -tools/javap/TestSuperclass.java generic-all -tools/javap/classfile/6888367/T6888367.java generic-all -tools/javap/classfile/T6887895.java generic-all -tools/javap/classfile/deps/T6907575.java generic-all -tools/javap/stackmap/StackmapTest.java generic-all tools/jdeps/APIDeps.java generic-all tools/jdeps/Basic.java generic-all tools/jdeps/DotFileTest.java generic-all diff --git a/src/IKVM.OpenJDK.Tests/nashorn/ExcludeList.txt b/src/IKVM.OpenJDK.Tests/nashorn/ExcludeList.txt index 36d016998c..e69de29bb2 100644 --- a/src/IKVM.OpenJDK.Tests/nashorn/ExcludeList.txt +++ b/src/IKVM.OpenJDK.Tests/nashorn/ExcludeList.txt @@ -1,13 +0,0 @@ -src/jdk/nashorn/api/javaaccess/BooleanAccessTest.java generic-all -src/jdk/nashorn/api/javaaccess/MethodAccessTest.java generic-all -src/jdk/nashorn/api/javaaccess/NumberAccessTest.java generic-all -src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java generic-all -src/jdk/nashorn/api/javaaccess/ObjectAccessTest.java generic-all -src/jdk/nashorn/api/javaaccess/StringAccessTest.java generic-all -src/jdk/nashorn/api/scripting/MultipleEngineTest.java generic-all -src/jdk/nashorn/api/scripting/ScriptEngineTest.java generic-all -src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java generic-all -src/jdk/nashorn/internal/runtime/ContextTest.java generic-all -src/jdk/nashorn/internal/runtime/ExceptionsNotSerializable.java generic-all -src/jdk/nashorn/internal/runtime/LexicalBindingTest.java generic-all -src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java generic-all diff --git a/src/IKVM.Reflection/Assembly.cs b/src/IKVM.Reflection/Assembly.cs index 184ec481ec..4617c9bdea 100644 --- a/src/IKVM.Reflection/Assembly.cs +++ b/src/IKVM.Reflection/Assembly.cs @@ -161,30 +161,22 @@ public Type GetType(string name, bool throwOnError) public Type GetType(string name, bool throwOnError, bool ignoreCase) { - TypeNameParser parser = TypeNameParser.Parse(name, throwOnError); + var parser = TypeNameParser.Parse(name, throwOnError); if (parser.Error) - { return null; - } + if (parser.AssemblyName != null) { if (throwOnError) - { throw new ArgumentException("Type names passed to Assembly.GetType() must not specify an assembly."); - } else - { return null; - } } - TypeName typeName = TypeName.Split(TypeNameParser.Unescape(parser.FirstNamePart)); - Type type = ignoreCase - ? FindTypeIgnoreCase(typeName.ToLowerInvariant()) - : FindType(typeName); + var typeName = TypeName.Split(TypeNameParser.Unescape(parser.FirstNamePart)); + var type = ignoreCase ? FindTypeIgnoreCase(typeName.ToLowerInvariant()) : FindType(typeName); if (type == null && __IsMissing) - { throw new MissingAssemblyException((MissingAssembly)this); - } + return parser.Expand(type, this.ManifestModule, throwOnError, name, false, ignoreCase); } @@ -241,20 +233,11 @@ public string CodeBase } } - public virtual bool IsDynamic - { - get { return false; } - } + public virtual bool IsDynamic => false; - public virtual bool __IsMissing - { - get { return false; } - } + public virtual bool __IsMissing => false; - public AssemblyNameFlags __AssemblyFlags - { - get { return GetAssemblyFlags(); } - } + public AssemblyNameFlags __AssemblyFlags => GetAssemblyFlags(); protected virtual AssemblyNameFlags GetAssemblyFlags() { @@ -262,5 +245,7 @@ protected virtual AssemblyNameFlags GetAssemblyFlags() } internal abstract IList GetCustomAttributesData(Type attributeType); + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Attribute.cs b/src/IKVM.Reflection/AssemblyComparisonResult.cs similarity index 63% rename from src/ikvmc/IKVM/Internal/MapXml/Attribute.cs rename to src/IKVM.Reflection/AssemblyComparisonResult.cs index 5298839b49..776f1f2a9a 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Attribute.cs +++ b/src/IKVM.Reflection/AssemblyComparisonResult.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2009-2013 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,23 +22,21 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml +namespace IKVM.Reflection { - public sealed class Attribute + public enum AssemblyComparisonResult { - [XmlAttribute("type")] - public string Type; - [XmlAttribute("class")] - public string Class; - [XmlAttribute("sig")] - public string Sig; - [XmlElement("parameter")] - public Param[] Params; - [XmlElement("property")] - public Param[] Properties; - [XmlElement("field")] - public Param[] Fields; + Unknown = 0, + EquivalentFullMatch = 1, + EquivalentWeakNamed = 2, + EquivalentFXUnified = 3, + EquivalentUnified = 4, + NonEquivalentVersion = 5, + NonEquivalent = 6, + EquivalentPartialMatch = 7, + EquivalentPartialWeakNamed = 8, + EquivalentPartialUnified = 9, + EquivalentPartialFXUnified = 10, + NonEquivalentPartialVersion = 11, } } diff --git a/src/IKVM.Reflection/AssemblyName.cs b/src/IKVM.Reflection/AssemblyName.cs index c7036f89f3..a7c6b637fc 100644 --- a/src/IKVM.Reflection/AssemblyName.cs +++ b/src/IKVM.Reflection/AssemblyName.cs @@ -23,509 +23,476 @@ Jeroen Frijters */ using System; using System.Globalization; -using System.Configuration.Assemblies; using System.IO; using System.Security.Cryptography; using System.Text; + using IKVM.Reflection.Reader; namespace IKVM.Reflection { - public sealed class AssemblyName - : ICloneable - { - private string name; - private string culture; - private Version version; - private byte[] publicKeyToken; - private byte[] publicKey; - private StrongNameKeyPair keyPair; - private AssemblyNameFlags flags; - private AssemblyHashAlgorithm hashAlgorithm; - private AssemblyVersionCompatibility versionCompatibility = AssemblyVersionCompatibility.SameMachine; - private string codeBase; - internal byte[] hash; - - public AssemblyName() - { - } - - public AssemblyName(string assemblyName) - { - if (assemblyName == null) - { - throw new ArgumentNullException("assemblyName"); - } - if (assemblyName == "") - { - throw new ArgumentException(); - } - ParsedAssemblyName parsed; - switch (Fusion.ParseAssemblyName(assemblyName, out parsed)) - { - case ParseAssemblyResult.GenericError: - case ParseAssemblyResult.DuplicateKey: - throw new FileLoadException(); - } - if (!ParseVersion(parsed.Version, parsed.Retargetable.HasValue, out version)) - { - throw new FileLoadException(); - } - name = parsed.Name; - if (parsed.Culture != null) - { - if (parsed.Culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) - { - culture = ""; - } - else if (parsed.Culture == "") - { - throw new FileLoadException(); - } - else - { - culture = new CultureInfo(parsed.Culture).Name; - } - } - if (parsed.PublicKeyToken != null) - { - if (parsed.PublicKeyToken.Equals("null", StringComparison.OrdinalIgnoreCase)) - { - publicKeyToken = Empty.Array; - } - else if (parsed.PublicKeyToken.Length != 16) - { - throw new FileLoadException(); - } - else - { - publicKeyToken = ParseKey(parsed.PublicKeyToken); - } - } - if (parsed.Retargetable.HasValue) - { - if (parsed.Culture == null || parsed.PublicKeyToken == null || version == null) - { - throw new FileLoadException(); - } - if (parsed.Retargetable.Value) - { - flags |= AssemblyNameFlags.Retargetable; - } - } - ProcessorArchitecture = parsed.ProcessorArchitecture; - if (parsed.WindowsRuntime) - { - ContentType = AssemblyContentType.WindowsRuntime; - } - } - - private static byte[] ParseKey(string key) - { - if ((key.Length & 1) != 0) - { - throw new FileLoadException(); - } - byte[] buf = new byte[key.Length / 2]; - for (int i = 0; i < buf.Length; i++) - { - buf[i] = (byte)(ParseHexDigit(key[i * 2]) * 16 + ParseHexDigit(key[i * 2 + 1])); - } - return buf; - } - - private static int ParseHexDigit(char digit) - { - if (digit >= '0' && digit <= '9') - { - return digit - '0'; - } - else - { - digit |= (char)0x20; - if (digit >= 'a' && digit <= 'f') - { - return 10 + digit - 'a'; - } - else - { - throw new FileLoadException(); - } - } - } - - public override string ToString() - { - return FullName; - } - - public string Name - { - get { return name; } - set { name = value; } - } - - public CultureInfo CultureInfo - { - get { return culture == null ? null : new CultureInfo(culture); } - set { culture = value == null ? null : value.Name; } - } - - public string CultureName - { - get { return culture; } - set { culture = value; } - } - - public Version Version - { - get { return version; } - set { version = value; } - } - - public StrongNameKeyPair KeyPair - { - get { return keyPair; } - set { keyPair = value; } - } - - public string CodeBase - { - get { return codeBase; } - set { codeBase = value; } - } - - public string EscapedCodeBase - { - get - { - // HACK use the real AssemblyName to escape the codebase - System.Reflection.AssemblyName tmp = new System.Reflection.AssemblyName(); - tmp.CodeBase = codeBase; - return tmp.EscapedCodeBase; - } - } - - public ProcessorArchitecture ProcessorArchitecture - { - get { return (ProcessorArchitecture)(((int)flags & 0x70) >> 4); } - set - { - if (value >= ProcessorArchitecture.None && value <= ProcessorArchitecture.Arm64) - { - flags = (flags & ~(AssemblyNameFlags)0x70) | (AssemblyNameFlags)((int)value << 4); - } - } - } - - public AssemblyNameFlags Flags - { - get { return flags & (AssemblyNameFlags)~0xEF0; } - set { flags = (flags & (AssemblyNameFlags)0xEF0) | (value & (AssemblyNameFlags)~0xEF0); } - } - - public AssemblyVersionCompatibility VersionCompatibility - { - get { return versionCompatibility; } - set { versionCompatibility = value; } - } - - public AssemblyContentType ContentType - { - get { return (AssemblyContentType)(((int)flags & 0xE00) >> 9); } - set - { - if (value >= AssemblyContentType.Default && value <= AssemblyContentType.WindowsRuntime) - { - flags = (flags & ~(AssemblyNameFlags)0xE00) | (AssemblyNameFlags)((int)value << 9); - } - } - } - - public byte[] GetPublicKey() - { - return publicKey; - } - - public void SetPublicKey(byte[] publicKey) - { - this.publicKey = publicKey; - flags = (flags & ~AssemblyNameFlags.PublicKey) | (publicKey == null ? 0 : AssemblyNameFlags.PublicKey); - } - - public byte[] GetPublicKeyToken() - { - if (publicKeyToken == null && publicKey != null) - { - // note that GetPublicKeyToken() has a side effect in this case, because we retain this token even after the public key subsequently gets changed - publicKeyToken = ComputePublicKeyToken(publicKey); - } - return publicKeyToken; - } - - public void SetPublicKeyToken(byte[] publicKeyToken) - { - this.publicKeyToken = publicKeyToken; - } - - public AssemblyHashAlgorithm HashAlgorithm - { - get { return hashAlgorithm; } - set { hashAlgorithm = value; } - } - - public byte[] __Hash - { - get { return hash; } - } - - public string FullName - { - get - { - if (name == null) - { - return ""; - } - ushort versionMajor = 0xFFFF; - ushort versionMinor = 0xFFFF; - ushort versionBuild = 0xFFFF; - ushort versionRevision = 0xFFFF; - if (version != null) - { - versionMajor = (ushort)version.Major; - versionMinor = (ushort)version.Minor; - versionBuild = (ushort)version.Build; - versionRevision = (ushort)version.Revision; - } - byte[] publicKeyToken = this.publicKeyToken; - if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null) - { - publicKeyToken = ComputePublicKeyToken(publicKey); - } - return GetFullName(name, versionMajor, versionMinor, versionBuild, versionRevision, culture, publicKeyToken, (int)flags); - } - } - - internal static string GetFullName(string name, ushort versionMajor, ushort versionMinor, ushort versionBuild, ushort versionRevision, string culture, byte[] publicKeyToken, int flags) - { - StringBuilder sb = new StringBuilder(); - bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1; - bool singleQuotes = name.IndexOf('"') != -1; - if (singleQuotes) - { - sb.Append('\''); - } - else if (doubleQuotes) - { - sb.Append('"'); - } - if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1)) - { - for (int i = 0; i < name.Length; i++) - { - char c = name[i]; - if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\'')) - { - sb.Append('\\'); - } - sb.Append(c); - } - } - else - { - sb.Append(name); - } - if (singleQuotes) - { - sb.Append('\''); - } - else if (doubleQuotes) - { - sb.Append('"'); - } - if (versionMajor != 0xFFFF) - { - sb.Append(", Version=").Append(versionMajor); - if (versionMinor != 0xFFFF) - { - sb.Append('.').Append(versionMinor); - if (versionBuild != 0xFFFF) - { - sb.Append('.').Append(versionBuild); - if (versionRevision != 0xFFFF) - { - sb.Append('.').Append(versionRevision); - } - } - } - } - if (culture != null) - { - sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture); - } - if (publicKeyToken != null) - { - sb.Append(", PublicKeyToken="); - if (publicKeyToken.Length == 0) - { - sb.Append("null"); - } - else - { - AppendPublicKey(sb, publicKeyToken); - } - } - if ((flags & (int)AssemblyNameFlags.Retargetable) != 0) - { - sb.Append(", Retargetable=Yes"); - } - if ((AssemblyContentType)((flags & 0xE00) >> 9) == AssemblyContentType.WindowsRuntime) - { - sb.Append(", ContentType=WindowsRuntime"); - } - return sb.ToString(); - } - - internal static byte[] ComputePublicKeyToken(byte[] publicKey) - { - if (publicKey.Length == 0) - { - return publicKey; - } - byte[] hash; - using (SHA1 sha1 = SHA1.Create()) - { - hash = sha1.ComputeHash(publicKey); - } - byte[] token = new byte[8]; - for (int i = 0; i < token.Length; i++) - { - token[i] = hash[hash.Length - 1 - i]; - } - return token; - } - - internal static string ComputePublicKeyToken(string publicKey) - { - StringBuilder sb = new StringBuilder(16); - AppendPublicKey(sb, ComputePublicKeyToken(ParseKey(publicKey))); - return sb.ToString(); - } - - private static void AppendPublicKey(StringBuilder sb, byte[] publicKey) - { - for (int i = 0; i < publicKey.Length; i++) - { - sb.Append("0123456789abcdef"[publicKey[i] >> 4]); - sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]); - } - } - - public override bool Equals(object obj) - { - AssemblyName other = obj as AssemblyName; - return other != null && other.FullName == this.FullName; - } - - public override int GetHashCode() - { - return FullName.GetHashCode(); - } - - public object Clone() - { - AssemblyName copy = (AssemblyName)MemberwiseClone(); - copy.publicKey = Copy(publicKey); - copy.publicKeyToken = Copy(publicKeyToken); - return copy; - } - - private static byte[] Copy(byte[] b) - { - return b == null || b.Length == 0 ? b : (byte[])b.Clone(); - } - - public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition) - { - // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method - return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName)); - } - - public static AssemblyName GetAssemblyName(string path) - { - try - { - path = Path.GetFullPath(path); - using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - ModuleReader module = new ModuleReader(null, null, fs, path, false); - if (module.Assembly == null) - { - throw new BadImageFormatException("Module does not contain a manifest"); - } - return module.Assembly.GetName(); - } - } - catch (IOException x) - { - throw new FileNotFoundException(x.Message, x); - } - catch (UnauthorizedAccessException x) - { - throw new FileNotFoundException(x.Message, x); - } - } - - internal AssemblyNameFlags RawFlags - { - get { return flags; } - set { flags = value; } - } - - private static bool ParseVersion(string str, bool mustBeComplete, out Version version) - { - if (str == null) - { - version = null; - return true; - } - string[] parts = str.Split('.'); - if (parts.Length < 2 || parts.Length > 4) - { - version = null; - ushort dummy; - // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name - return parts.Length == 1 && ushort.TryParse(parts[0], NumberStyles.Integer, null, out dummy); - } - if (parts[0] == "" || parts[1] == "") - { - // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name - version = null; - return true; - } - ushort major, minor, build = 65535, revision = 65535; - if (ushort.TryParse(parts[0], NumberStyles.Integer, null, out major) - && ushort.TryParse(parts[1], NumberStyles.Integer, null, out minor) - && (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], NumberStyles.Integer, null, out build)) - && (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], NumberStyles.Integer, null, out revision)))) - { - if (mustBeComplete && (parts.Length < 4 || parts[2] == "" || parts[3] == "")) - { - version = null; - } - else if (major == 65535 || minor == 65535) - { - version = null; - } - else - { - version = new Version(major, minor, build, revision); - } - return true; - } - version = null; - return false; - } - } + public sealed class AssemblyName + : ICloneable + { + private string name; + private string culture; + private Version version; + private byte[] publicKeyToken; + private byte[] publicKey; + private StrongNameKeyPair keyPair; + private AssemblyNameFlags flags; + private AssemblyHashAlgorithm hashAlgorithm; + private AssemblyVersionCompatibility versionCompatibility = AssemblyVersionCompatibility.SameMachine; + private string codeBase; + internal byte[] hash; + + /// + /// Initializes a new instance. + /// + public AssemblyName() + { + + } + + public AssemblyName(string assemblyName) + { + if (assemblyName == null) + throw new ArgumentNullException("assemblyName"); + if (assemblyName == "") + throw new ArgumentException(); + + switch (Fusion.ParseAssemblyName(assemblyName, out var parsed)) + { + case ParseAssemblyResult.GenericError: + case ParseAssemblyResult.DuplicateKey: + throw new FileLoadException(); + } + + if (!ParseVersion(parsed.Version, parsed.Retargetable.HasValue, out version)) + throw new FileLoadException(); + + name = parsed.Name; + if (parsed.Culture != null) + { + if (parsed.Culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) + culture = ""; + else if (parsed.Culture == "") + throw new FileLoadException(); + else + culture = new CultureInfo(parsed.Culture).Name; + } + + if (parsed.PublicKeyToken != null) + { + if (parsed.PublicKeyToken.Equals("null", StringComparison.OrdinalIgnoreCase)) + publicKeyToken = Empty.Array; + else if (parsed.PublicKeyToken.Length != 16) + throw new FileLoadException(); + else + publicKeyToken = ParseKey(parsed.PublicKeyToken); + } + + if (parsed.Retargetable.HasValue) + { + if (parsed.Culture == null || parsed.PublicKeyToken == null || version == null) + throw new FileLoadException(); + if (parsed.Retargetable.Value) + flags |= AssemblyNameFlags.Retargetable; + } + + ProcessorArchitecture = parsed.ProcessorArchitecture; + if (parsed.WindowsRuntime) + ContentType = AssemblyContentType.WindowsRuntime; + } + + static byte[] ParseKey(string key) + { + if ((key.Length & 1) != 0) + throw new FileLoadException(); + + var buf = new byte[key.Length / 2]; + for (int i = 0; i < buf.Length; i++) + buf[i] = (byte)(ParseHexDigit(key[i * 2]) * 16 + ParseHexDigit(key[i * 2 + 1])); + + return buf; + } + + static int ParseHexDigit(char digit) + { + if (digit >= '0' && digit <= '9') + { + return digit - '0'; + } + else + { + digit |= (char)0x20; + if (digit >= 'a' && digit <= 'f') + { + return 10 + digit - 'a'; + } + else + { + throw new FileLoadException(); + } + } + } + + public override string ToString() + { + return FullName; + } + + public string Name + { + get => name; + set => name = value; + } + + public CultureInfo CultureInfo + { + get => culture == null ? null : new CultureInfo(culture); + set => culture = value?.Name; + } + + public string CultureName + { + get => culture; + set => culture = value; + } + + public Version Version + { + get => version; + set => version = value; + } + + public StrongNameKeyPair KeyPair + { + get => keyPair; + set => keyPair = value; + } + + public string CodeBase + { + get => codeBase; + set => codeBase = value; + } + + public string EscapedCodeBase + { + get + { + // HACK use the real AssemblyName to escape the codebase + System.Reflection.AssemblyName tmp = new System.Reflection.AssemblyName(); + tmp.CodeBase = codeBase; + return tmp.EscapedCodeBase; + } + } + + public ProcessorArchitecture ProcessorArchitecture + { + get => (ProcessorArchitecture)(((int)flags & 0x70) >> 4); + set + { + if (value >= ProcessorArchitecture.None && value <= ProcessorArchitecture.Arm64) + flags = (flags & ~(AssemblyNameFlags)0x70) | (AssemblyNameFlags)((int)value << 4); + } + } + + public AssemblyNameFlags Flags + { + get => flags & (AssemblyNameFlags)~0xEF0; + set => flags = (flags & (AssemblyNameFlags)0xEF0) | (value & (AssemblyNameFlags)~0xEF0); + } + + public AssemblyVersionCompatibility VersionCompatibility + { + get => versionCompatibility; + set => versionCompatibility = value; + } + + public AssemblyContentType ContentType + { + get => (AssemblyContentType)(((int)flags & 0xE00) >> 9); + set + { + if (value >= AssemblyContentType.Default && value <= AssemblyContentType.WindowsRuntime) + { + flags = (flags & ~(AssemblyNameFlags)0xE00) | (AssemblyNameFlags)((int)value << 9); + } + } + } + + public byte[] GetPublicKey() + { + return publicKey; + } + + public void SetPublicKey(byte[] publicKey) + { + this.publicKey = publicKey; + flags = (flags & ~AssemblyNameFlags.PublicKey) | (publicKey == null ? 0 : AssemblyNameFlags.PublicKey); + } + + public byte[] GetPublicKeyToken() + { + if (publicKeyToken == null && publicKey != null) + { + // note that GetPublicKeyToken() has a side effect in this case, because we retain this token even after the public key subsequently gets changed + publicKeyToken = ComputePublicKeyToken(publicKey); + } + + return publicKeyToken; + } + + public void SetPublicKeyToken(byte[] publicKeyToken) + { + this.publicKeyToken = publicKeyToken; + } + + public AssemblyHashAlgorithm HashAlgorithm + { + get => hashAlgorithm; + set => hashAlgorithm = value; + } + + public byte[] __Hash => hash; + + public string FullName + { + get + { + if (name == null) + return ""; + + ushort versionMajor = 0xFFFF; + ushort versionMinor = 0xFFFF; + ushort versionBuild = 0xFFFF; + ushort versionRevision = 0xFFFF; + if (version != null) + { + versionMajor = (ushort)version.Major; + versionMinor = (ushort)version.Minor; + versionBuild = (ushort)version.Build; + versionRevision = (ushort)version.Revision; + } + + var publicKeyToken = this.publicKeyToken; + if ((publicKeyToken == null || publicKeyToken.Length == 0) && publicKey != null) + publicKeyToken = ComputePublicKeyToken(publicKey); + + return GetFullName(name, versionMajor, versionMinor, versionBuild, versionRevision, culture, publicKeyToken, (int)flags); + } + } + + internal static string GetFullName(string name, ushort versionMajor, ushort versionMinor, ushort versionBuild, ushort versionRevision, string culture, byte[] publicKeyToken, int flags) + { + var sb = new StringBuilder(); + bool doubleQuotes = name.StartsWith(" ") || name.EndsWith(" ") || name.IndexOf('\'') != -1; + bool singleQuotes = name.IndexOf('"') != -1; + if (singleQuotes) + sb.Append('\''); + else if (doubleQuotes) + sb.Append('"'); + + if (name.IndexOf(',') != -1 || name.IndexOf('\\') != -1 || name.IndexOf('=') != -1 || (singleQuotes && name.IndexOf('\'') != -1)) + { + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + if (c == ',' || c == '\\' || c == '=' || (singleQuotes && c == '\'')) + { + sb.Append('\\'); + } + sb.Append(c); + } + } + else + { + sb.Append(name); + } + if (singleQuotes) + { + sb.Append('\''); + } + else if (doubleQuotes) + { + sb.Append('"'); + } + if (versionMajor != 0xFFFF) + { + sb.Append(", Version=").Append(versionMajor); + if (versionMinor != 0xFFFF) + { + sb.Append('.').Append(versionMinor); + if (versionBuild != 0xFFFF) + { + sb.Append('.').Append(versionBuild); + if (versionRevision != 0xFFFF) + { + sb.Append('.').Append(versionRevision); + } + } + } + } + if (culture != null) + { + sb.Append(", Culture=").Append(culture == "" ? "neutral" : culture); + } + if (publicKeyToken != null) + { + sb.Append(", PublicKeyToken="); + if (publicKeyToken.Length == 0) + { + sb.Append("null"); + } + else + { + AppendPublicKey(sb, publicKeyToken); + } + } + if ((flags & (int)AssemblyNameFlags.Retargetable) != 0) + { + sb.Append(", Retargetable=Yes"); + } + if ((AssemblyContentType)((flags & 0xE00) >> 9) == AssemblyContentType.WindowsRuntime) + { + sb.Append(", ContentType=WindowsRuntime"); + } + + return sb.ToString(); + } + + internal static byte[] ComputePublicKeyToken(byte[] publicKey) + { + if (publicKey.Length == 0) + return publicKey; + + byte[] hash; + using (SHA1 sha1 = SHA1.Create()) + hash = sha1.ComputeHash(publicKey); + + var token = new byte[8]; + for (int i = 0; i < token.Length; i++) + token[i] = hash[hash.Length - 1 - i]; + + return token; + } + + internal static string ComputePublicKeyToken(string publicKey) + { + var sb = new StringBuilder(16); + AppendPublicKey(sb, ComputePublicKeyToken(ParseKey(publicKey))); + return sb.ToString(); + } + + private static void AppendPublicKey(StringBuilder sb, byte[] publicKey) + { + for (int i = 0; i < publicKey.Length; i++) + { + sb.Append("0123456789abcdef"[publicKey[i] >> 4]); + sb.Append("0123456789abcdef"[publicKey[i] & 0x0F]); + } + } + + public override bool Equals(object obj) + { + return obj is AssemblyName other && other.FullName == FullName; + } + + public override int GetHashCode() + { + return FullName.GetHashCode(); + } + + public object Clone() + { + var copy = (AssemblyName)MemberwiseClone(); + copy.publicKey = Copy(publicKey); + copy.publicKeyToken = Copy(publicKeyToken); + return copy; + } + + private static byte[] Copy(byte[] b) + { + return b == null || b.Length == 0 ? b : (byte[])b.Clone(); + } + + public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition) + { + // HACK use the real AssemblyName to implement the (broken) ReferenceMatchesDefinition method + return System.Reflection.AssemblyName.ReferenceMatchesDefinition(new System.Reflection.AssemblyName(reference.FullName), new System.Reflection.AssemblyName(definition.FullName)); + } + + public static AssemblyName GetAssemblyName(string path) + { + try + { + path = Path.GetFullPath(path); + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + var module = new ModuleReader(null, null, fs, path, false); + if (module.Assembly == null) + throw new BadImageFormatException("Module does not contain a manifest"); + + return module.Assembly.GetName(); + } + } + catch (IOException x) + { + throw new FileNotFoundException(x.Message, x); + } + catch (UnauthorizedAccessException x) + { + throw new FileNotFoundException(x.Message, x); + } + } + + internal AssemblyNameFlags RawFlags + { + get => flags; + set => flags = value; + } + + static bool ParseVersion(string str, bool mustBeComplete, out Version version) + { + version = null; + + if (str == null) + return true; + + var parts = str.Split('.'); + if (parts.Length < 2 || parts.Length > 4) + { + // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name + return parts.Length == 1 && ushort.TryParse(parts[0], NumberStyles.Integer, null, out _); + } + if (parts[0] == "" || parts[1] == "") + { + // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name + return true; + } + + ushort build = 65535, revision = 65535; + if (ushort.TryParse(parts[0], NumberStyles.Integer, null, out ushort major) && + ushort.TryParse(parts[1], NumberStyles.Integer, null, out ushort minor) && + (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], NumberStyles.Integer, null, out build)) && + (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], NumberStyles.Integer, null, out revision)))) + { + if (mustBeComplete && (parts.Length < 4 || parts[2] == "" || parts[3] == "")) + version = null; + else if (major == 65535 || minor == 65535) + version = null; + else + version = new Version(major, minor, build, revision); + + return true; + } + + version = null; + return false; + } + + } + } diff --git a/src/IKVM.Reflection/Emit/ArrayMethod.cs b/src/IKVM.Reflection/Emit/ArrayMethod.cs new file mode 100644 index 0000000000..394b2b70e6 --- /dev/null +++ b/src/IKVM.Reflection/Emit/ArrayMethod.cs @@ -0,0 +1,142 @@ +/* + Copyright (C) 2008-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +namespace IKVM.Reflection.Emit +{ + class ArrayMethod : MethodInfo + { + private readonly Module module; + private readonly Type arrayClass; + private readonly string methodName; + private readonly CallingConventions callingConvention; + private readonly Type returnType; + protected readonly Type[] parameterTypes; + private MethodSignature methodSignature; + + internal ArrayMethod(Module module, Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + this.module = module; + this.arrayClass = arrayClass; + this.methodName = methodName; + this.callingConvention = callingConvention; + this.returnType = returnType ?? module.universe.System_Void; + this.parameterTypes = Util.Copy(parameterTypes); + } + + public override MethodBody GetMethodBody() + { + throw new InvalidOperationException(); + } + + public override int __MethodRVA + { + get { throw new InvalidOperationException(); } + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + throw new NotSupportedException(); + } + + public override ParameterInfo[] GetParameters() + { + throw new NotSupportedException(); + } + + internal override int ImportTo(ModuleBuilder module) + { + return module.ImportMethodOrField(arrayClass, methodName, MethodSignature); + } + + public override MethodAttributes Attributes + { + get { throw new NotSupportedException(); } + } + + public override CallingConventions CallingConvention + { + get { return callingConvention; } + } + + public override Type DeclaringType + { + get { return arrayClass; } + } + + internal override MethodSignature MethodSignature + { + get + { + if (methodSignature == null) + { + methodSignature = MethodSignature.MakeFromBuilder(returnType, parameterTypes, new PackedCustomModifiers(), callingConvention, 0); + } + return methodSignature; + } + } + + public override Module Module + { + // FXBUG like .NET, we return the module that GetArrayMethod was called on, not the module associated with the array type + get { return module; } + } + + public override string Name + { + get { return methodName; } + } + + internal override int ParameterCount + { + get { return parameterTypes.Length; } + } + + public override ParameterInfo ReturnParameter + { + // FXBUG like .NET, we throw NotImplementedException + get { throw new NotImplementedException(); } + } + + public override Type ReturnType + { + get { return returnType; } + } + + internal override bool HasThis + { + get { return (callingConvention & (CallingConventions.HasThis | CallingConventions.ExplicitThis)) == CallingConventions.HasThis; } + } + + internal override int GetCurrentToken() + { + return this.MetadataToken; + } + + internal override bool IsBaked + { + get { return arrayClass.IsBaked; } + } + } +} diff --git a/src/IKVM.Reflection/Emit/AssemblyBuilder.cs b/src/IKVM.Reflection/Emit/AssemblyBuilder.cs index e2707c9bc9..114e61de66 100644 --- a/src/IKVM.Reflection/Emit/AssemblyBuilder.cs +++ b/src/IKVM.Reflection/Emit/AssemblyBuilder.cs @@ -34,797 +34,782 @@ namespace IKVM.Reflection.Emit { public sealed class AssemblyBuilder : Assembly - { - - private readonly string name; - private ushort majorVersion; - private ushort minorVersion; - private ushort buildVersion; - private ushort revisionVersion; - private string culture; - private AssemblyNameFlags flags; - private AssemblyHashAlgorithm hashAlgorithm; - private StrongNameKeyPair keyPair; - private byte[] publicKey; - internal readonly string dir; - private PEFileKinds fileKind = PEFileKinds.Dll; - private MethodInfo entryPoint; - private VersionInfo versionInfo; - private byte[] win32icon; - private byte[] win32manifest; - private byte[] win32resources; - private string imageRuntimeVersion; - internal int mdStreamVersion = 0x20000; - private Module pseudoManifestModule; - private readonly List resourceFiles = new List(); - private readonly List modules = new List(); - private readonly List addedModules = new List(); - private readonly List customAttributes = new List(); - private readonly List declarativeSecurity = new List(); - private readonly List typeForwarders = new List(); - - struct TypeForwarder - { - internal readonly Type Type; - internal readonly bool IncludeNested; - - internal TypeForwarder(Type type, bool includeNested) - { - this.Type = type; - this.IncludeNested = includeNested; - } - } - - private struct ResourceFile - { - internal string Name; - internal string FileName; - internal ResourceAttributes Attributes; - internal ResourceWriter Writer; - } - - internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, IEnumerable customAttributes) - : base(universe) - { - this.name = name.Name; - SetVersionHelper(name.Version); - if (!string.IsNullOrEmpty(name.CultureName)) - { - this.culture = name.CultureName; - } - this.flags = name.RawFlags; - this.hashAlgorithm = name.HashAlgorithm; - if (this.hashAlgorithm == AssemblyHashAlgorithm.None) - { - this.hashAlgorithm = AssemblyHashAlgorithm.SHA1; - } - this.keyPair = name.KeyPair; - if (this.keyPair != null) - { - this.publicKey = this.keyPair.PublicKey; - } - else - { - byte[] publicKey = name.GetPublicKey(); - if (publicKey != null && publicKey.Length != 0) - { - this.publicKey = (byte[])publicKey.Clone(); - } - } - this.dir = dir ?? "."; - if (customAttributes != null) - { - this.customAttributes.AddRange(customAttributes); - } - if (universe.HasCoreLib && !universe.CoreLib.__IsMissing && universe.CoreLib.ImageRuntimeVersion != null) - { - this.imageRuntimeVersion = universe.CoreLib.ImageRuntimeVersion; - } - else - { - this.imageRuntimeVersion = TypeUtil.GetAssembly(typeof(object)).ImageRuntimeVersion; - } - universe.RegisterDynamicAssembly(this); - } - - private void SetVersionHelper(Version version) - { - if (version == null) - { - majorVersion = 0; - minorVersion = 0; - buildVersion = 0; - revisionVersion = 0; - } - else - { - majorVersion = (ushort)version.Major; - minorVersion = (ushort)version.Minor; - buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build; - revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision; - } - } - - private void Rename(AssemblyName oldName) - { - this.fullName = null; - universe.RenameAssembly(this, oldName); - } - - public void __SetAssemblyVersion(Version version) - { - AssemblyName oldName = GetName(); - SetVersionHelper(version); - Rename(oldName); - } - - public void __SetAssemblyCulture(string cultureName) - { - AssemblyName oldName = GetName(); - this.culture = cultureName; - Rename(oldName); - } - - public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair) - { - AssemblyName oldName = GetName(); - this.keyPair = keyPair; - if (keyPair != null) - { - this.publicKey = keyPair.PublicKey; - } - Rename(oldName); - } - - // this is used in combination with delay signing - public void __SetAssemblyPublicKey(byte[] publicKey) - { - AssemblyName oldName = GetName(); - this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone(); - Rename(oldName); - } - - public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm) - { - this.hashAlgorithm = hashAlgorithm; - } - - [Obsolete("Use __AssemblyFlags property instead.")] - public void __SetAssemblyFlags(AssemblyNameFlags flags) - { - this.__AssemblyFlags = flags; - } - - protected override AssemblyNameFlags GetAssemblyFlags() - { - return flags; - } - - public new AssemblyNameFlags __AssemblyFlags - { - get { return flags; } - set - { - AssemblyName oldName = GetName(); - this.flags = value; - Rename(oldName); - } - } - - internal string Name - { - get { return name; } - } - - public override AssemblyName GetName() - { - AssemblyName n = new AssemblyName(); - n.Name = name; - n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion); - n.CultureName = culture ?? ""; - n.HashAlgorithm = hashAlgorithm; - n.RawFlags = flags; - n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty.Array); - n.KeyPair = keyPair; - return n; - } - - public override string Location - { - get { throw new NotSupportedException(); } - } - - public ModuleBuilder DefineDynamicModule(string name, string fileName) - { - return DefineDynamicModule(name, fileName, false); - } - - public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo) - { - ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo); - modules.Add(module); - return module; - } - - public ModuleBuilder GetDynamicModule(string name) - { - foreach (ModuleBuilder module in modules) - { - if (module.Name == name) - { - return module; - } - } - return null; - } - - public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute)); - } - - public void SetCustomAttribute(CustomAttributeBuilder customBuilder) - { - customAttributes.Add(customBuilder); - } - - public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder) - { - declarativeSecurity.Add(customBuilder); - } - - public void __AddTypeForwarder(Type type) - { - __AddTypeForwarder(type, true); - } - - public void __AddTypeForwarder(Type type, bool includeNested) - { - typeForwarders.Add(new TypeForwarder(type, includeNested)); - } - - public void SetEntryPoint(MethodInfo entryMethod) - { - SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication); - } - - public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind) - { - this.entryPoint = entryMethod; - this.fileKind = fileKind; - } - - public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0) - { - throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream"); - } - if (modules.Count != 1) - { - throw new NotSupportedException("Saving to a stream is only supported for single module assemblies."); - } - SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine); - } - - public void Save(string assemblyFileName) - { - Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386); - } - - public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine); - } - - private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - ModuleBuilder manifestModule = null; - - foreach (ModuleBuilder moduleBuilder in modules) - { - moduleBuilder.SetIsSaved(); - moduleBuilder.PopulatePropertyAndEventTables(); - - if (manifestModule == null - && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0) - { - manifestModule = moduleBuilder; - } - } - - if (manifestModule == null) - { - manifestModule = DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false); - } - - AssemblyTable.Record assemblyRecord = new AssemblyTable.Record(); - assemblyRecord.HashAlgId = (int)hashAlgorithm; - assemblyRecord.Name = manifestModule.Strings.Add(name); - assemblyRecord.MajorVersion = majorVersion; - assemblyRecord.MinorVersion = minorVersion; - assemblyRecord.BuildNumber = buildVersion; - assemblyRecord.RevisionNumber = revisionVersion; - if (publicKey != null) - { - assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey)); - assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey); - } - else - { - assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey); - } - if (culture != null) - { - assemblyRecord.Culture = manifestModule.Strings.Add(culture); - } - manifestModule.AssemblyTable.AddRecord(assemblyRecord); - - ResourceSection unmanagedResources = versionInfo != null || win32icon != null || win32manifest != null || win32resources != null - ? new ResourceSection() - : null; - - if (versionInfo != null) - { - versionInfo.SetName(GetName()); - versionInfo.SetFileName(assemblyFileName); - foreach (CustomAttributeBuilder cab in customAttributes) - { - // .NET doesn't support copying blob custom attributes into the version info - if (!cab.HasBlob || universe.DecodeVersionInfoAttributeBlobs) - { - versionInfo.SetAttribute(this, cab); - } - } - ByteBuffer versionInfoData = new ByteBuffer(512); - versionInfo.Write(versionInfoData); - unmanagedResources.AddVersionInfo(versionInfoData); - } - - if (win32icon != null) - { - unmanagedResources.AddIcon(win32icon); - } - - if (win32manifest != null) - { - unmanagedResources.AddManifest(win32manifest, fileKind == PEFileKinds.Dll ? (ushort)2 : (ushort)1); - } - - if (win32resources != null) - { - unmanagedResources.ExtractResources(win32resources); - } - - foreach (CustomAttributeBuilder cab in customAttributes) - { - // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET) - manifestModule.SetCustomAttribute(0x20000001, cab); - } - - manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity); - - foreach (TypeForwarder fwd in typeForwarders) - { - manifestModule.AddTypeForwarder(fwd.Type, fwd.IncludeNested); - } - - foreach (ResourceFile resfile in resourceFiles) - { - if (resfile.Writer != null) - { - resfile.Writer.Generate(); - resfile.Writer.Close(); - } - int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/); - ManifestResourceTable.Record rec = new ManifestResourceTable.Record(); - rec.Offset = 0; - rec.Flags = (int)resfile.Attributes; - rec.Name = manifestModule.Strings.Add(resfile.Name); - rec.Implementation = fileToken; - manifestModule.ManifestResource.AddRecord(rec); - } - - int entryPointToken = 0; - - foreach (ModuleBuilder moduleBuilder in modules) - { - moduleBuilder.FillAssemblyRefTable(); - moduleBuilder.EmitResources(); - if (moduleBuilder != manifestModule) - { - int fileToken; - if (entryPoint != null && entryPoint.Module == moduleBuilder) - { - ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken); - entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/); - } - else - { - ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0); - fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/); - } - moduleBuilder.ExportTypes(fileToken, manifestModule); - } - moduleBuilder.CloseResources(); - } - - foreach (Module module in addedModules) - { - int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/); - module.ExportTypes(fileToken, manifestModule); - } - - if (entryPointToken == 0 && entryPoint != null) - { - entryPointToken = entryPoint.MetadataToken; - } - - // finally, write the manifest module - ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull); - } - - private int AddFile(ModuleBuilder manifestModule, string fileName, int flags) - { - string fullPath = fileName; - if (dir != null) - { - fullPath = Path.Combine(dir, fileName); - } - byte[] hash; - using (SHA1 sha1 = SHA1.Create()) - using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) - { - hash = sha1.ComputeHash(fs); - } - return manifestModule.__AddModule(flags, Path.GetFileName(fileName), hash); - } - - public void AddResourceFile(string name, string fileName) - { - AddResourceFile(name, fileName, ResourceAttributes.Public); - } - - public void AddResourceFile(string name, string fileName, ResourceAttributes attribs) - { - ResourceFile resfile = new ResourceFile(); - resfile.Name = name; - resfile.FileName = fileName; - resfile.Attributes = attribs; - resourceFiles.Add(resfile); - } - - public IResourceWriter DefineResource(string name, string description, string fileName) - { - return DefineResource(name, description, fileName, ResourceAttributes.Public); - } - - public IResourceWriter DefineResource(string name, string description, string fileName, ResourceAttributes attribute) - { - // FXBUG we ignore the description, because there is no such thing - - string fullPath = fileName; - if (dir != null) - { - fullPath = Path.Combine(dir, fileName); - } - ResourceWriter rw = new ResourceWriter(fullPath); - ResourceFile resfile; - resfile.Name = name; - resfile.FileName = fileName; - resfile.Attributes = attribute; - resfile.Writer = rw; - resourceFiles.Add(resfile); - return rw; - } - - public void DefineVersionInfoResource() - { - if (versionInfo != null || win32resources != null) - { - throw new ArgumentException("Native resource has already been defined."); - } - versionInfo = new VersionInfo(); - } - - public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark) - { - if (versionInfo != null || win32resources != null) - { - throw new ArgumentException("Native resource has already been defined."); - } - versionInfo = new VersionInfo(); - versionInfo.product = product; - versionInfo.informationalVersion = productVersion; - versionInfo.company = company; - versionInfo.copyright = copyright; - versionInfo.trademark = trademark; - } - - public void __DefineIconResource(byte[] iconFile) - { - if (win32icon != null || win32resources != null) - { - throw new ArgumentException("Native resource has already been defined."); - } - win32icon = (byte[])iconFile.Clone(); - } - - public void __DefineManifestResource(byte[] manifest) - { - if (win32manifest != null || win32resources != null) - { - throw new ArgumentException("Native resource has already been defined."); - } - win32manifest = (byte[])manifest.Clone(); - } - - public void __DefineUnmanagedResource(byte[] resource) - { - if (versionInfo != null || win32icon != null || win32manifest != null || win32resources != null) - { - throw new ArgumentException("Native resource has already been defined."); - } - // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section, - // but it doesn't set the PE file Resource Directory entry to point to it. That's why we have a renamed version, which behaves - // like DefineUnmanagedResource(string). - win32resources = (byte[])resource.Clone(); - } - - public void DefineUnmanagedResource(string resourceFileName) - { - // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section, - // also setting the Resource Directory entry. - __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName)); - } - - public override Type[] GetTypes() - { - List list = new List(); - foreach (ModuleBuilder module in modules) - { - module.GetTypesImpl(list); - } - foreach (Module module in addedModules) - { - module.GetTypesImpl(list); - } - return list.ToArray(); - } - - internal override Type FindType(TypeName typeName) - { - foreach (ModuleBuilder mb in modules) - { - Type type = mb.FindType(typeName); - if (type != null) - { - return type; - } - } - foreach (Module module in addedModules) - { - Type type = module.FindType(typeName); - if (type != null) - { - return type; - } - } - return null; - } - - internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) - { - foreach (ModuleBuilder mb in modules) - { - Type type = mb.FindTypeIgnoreCase(lowerCaseName); - if (type != null) - { - return type; - } - } - foreach (Module module in addedModules) - { - Type type = module.FindTypeIgnoreCase(lowerCaseName); - if (type != null) - { - return type; - } - } - return null; - } - - public override string ImageRuntimeVersion - { - get { return imageRuntimeVersion; } - } - - public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion) - { - this.imageRuntimeVersion = imageRuntimeVersion; - this.mdStreamVersion = mdStreamVersion; - } - - public override Module ManifestModule - { - get - { - if (pseudoManifestModule == null) - { - pseudoManifestModule = new ManifestModule(this); - } - return pseudoManifestModule; - } - } - - public override MethodInfo EntryPoint - { - get { return entryPoint; } - } - - public override AssemblyName[] GetReferencedAssemblies() - { - return Empty.Array; - } - - public override Module[] GetLoadedModules(bool getResourceModules) - { - return GetModules(getResourceModules); - } - - public override Module[] GetModules(bool getResourceModules) - { - List list = new List(); - foreach (ModuleBuilder module in modules) - { - if (getResourceModules || !module.IsResource()) - { - list.Add(module); - } - } - foreach (Module module in addedModules) - { - if (getResourceModules || !module.IsResource()) - { - list.Add(module); - } - } - return list.ToArray(); - } - - public override Module GetModule(string name) - { - foreach (ModuleBuilder module in modules) - { - if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - return module; - } - } - foreach (Module module in addedModules) - { - if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - return module; - } - } - return null; - } - - public Module __AddModule(RawModule module) - { - Module mod = module.ToModule(this); - addedModules.Add(mod); - return mod; - } - - public override ManifestResourceInfo GetManifestResourceInfo(string resourceName) - { - throw new NotSupportedException(); - } - - public override string[] GetManifestResourceNames() - { - throw new NotSupportedException(); - } - - public override Stream GetManifestResourceStream(string resourceName) - { - throw new NotSupportedException(); - } - - public override bool IsDynamic - { - get { return true; } - } - - public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) - { - return new Universe().DefineDynamicAssembly(name, access); - } - - public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access, IEnumerable assemblyAttributes) - { - return new Universe().DefineDynamicAssembly(name, access, assemblyAttributes); - } - - internal override IList GetCustomAttributesData(Type attributeType) - { - List list = new List(); - foreach (CustomAttributeBuilder cab in customAttributes) - { - if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType)) - { - list.Add(cab.ToData(this)); - } - } - return list; - } - - internal bool IsWindowsRuntime - { - get { return (flags & (AssemblyNameFlags)0x200) != 0; } - } - } - - sealed class ManifestModule : NonPEModule - { - private readonly AssemblyBuilder assembly; - private readonly Guid guid = Guid.NewGuid(); - - internal ManifestModule(AssemblyBuilder assembly) - : base(assembly.universe) - { - this.assembly = assembly; - } - - public override int MDStreamVersion - { - get { return assembly.mdStreamVersion; } - } - - public override Assembly Assembly - { - get { return assembly; } - } - - internal override Type FindType(TypeName typeName) - { - return null; - } - - internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) - { - return null; - } - - internal override void GetTypesImpl(List list) - { - } - - public override string FullyQualifiedName - { - get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); } - } - - public override string Name - { - get { return ""; } - } - - public override Guid ModuleVersionId - { - get { return guid; } - } - - public override string ScopeName - { - get { return "RefEmit_InMemoryManifestModule"; } - } - - protected override Exception NotSupportedException() - { - return new InvalidOperationException(); - } - } + { + + private readonly string name; + private ushort majorVersion; + private ushort minorVersion; + private ushort buildVersion; + private ushort revisionVersion; + private string culture; + private AssemblyNameFlags flags; + private AssemblyHashAlgorithm hashAlgorithm; + private StrongNameKeyPair keyPair; + private byte[] publicKey; + internal readonly string dir; + private PEFileKinds fileKind = PEFileKinds.Dll; + private MethodInfo entryPoint; + private VersionInfo versionInfo; + private byte[] win32icon; + private byte[] win32manifest; + private byte[] win32resources; + private string imageRuntimeVersion; + internal int mdStreamVersion = 0x20000; + private Module pseudoManifestModule; + private readonly List resourceFiles = new List(); + private readonly List modules = new List(); + private readonly List addedModules = new List(); + private readonly List customAttributes = new List(); + private readonly List declarativeSecurity = new List(); + private readonly List typeForwarders = new List(); + + struct TypeForwarder + { + internal readonly Type Type; + internal readonly bool IncludeNested; + + internal TypeForwarder(Type type, bool includeNested) + { + this.Type = type; + this.IncludeNested = includeNested; + } + } + + private struct ResourceFile + { + internal string Name; + internal string FileName; + internal ResourceAttributes Attributes; + internal ResourceWriter Writer; + } + + internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, IEnumerable customAttributes) + : base(universe) + { + this.name = name.Name; + SetVersionHelper(name.Version); + if (!string.IsNullOrEmpty(name.CultureName)) + { + this.culture = name.CultureName; + } + this.flags = name.RawFlags; + this.hashAlgorithm = name.HashAlgorithm; + if (this.hashAlgorithm == AssemblyHashAlgorithm.None) + { + this.hashAlgorithm = AssemblyHashAlgorithm.SHA1; + } + this.keyPair = name.KeyPair; + if (this.keyPair != null) + { + this.publicKey = this.keyPair.PublicKey; + } + else + { + byte[] publicKey = name.GetPublicKey(); + if (publicKey != null && publicKey.Length != 0) + { + this.publicKey = (byte[])publicKey.Clone(); + } + } + this.dir = dir ?? "."; + if (customAttributes != null) + { + this.customAttributes.AddRange(customAttributes); + } + if (universe.HasCoreLib && !universe.CoreLib.__IsMissing && universe.CoreLib.ImageRuntimeVersion != null) + { + this.imageRuntimeVersion = universe.CoreLib.ImageRuntimeVersion; + } + else + { + this.imageRuntimeVersion = TypeUtil.GetAssembly(typeof(object)).ImageRuntimeVersion; + } + universe.RegisterDynamicAssembly(this); + } + + private void SetVersionHelper(Version version) + { + if (version == null) + { + majorVersion = 0; + minorVersion = 0; + buildVersion = 0; + revisionVersion = 0; + } + else + { + majorVersion = (ushort)version.Major; + minorVersion = (ushort)version.Minor; + buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build; + revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision; + } + } + + private void Rename(AssemblyName oldName) + { + this.fullName = null; + universe.RenameAssembly(this, oldName); + } + + public void __SetAssemblyVersion(Version version) + { + AssemblyName oldName = GetName(); + SetVersionHelper(version); + Rename(oldName); + } + + public void __SetAssemblyCulture(string cultureName) + { + AssemblyName oldName = GetName(); + this.culture = cultureName; + Rename(oldName); + } + + public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair) + { + AssemblyName oldName = GetName(); + this.keyPair = keyPair; + if (keyPair != null) + { + this.publicKey = keyPair.PublicKey; + } + Rename(oldName); + } + + // this is used in combination with delay signing + public void __SetAssemblyPublicKey(byte[] publicKey) + { + AssemblyName oldName = GetName(); + this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone(); + Rename(oldName); + } + + public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm) + { + this.hashAlgorithm = hashAlgorithm; + } + + [Obsolete("Use __AssemblyFlags property instead.")] + public void __SetAssemblyFlags(AssemblyNameFlags flags) + { + this.__AssemblyFlags = flags; + } + + protected override AssemblyNameFlags GetAssemblyFlags() + { + return flags; + } + + public new AssemblyNameFlags __AssemblyFlags + { + get { return flags; } + set + { + AssemblyName oldName = GetName(); + this.flags = value; + Rename(oldName); + } + } + + internal string Name + { + get { return name; } + } + + public override AssemblyName GetName() + { + AssemblyName n = new AssemblyName(); + n.Name = name; + n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion); + n.CultureName = culture ?? ""; + n.HashAlgorithm = hashAlgorithm; + n.RawFlags = flags; + n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty.Array); + n.KeyPair = keyPair; + return n; + } + + public override string Location + { + get { throw new NotSupportedException(); } + } + + public ModuleBuilder DefineDynamicModule(string name, string fileName) + { + return DefineDynamicModule(name, fileName, false); + } + + public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo) + { + ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo); + modules.Add(module); + return module; + } + + public ModuleBuilder GetDynamicModule(string name) + { + foreach (ModuleBuilder module in modules) + { + if (module.Name == name) + { + return module; + } + } + return null; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute)); + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + customAttributes.Add(customBuilder); + } + + public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder) + { + declarativeSecurity.Add(customBuilder); + } + + public void __AddTypeForwarder(Type type) + { + __AddTypeForwarder(type, true); + } + + public void __AddTypeForwarder(Type type, bool includeNested) + { + typeForwarders.Add(new TypeForwarder(type, includeNested)); + } + + public void SetEntryPoint(MethodInfo entryMethod) + { + SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication); + } + + public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind) + { + this.entryPoint = entryMethod; + this.fileKind = fileKind; + } + + public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0) + { + throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream"); + } + if (modules.Count != 1) + { + throw new NotSupportedException("Saving to a stream is only supported for single module assemblies."); + } + SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine); + } + + public void Save(string assemblyFileName) + { + Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386); + } + + public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine); + } + + private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + ModuleBuilder manifestModule = null; + + foreach (ModuleBuilder moduleBuilder in modules) + { + moduleBuilder.SetIsSaved(); + moduleBuilder.PopulatePropertyAndEventTables(); + + if (manifestModule == null && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0) + manifestModule = moduleBuilder; + } + + manifestModule ??= DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false); + + var assemblyRecord = new AssemblyTable.Record(); + assemblyRecord.HashAlgId = (int)hashAlgorithm; + assemblyRecord.Name = manifestModule.Strings.Add(name); + assemblyRecord.MajorVersion = majorVersion; + assemblyRecord.MinorVersion = minorVersion; + assemblyRecord.BuildNumber = buildVersion; + assemblyRecord.RevisionNumber = revisionVersion; + + if (publicKey != null) + { + assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey)); + assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey); + } + else + { + assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey); + } + + if (culture != null) + { + assemblyRecord.Culture = manifestModule.Strings.Add(culture); + } + manifestModule.AssemblyTable.AddRecord(assemblyRecord); + + var unmanagedResources = versionInfo != null || win32icon != null || win32manifest != null || win32resources != null ? new ResourceSection() : null; + + if (versionInfo != null) + { + versionInfo.SetName(GetName()); + versionInfo.SetFileName(assemblyFileName); + foreach (var cab in customAttributes) + { + // .NET doesn't support copying blob custom attributes into the version info + if (!cab.HasBlob || universe.DecodeVersionInfoAttributeBlobs) + versionInfo.SetAttribute(this, cab); + } + var versionInfoData = new ByteBuffer(512); + versionInfo.Write(versionInfoData); + unmanagedResources.AddVersionInfo(versionInfoData); + } + + if (win32icon != null) + unmanagedResources.AddIcon(win32icon); + + if (win32manifest != null) + unmanagedResources.AddManifest(win32manifest, fileKind == PEFileKinds.Dll ? (ushort)2 : (ushort)1); + + if (win32resources != null) + unmanagedResources.ExtractResources(win32resources); + + foreach (var cab in customAttributes) + { + // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET) + manifestModule.SetCustomAttribute(0x20000001, cab); + } + + manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity); + + foreach (var fwd in typeForwarders) + manifestModule.AddTypeForwarder(fwd.Type, fwd.IncludeNested); + + foreach (var resfile in resourceFiles) + { + if (resfile.Writer != null) + { + resfile.Writer.Generate(); + resfile.Writer.Close(); + } + + int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/); + var rec = new ManifestResourceTable.Record(); + rec.Offset = 0; + rec.Flags = (int)resfile.Attributes; + rec.Name = manifestModule.Strings.Add(resfile.Name); + rec.Implementation = fileToken; + manifestModule.ManifestResource.AddRecord(rec); + } + + int entryPointToken = 0; + + foreach (var moduleBuilder in modules) + { + moduleBuilder.FillAssemblyRefTable(); + moduleBuilder.EmitResources(); + + if (moduleBuilder != manifestModule) + { + int fileToken; + if (entryPoint != null && entryPoint.Module == moduleBuilder) + { + ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken); + entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/); + } + else + { + ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0); + fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/); + } + + moduleBuilder.ExportTypes(fileToken, manifestModule); + } + + moduleBuilder.CloseResources(); + } + + foreach (var module in addedModules) + { + int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/); + module.ExportTypes(fileToken, manifestModule); + } + + if (entryPointToken == 0 && entryPoint != null) + entryPointToken = entryPoint.MetadataToken; + + // finally, write the manifest module + ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull); + } + + private int AddFile(ModuleBuilder manifestModule, string fileName, int flags) + { + string fullPath = fileName; + if (dir != null) + { + fullPath = Path.Combine(dir, fileName); + } + byte[] hash; + using (SHA1 sha1 = SHA1.Create()) + using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) + { + hash = sha1.ComputeHash(fs); + } + return manifestModule.__AddModule(flags, Path.GetFileName(fileName), hash); + } + + public void AddResourceFile(string name, string fileName) + { + AddResourceFile(name, fileName, ResourceAttributes.Public); + } + + public void AddResourceFile(string name, string fileName, ResourceAttributes attribs) + { + ResourceFile resfile = new ResourceFile(); + resfile.Name = name; + resfile.FileName = fileName; + resfile.Attributes = attribs; + resourceFiles.Add(resfile); + } + + public IResourceWriter DefineResource(string name, string description, string fileName) + { + return DefineResource(name, description, fileName, ResourceAttributes.Public); + } + + public IResourceWriter DefineResource(string name, string description, string fileName, ResourceAttributes attribute) + { + // FXBUG we ignore the description, because there is no such thing + + string fullPath = fileName; + if (dir != null) + { + fullPath = Path.Combine(dir, fileName); + } + ResourceWriter rw = new ResourceWriter(fullPath); + ResourceFile resfile; + resfile.Name = name; + resfile.FileName = fileName; + resfile.Attributes = attribute; + resfile.Writer = rw; + resourceFiles.Add(resfile); + return rw; + } + + public void DefineVersionInfoResource() + { + if (versionInfo != null || win32resources != null) + throw new ArgumentException("Native resource has already been defined."); + + versionInfo = new VersionInfo(); + } + + public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark) + { + if (versionInfo != null || win32resources != null) + { + throw new ArgumentException("Native resource has already been defined."); + } + versionInfo = new VersionInfo(); + versionInfo.product = product; + versionInfo.informationalVersion = productVersion; + versionInfo.company = company; + versionInfo.copyright = copyright; + versionInfo.trademark = trademark; + } + + public void __DefineIconResource(byte[] iconFile) + { + if (win32icon != null || win32resources != null) + { + throw new ArgumentException("Native resource has already been defined."); + } + win32icon = (byte[])iconFile.Clone(); + } + + public void __DefineManifestResource(byte[] manifest) + { + if (win32manifest != null || win32resources != null) + { + throw new ArgumentException("Native resource has already been defined."); + } + win32manifest = (byte[])manifest.Clone(); + } + + public void __DefineUnmanagedResource(byte[] resource) + { + if (versionInfo != null || win32icon != null || win32manifest != null || win32resources != null) + { + throw new ArgumentException("Native resource has already been defined."); + } + // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section, + // but it doesn't set the PE file Resource Directory entry to point to it. That's why we have a renamed version, which behaves + // like DefineUnmanagedResource(string). + win32resources = (byte[])resource.Clone(); + } + + public void DefineUnmanagedResource(string resourceFileName) + { + // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section, + // also setting the Resource Directory entry. + __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName)); + } + + public override Type[] GetTypes() + { + List list = new List(); + foreach (ModuleBuilder module in modules) + { + module.GetTypesImpl(list); + } + foreach (Module module in addedModules) + { + module.GetTypesImpl(list); + } + return list.ToArray(); + } + + internal override Type FindType(TypeName typeName) + { + foreach (ModuleBuilder mb in modules) + { + Type type = mb.FindType(typeName); + if (type != null) + { + return type; + } + } + foreach (Module module in addedModules) + { + Type type = module.FindType(typeName); + if (type != null) + { + return type; + } + } + return null; + } + + internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) + { + foreach (ModuleBuilder mb in modules) + { + Type type = mb.FindTypeIgnoreCase(lowerCaseName); + if (type != null) + { + return type; + } + } + foreach (Module module in addedModules) + { + Type type = module.FindTypeIgnoreCase(lowerCaseName); + if (type != null) + { + return type; + } + } + return null; + } + + public override string ImageRuntimeVersion + { + get { return imageRuntimeVersion; } + } + + public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion) + { + this.imageRuntimeVersion = imageRuntimeVersion; + this.mdStreamVersion = mdStreamVersion; + } + + public override Module ManifestModule + { + get + { + if (pseudoManifestModule == null) + { + pseudoManifestModule = new ManifestModule(this); + } + return pseudoManifestModule; + } + } + + public override MethodInfo EntryPoint + { + get { return entryPoint; } + } + + public override AssemblyName[] GetReferencedAssemblies() + { + return Empty.Array; + } + + public override Module[] GetLoadedModules(bool getResourceModules) + { + return GetModules(getResourceModules); + } + + public override Module[] GetModules(bool getResourceModules) + { + List list = new List(); + foreach (ModuleBuilder module in modules) + { + if (getResourceModules || !module.IsResource()) + { + list.Add(module); + } + } + foreach (Module module in addedModules) + { + if (getResourceModules || !module.IsResource()) + { + list.Add(module); + } + } + return list.ToArray(); + } + + public override Module GetModule(string name) + { + foreach (ModuleBuilder module in modules) + { + if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return module; + } + } + foreach (Module module in addedModules) + { + if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return module; + } + } + return null; + } + + public Module __AddModule(RawModule module) + { + Module mod = module.ToModule(this); + addedModules.Add(mod); + return mod; + } + + public override ManifestResourceInfo GetManifestResourceInfo(string resourceName) + { + throw new NotSupportedException(); + } + + public override string[] GetManifestResourceNames() + { + throw new NotSupportedException(); + } + + public override Stream GetManifestResourceStream(string resourceName) + { + throw new NotSupportedException(); + } + + public override bool IsDynamic + { + get { return true; } + } + + public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) + { + return new Universe().DefineDynamicAssembly(name, access); + } + + public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access, IEnumerable assemblyAttributes) + { + return new Universe().DefineDynamicAssembly(name, access, assemblyAttributes); + } + + internal override IList GetCustomAttributesData(Type attributeType) + { + List list = new List(); + foreach (CustomAttributeBuilder cab in customAttributes) + { + if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType)) + { + list.Add(cab.ToData(this)); + } + } + return list; + } + + internal bool IsWindowsRuntime + { + get { return (flags & (AssemblyNameFlags)0x200) != 0; } + } + } + + sealed class ManifestModule : NonPEModule + { + private readonly AssemblyBuilder assembly; + private readonly Guid guid = Guid.NewGuid(); + + internal ManifestModule(AssemblyBuilder assembly) + : base(assembly.universe) + { + this.assembly = assembly; + } + + public override int MDStreamVersion + { + get { return assembly.mdStreamVersion; } + } + + public override Assembly Assembly + { + get { return assembly; } + } + + internal override Type FindType(TypeName typeName) + { + return null; + } + + internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) + { + return null; + } + + internal override void GetTypesImpl(List list) + { + } + + public override string FullyQualifiedName + { + get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); } + } + + public override string Name + { + get { return ""; } + } + + public override Guid ModuleVersionId + { + get { return guid; } + } + + public override string ScopeName + { + get { return "RefEmit_InMemoryManifestModule"; } + } + + protected override Exception NotSupportedException() + { + return new InvalidOperationException(); + } + } } diff --git a/src/IKVM.Reflection/Emit/CustomAttributeBuilder.cs b/src/IKVM.Reflection/Emit/CustomAttributeBuilder.cs index a76ac63262..31fd670613 100644 --- a/src/IKVM.Reflection/Emit/CustomAttributeBuilder.cs +++ b/src/IKVM.Reflection/Emit/CustomAttributeBuilder.cs @@ -22,737 +22,742 @@ Jeroen Frijters */ using System; -using System.IO; using System.Collections.Generic; using System.Diagnostics; using System.Text; + using IKVM.Reflection.Writer; namespace IKVM.Reflection.Emit { - public sealed class CustomAttributeBuilder - { - internal static readonly ConstructorInfo LegacyPermissionSet = new ConstructorBuilder(null); - private readonly ConstructorInfo con; - private readonly byte[] blob; - private readonly object[] constructorArgs; - private readonly PropertyInfo[] namedProperties; - private readonly object[] propertyValues; - private readonly FieldInfo[] namedFields; - private readonly object[] fieldValues; - - internal CustomAttributeBuilder(ConstructorInfo con, byte[] blob) - { - this.con = con; - this.blob = blob; - } - - private CustomAttributeBuilder(ConstructorInfo con, int securityAction, byte[] blob) - { - this.con = con; - this.blob = blob; - this.constructorArgs = new object[] { securityAction }; - } - - public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs) - : this(con, constructorArgs, null, null, null,null) - { - } - - public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues) - : this(con, constructorArgs, null, null, namedFields, fieldValues) - { - } - - public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues) - : this(con, constructorArgs, namedProperties, propertyValues, null, null) - { - } - - public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues) - { - this.con = con; - this.constructorArgs = constructorArgs; - this.namedProperties = namedProperties; - this.propertyValues = propertyValues; - this.namedFields = namedFields; - this.fieldValues = fieldValues; - } - - public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, byte[] blob) - { - return new CustomAttributeBuilder(con, blob); - } - - public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, int securityAction, byte[] blob) - { - return new CustomAttributeBuilder(con, securityAction, blob); - } - - public static CustomAttributeTypedArgument __MakeTypedArgument(Type type, object value) - { - return new CustomAttributeTypedArgument(type, value); - } - - private sealed class BlobWriter - { - private readonly Assembly assembly; - private readonly CustomAttributeBuilder cab; - private readonly ByteBuffer bb; - - internal BlobWriter(Assembly assembly, CustomAttributeBuilder cab, ByteBuffer bb) - { - this.assembly = assembly; - this.cab = cab; - this.bb = bb; - } - - internal void WriteCustomAttributeBlob() - { - // prolog - WriteUInt16(1); - ParameterInfo[] pi = cab.con.GetParameters(); - for (int i = 0; i < pi.Length; i++) - { - WriteFixedArg(pi[i].ParameterType, cab.constructorArgs[i]); - } - WriteNamedArguments(false); - } - - internal void WriteNamedArguments(bool forDeclSecurity) - { - // NumNamed - int named = 0; - if (cab.namedFields != null) - { - named += cab.namedFields.Length; - } - if (cab.namedProperties != null) - { - named += cab.namedProperties.Length; - } - if (forDeclSecurity) - { - WritePackedLen(named); - } - else - { - WriteUInt16((ushort)named); - } - if (cab.namedFields != null) - { - for (int i = 0; i < cab.namedFields.Length; i++) - { - WriteNamedArg(0x53, cab.namedFields[i].FieldType, cab.namedFields[i].Name, cab.fieldValues[i]); - } - } - if (cab.namedProperties != null) - { - for (int i = 0; i < cab.namedProperties.Length; i++) - { - WriteNamedArg(0x54, cab.namedProperties[i].PropertyType, cab.namedProperties[i].Name, cab.propertyValues[i]); - } - } - } - - private void WriteNamedArg(byte fieldOrProperty, Type type, string name, object value) - { - WriteByte(fieldOrProperty); - WriteFieldOrPropType(type); - WriteString(name); - WriteFixedArg(type, value); - } - - private void WriteByte(byte value) - { - bb.Write(value); - } - - private void WriteUInt16(ushort value) - { - bb.Write(value); - } - - private void WriteInt32(int value) - { - bb.Write(value); - } - - private void WriteFixedArg(Type type, object value) - { - Universe u = assembly.universe; - if (type == u.System_String) - { - WriteString((string)value); - } - else if (type == u.System_Boolean) - { - WriteByte((bool)value ? (byte)1 : (byte)0); - } - else if (type == u.System_Char) - { - WriteUInt16((char)value); - } - else if (type == u.System_SByte) - { - WriteByte((byte)(sbyte)value); - } - else if (type == u.System_Byte) - { - WriteByte((byte)value); - } - else if (type == u.System_Int16) - { - WriteUInt16((ushort)(short)value); - } - else if (type == u.System_UInt16) - { - WriteUInt16((ushort)value); - } - else if (type == u.System_Int32) - { - WriteInt32((int)value); - } - else if (type == u.System_UInt32) - { - WriteInt32((int)(uint)value); - } - else if (type == u.System_Int64) - { - WriteInt64((long)value); - } - else if (type == u.System_UInt64) - { - WriteInt64((long)(ulong)value); - } - else if (type == u.System_Single) - { - WriteSingle((float)value); - } - else if (type == u.System_Double) - { - WriteDouble((double)value); - } - else if (type == u.System_Type) - { - WriteTypeName((Type)value); - } - else if (type == u.System_Object) - { - if (value == null) - { - type = u.System_String; - } - else if (value is Type) - { - // value.GetType() would return a subclass of Type, but we don't want to deal with that - type = u.System_Type; - } - else if (value is CustomAttributeTypedArgument) - { - CustomAttributeTypedArgument cta = (CustomAttributeTypedArgument)value; - value = cta.Value; - type = cta.ArgumentType; - } - else - { - type = u.Import(value.GetType()); - } - WriteFieldOrPropType(type); - WriteFixedArg(type, value); - } - else if (type.IsArray) - { - if (value == null) - { - WriteInt32(-1); - } - else - { - Array array = (Array)value; - Type elemType = type.GetElementType(); - WriteInt32(array.Length); - foreach (object val in array) - { - WriteFixedArg(elemType, val); - } - } - } - else if (type.IsEnum) - { - WriteFixedArg(type.GetEnumUnderlyingTypeImpl(), value); - } - else - { - throw new ArgumentException(); - } - } - - private void WriteInt64(long value) - { - bb.Write(value); - } - - private void WriteSingle(float value) - { - bb.Write(value); - } - - private void WriteDouble(double value) - { - bb.Write(value); - } - - private void WriteTypeName(Type type) - { - string name = null; - if (type != null) - { - StringBuilder sb = new StringBuilder(); - GetTypeName(sb, type, false); - name = sb.ToString(); - } - WriteString(name); - } - - private void GetTypeName(StringBuilder sb, Type type, bool isTypeParam) - { - bool v1 = !assembly.ManifestModule.__IsMissing && assembly.ManifestModule.MDStreamVersion < 0x20000; - bool includeAssemblyName = type.Assembly != assembly && (!v1 || type.Assembly != type.Module.universe.CoreLib); - if (isTypeParam && includeAssemblyName) - { - sb.Append('['); - } - GetTypeNameImpl(sb, type); - if (includeAssemblyName) - { - if (v1) - { - sb.Append(','); - } - else - { - sb.Append(", "); - } - if (isTypeParam) - { - sb.Append(type.Assembly.FullName.Replace("]", "\\]")).Append(']'); - } - else - { - sb.Append(type.Assembly.FullName); - } - } - } - - private void GetTypeNameImpl(StringBuilder sb, Type type) - { - if (type.HasElementType) - { - GetTypeNameImpl(sb, type.GetElementType()); - sb.Append(((ElementHolderType)type).GetSuffix()); - } - else if (type.IsConstructedGenericType) - { - sb.Append(type.GetGenericTypeDefinition().FullName); - sb.Append('['); - string sep = ""; - foreach (Type typeParam in type.GetGenericArguments()) - { - sb.Append(sep); - GetTypeName(sb, typeParam, true); - sep = ","; - } - sb.Append(']'); - } - else - { - sb.Append(type.FullName); - } - } - - private void WriteString(string val) - { - bb.Write(val); - } - - private void WritePackedLen(int len) - { - bb.WriteCompressedUInt(len); - } - - private void WriteFieldOrPropType(Type type) - { - Universe u = type.Module.universe; - if (type == u.System_Type) - { - WriteByte(0x50); - } - else if (type == u.System_Object) - { - WriteByte(0x51); - } - else if (type == u.System_Boolean) - { - WriteByte(0x02); - } - else if (type == u.System_Char) - { - WriteByte(0x03); - } - else if (type == u.System_SByte) - { - WriteByte(0x04); - } - else if (type == u.System_Byte) - { - WriteByte(0x05); - } - else if (type == u.System_Int16) - { - WriteByte(0x06); - } - else if (type == u.System_UInt16) - { - WriteByte(0x07); - } - else if (type == u.System_Int32) - { - WriteByte(0x08); - } - else if (type == u.System_UInt32) - { - WriteByte(0x09); - } - else if (type == u.System_Int64) - { - WriteByte(0x0A); - } - else if (type == u.System_UInt64) - { - WriteByte(0x0B); - } - else if (type == u.System_Single) - { - WriteByte(0x0C); - } - else if (type == u.System_Double) - { - WriteByte(0x0D); - } - else if (type == u.System_String) - { - WriteByte(0x0E); - } - else if (type.IsArray) - { - WriteByte(0x1D); - WriteFieldOrPropType(type.GetElementType()); - } - else if (type.IsEnum) - { - WriteByte(0x55); - WriteTypeName(type); - } - else - { - throw new ArgumentException(); - } - } - } - - internal ConstructorInfo Constructor - { - get { return con; } - } - - internal int WriteBlob(ModuleBuilder moduleBuilder) - { - ByteBuffer bb; - if (blob != null) - { - bb = ByteBuffer.Wrap(blob); - } - else - { - bb = new ByteBuffer(100); - BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); - bw.WriteCustomAttributeBlob(); - } - return moduleBuilder.Blobs.Add(bb); - } - - internal object GetConstructorArgument(int pos) - { - return constructorArgs[pos]; - } - - internal int ConstructorArgumentCount - { - get { return constructorArgs == null ? 0 : constructorArgs.Length; } - } - - internal T? GetFieldValue(string name) where T : struct - { - object val = GetFieldValue(name); - if (val is T) - { - return (T)val; - } - else if (val != null) - { - if (TypeUtil.IsEnum(typeof(T))) - { - Debug.Assert(Enum.GetUnderlyingType(typeof(T)) == val.GetType()); - return (T)Enum.ToObject(typeof(T), val); - } - else - { - Debug.Assert(Enum.GetUnderlyingType(val.GetType()) == typeof(T)); - return (T)Convert.ChangeType(val, typeof(T)); - } - } - else - { - return null; - } - } - - internal object GetFieldValue(string name) - { - if (namedFields != null) - { - for (int i = 0; i < namedFields.Length; i++) - { - if (namedFields[i].Name == name) - { - return fieldValues[i]; - } - } - } - return null; - } - - internal bool IsLegacyDeclSecurity - { - get - { - return ReferenceEquals(con, LegacyPermissionSet) - || (con.DeclaringType == con.Module.universe.System_Security_Permissions_PermissionSetAttribute - && blob == null - && (namedFields == null || namedFields.Length == 0) - && namedProperties != null - && namedProperties.Length == 1 - && namedProperties[0].Name == "XML" - && propertyValues[0] is string); - } - } - - internal int WriteLegacyDeclSecurityBlob(ModuleBuilder moduleBuilder) - { - if (blob != null) - { - return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(blob)); - } - else - { - return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(Encoding.Unicode.GetBytes((string)propertyValues[0]))); - } - } - - internal void WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb) - { - if (blob != null) - { - bb.Write(blob); - } - else - { - BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); - bw.WriteNamedArguments(true); - } - } - - internal CustomAttributeData ToData(Assembly asm) - { - if (blob != null) - { - if (constructorArgs != null) - { - return new CustomAttributeData(asm, con, (int)constructorArgs[0], blob, -1); - } - return new CustomAttributeData(asm, con, new IKVM.Reflection.Reader.ByteReader(blob, 0, blob.Length)); - } - else - { - List namedArgs = new List(); - if (namedProperties != null) - { - for (int i = 0; i < namedProperties.Length; i++) - { - namedArgs.Add(new CustomAttributeNamedArgument(namedProperties[i], RewrapValue(namedProperties[i].PropertyType, propertyValues[i]))); - } - } - if (namedFields != null) - { - for (int i = 0; i < namedFields.Length; i++) - { - namedArgs.Add(new CustomAttributeNamedArgument(namedFields[i], RewrapValue(namedFields[i].FieldType, fieldValues[i]))); - } - } - List args = new List(constructorArgs.Length); - ParameterInfo[] parameters = this.Constructor.GetParameters(); - for (int i = 0; i < constructorArgs.Length; i++) - { - args.Add(RewrapValue(parameters[i].ParameterType, constructorArgs[i])); - } - return new CustomAttributeData(asm.ManifestModule, con, args, namedArgs); - } - } - - private static CustomAttributeTypedArgument RewrapValue(Type type, object value) - { - if (value is Array) - { - Array array = (Array)value; - Type arrayType = type.Module.universe.Import(array.GetType()); - return RewrapArray(arrayType, array); - } - else if (value is CustomAttributeTypedArgument) - { - CustomAttributeTypedArgument arg = (CustomAttributeTypedArgument)value; - if (arg.Value is Array) - { - return RewrapArray(arg.ArgumentType, (Array)arg.Value); - } - return arg; - } - else - { - return new CustomAttributeTypedArgument(type, value); - } - } - - private static CustomAttributeTypedArgument RewrapArray(Type arrayType, Array array) - { - Type elementType = arrayType.GetElementType(); - CustomAttributeTypedArgument[] newArray = new CustomAttributeTypedArgument[array.Length]; - for (int i = 0; i < newArray.Length; i++) - { - newArray[i] = RewrapValue(elementType, array.GetValue(i)); - } - return new CustomAttributeTypedArgument(arrayType, newArray); - } - - internal bool HasBlob - { - get { return blob != null; } - } - - internal CustomAttributeBuilder DecodeBlob(Assembly asm) - { - if (blob == null) - { - return this; - } - else - { - return ToData(asm).__ToBuilder(); - } - } - - internal byte[] GetBlob(Assembly asm) - { - ByteBuffer bb = new ByteBuffer(100); - BlobWriter bw = new BlobWriter(asm, this, bb); - bw.WriteCustomAttributeBlob(); - return bb.ToArray(); - } - - internal KnownCA KnownCA - { - get - { - Type attributeType = con.DeclaringType; - if (attributeType.IsConstructedGenericType) - { - // a constructed generic type doesn't have a TypeName and we already know it's not a Known CA - return KnownCA.Unknown; - } - TypeName typeName = attributeType.TypeName; - switch (typeName.Namespace) - { - case "System": - switch (typeName.Name) - { - case "SerializableAttribute": - return KnownCA.SerializableAttribute; - case "NonSerializedAttribute": - return KnownCA.NonSerializedAttribute; - } - break; - case "System.Runtime.CompilerServices": - switch (typeName.Name) - { - case "MethodImplAttribute": - return KnownCA.MethodImplAttribute; - case "SpecialNameAttribute": - return KnownCA.SpecialNameAttribute; - } - break; - case "System.Runtime.InteropServices": - switch (typeName.Name) - { - case "DllImportAttribute": - return KnownCA.DllImportAttribute; - case "ComImportAttribute": - return KnownCA.ComImportAttribute; - case "MarshalAsAttribute": - return KnownCA.MarshalAsAttribute; - case "PreserveSigAttribute": - return KnownCA.PreserveSigAttribute; - case "InAttribute": - return KnownCA.InAttribute; - case "OutAttribute": - return KnownCA.OutAttribute; - case "OptionalAttribute": - return KnownCA.OptionalAttribute; - case "StructLayoutAttribute": - return KnownCA.StructLayoutAttribute; - case "FieldOffsetAttribute": - return KnownCA.FieldOffsetAttribute; - } - break; - } - if (typeName.Matches("System.Security.SuppressUnmanagedCodeSecurityAttribute")) - { - return KnownCA.SuppressUnmanagedCodeSecurityAttribute; - } - return KnownCA.Unknown; - } - } - } - - // These are the pseudo-custom attributes that are recognized by name by the runtime (i.e. the type identity is not considered). - // The corresponding list in the runtime is at https://github.com/dotnet/coreclr/blob/1afe5ce4f45045d724a4e129df4b816655d486fb/src/md/compiler/custattr_emit.cpp#L38 - // Note that we only need to handle a subset of the types, since we don't need the ones that are only used for validation by the runtime. - enum KnownCA - { - Unknown, - DllImportAttribute, - ComImportAttribute, - SerializableAttribute, - NonSerializedAttribute, - MethodImplAttribute, - MarshalAsAttribute, - PreserveSigAttribute, - InAttribute, - OutAttribute, - OptionalAttribute, - StructLayoutAttribute, - FieldOffsetAttribute, - SpecialNameAttribute, - // the following is not part of the runtime known custom attributes, but we handle it here for efficiency and convenience - SuppressUnmanagedCodeSecurityAttribute, - } + + public sealed class CustomAttributeBuilder + { + + internal static readonly ConstructorInfo LegacyPermissionSet = new ConstructorBuilder(null); + private readonly ConstructorInfo con; + private readonly byte[] blob; + private readonly object[] constructorArgs; + private readonly PropertyInfo[] namedProperties; + private readonly object[] propertyValues; + private readonly FieldInfo[] namedFields; + private readonly object[] fieldValues; + + internal CustomAttributeBuilder(ConstructorInfo con, byte[] blob) + { + this.con = con; + this.blob = blob; + } + + private CustomAttributeBuilder(ConstructorInfo con, int securityAction, byte[] blob) + { + this.con = con; + this.blob = blob; + this.constructorArgs = new object[] { securityAction }; + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs) : + this(con, constructorArgs, null, null, null, null) + { + + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues) : + this(con, constructorArgs, null, null, namedFields, fieldValues) + { + + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues) : + this(con, constructorArgs, namedProperties, propertyValues, null, null) + { + + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues) + { + this.con = con; + this.constructorArgs = constructorArgs; + this.namedProperties = namedProperties; + this.propertyValues = propertyValues; + this.namedFields = namedFields; + this.fieldValues = fieldValues; + } + + public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, byte[] blob) + { + return new CustomAttributeBuilder(con, blob); + } + + public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, int securityAction, byte[] blob) + { + return new CustomAttributeBuilder(con, securityAction, blob); + } + + public static CustomAttributeTypedArgument __MakeTypedArgument(Type type, object value) + { + return new CustomAttributeTypedArgument(type, value); + } + + private sealed class BlobWriter + { + private readonly Assembly assembly; + private readonly CustomAttributeBuilder cab; + private readonly ByteBuffer bb; + + internal BlobWriter(Assembly assembly, CustomAttributeBuilder cab, ByteBuffer bb) + { + this.assembly = assembly; + this.cab = cab; + this.bb = bb; + } + + internal void WriteCustomAttributeBlob() + { + // prolog + WriteUInt16(1); + ParameterInfo[] pi = cab.con.GetParameters(); + for (int i = 0; i < pi.Length; i++) + { + WriteFixedArg(pi[i].ParameterType, cab.constructorArgs[i]); + } + WriteNamedArguments(false); + } + + internal void WriteNamedArguments(bool forDeclSecurity) + { + // NumNamed + int named = 0; + if (cab.namedFields != null) + { + named += cab.namedFields.Length; + } + if (cab.namedProperties != null) + { + named += cab.namedProperties.Length; + } + if (forDeclSecurity) + { + WritePackedLen(named); + } + else + { + WriteUInt16((ushort)named); + } + if (cab.namedFields != null) + { + for (int i = 0; i < cab.namedFields.Length; i++) + { + WriteNamedArg(0x53, cab.namedFields[i].FieldType, cab.namedFields[i].Name, cab.fieldValues[i]); + } + } + if (cab.namedProperties != null) + { + for (int i = 0; i < cab.namedProperties.Length; i++) + { + WriteNamedArg(0x54, cab.namedProperties[i].PropertyType, cab.namedProperties[i].Name, cab.propertyValues[i]); + } + } + } + + private void WriteNamedArg(byte fieldOrProperty, Type type, string name, object value) + { + WriteByte(fieldOrProperty); + WriteFieldOrPropType(type); + WriteString(name); + WriteFixedArg(type, value); + } + + private void WriteByte(byte value) + { + bb.Write(value); + } + + private void WriteUInt16(ushort value) + { + bb.Write(value); + } + + private void WriteInt32(int value) + { + bb.Write(value); + } + + private void WriteFixedArg(Type type, object value) + { + Universe u = assembly.universe; + if (type == u.System_String) + { + WriteString((string)value); + } + else if (type == u.System_Boolean) + { + WriteByte((bool)value ? (byte)1 : (byte)0); + } + else if (type == u.System_Char) + { + WriteUInt16((char)value); + } + else if (type == u.System_SByte) + { + WriteByte((byte)(sbyte)value); + } + else if (type == u.System_Byte) + { + WriteByte((byte)value); + } + else if (type == u.System_Int16) + { + WriteUInt16((ushort)(short)value); + } + else if (type == u.System_UInt16) + { + WriteUInt16((ushort)value); + } + else if (type == u.System_Int32) + { + WriteInt32((int)value); + } + else if (type == u.System_UInt32) + { + WriteInt32((int)(uint)value); + } + else if (type == u.System_Int64) + { + WriteInt64((long)value); + } + else if (type == u.System_UInt64) + { + WriteInt64((long)(ulong)value); + } + else if (type == u.System_Single) + { + WriteSingle((float)value); + } + else if (type == u.System_Double) + { + WriteDouble((double)value); + } + else if (type == u.System_Type) + { + WriteTypeName((Type)value); + } + else if (type == u.System_Object) + { + if (value == null) + { + type = u.System_String; + } + else if (value is Type) + { + // value.GetType() would return a subclass of Type, but we don't want to deal with that + type = u.System_Type; + } + else if (value is CustomAttributeTypedArgument) + { + CustomAttributeTypedArgument cta = (CustomAttributeTypedArgument)value; + value = cta.Value; + type = cta.ArgumentType; + } + else + { + type = u.Import(value.GetType()); + } + WriteFieldOrPropType(type); + WriteFixedArg(type, value); + } + else if (type.IsArray) + { + if (value == null) + { + WriteInt32(-1); + } + else + { + Array array = (Array)value; + Type elemType = type.GetElementType(); + WriteInt32(array.Length); + foreach (object val in array) + { + WriteFixedArg(elemType, val); + } + } + } + else if (type.IsEnum) + { + WriteFixedArg(type.GetEnumUnderlyingTypeImpl(), value); + } + else + { + throw new ArgumentException(); + } + } + + private void WriteInt64(long value) + { + bb.Write(value); + } + + private void WriteSingle(float value) + { + bb.Write(value); + } + + private void WriteDouble(double value) + { + bb.Write(value); + } + + private void WriteTypeName(Type type) + { + string name = null; + if (type != null) + { + StringBuilder sb = new StringBuilder(); + GetTypeName(sb, type, false); + name = sb.ToString(); + } + WriteString(name); + } + + private void GetTypeName(StringBuilder sb, Type type, bool isTypeParam) + { + bool v1 = !assembly.ManifestModule.__IsMissing && assembly.ManifestModule.MDStreamVersion < 0x20000; + bool includeAssemblyName = type.Assembly != assembly && (!v1 || type.Assembly != type.Module.universe.CoreLib); + if (isTypeParam && includeAssemblyName) + { + sb.Append('['); + } + GetTypeNameImpl(sb, type); + if (includeAssemblyName) + { + if (v1) + { + sb.Append(','); + } + else + { + sb.Append(", "); + } + if (isTypeParam) + { + sb.Append(type.Assembly.FullName.Replace("]", "\\]")).Append(']'); + } + else + { + sb.Append(type.Assembly.FullName); + } + } + } + + private void GetTypeNameImpl(StringBuilder sb, Type type) + { + if (type.HasElementType) + { + GetTypeNameImpl(sb, type.GetElementType()); + sb.Append(((ElementHolderType)type).GetSuffix()); + } + else if (type.IsConstructedGenericType) + { + sb.Append(type.GetGenericTypeDefinition().FullName); + sb.Append('['); + string sep = ""; + foreach (Type typeParam in type.GetGenericArguments()) + { + sb.Append(sep); + GetTypeName(sb, typeParam, true); + sep = ","; + } + sb.Append(']'); + } + else + { + sb.Append(type.FullName); + } + } + + private void WriteString(string val) + { + bb.Write(val); + } + + private void WritePackedLen(int len) + { + bb.WriteCompressedUInt(len); + } + + private void WriteFieldOrPropType(Type type) + { + Universe u = type.Module.universe; + if (type == u.System_Type) + { + WriteByte(0x50); + } + else if (type == u.System_Object) + { + WriteByte(0x51); + } + else if (type == u.System_Boolean) + { + WriteByte(0x02); + } + else if (type == u.System_Char) + { + WriteByte(0x03); + } + else if (type == u.System_SByte) + { + WriteByte(0x04); + } + else if (type == u.System_Byte) + { + WriteByte(0x05); + } + else if (type == u.System_Int16) + { + WriteByte(0x06); + } + else if (type == u.System_UInt16) + { + WriteByte(0x07); + } + else if (type == u.System_Int32) + { + WriteByte(0x08); + } + else if (type == u.System_UInt32) + { + WriteByte(0x09); + } + else if (type == u.System_Int64) + { + WriteByte(0x0A); + } + else if (type == u.System_UInt64) + { + WriteByte(0x0B); + } + else if (type == u.System_Single) + { + WriteByte(0x0C); + } + else if (type == u.System_Double) + { + WriteByte(0x0D); + } + else if (type == u.System_String) + { + WriteByte(0x0E); + } + else if (type.IsArray) + { + WriteByte(0x1D); + WriteFieldOrPropType(type.GetElementType()); + } + else if (type.IsEnum) + { + WriteByte(0x55); + WriteTypeName(type); + } + else + { + throw new ArgumentException(); + } + } + } + + internal ConstructorInfo Constructor + { + get { return con; } + } + + internal int WriteBlob(ModuleBuilder moduleBuilder) + { + ByteBuffer bb; + if (blob != null) + { + bb = ByteBuffer.Wrap(blob); + } + else + { + bb = new ByteBuffer(100); + BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); + bw.WriteCustomAttributeBlob(); + } + return moduleBuilder.Blobs.Add(bb); + } + + internal object GetConstructorArgument(int pos) + { + return constructorArgs[pos]; + } + + internal int ConstructorArgumentCount + { + get { return constructorArgs == null ? 0 : constructorArgs.Length; } + } + + internal T? GetFieldValue(string name) where T : struct + { + object val = GetFieldValue(name); + if (val is T) + { + return (T)val; + } + else if (val != null) + { + if (TypeUtil.IsEnum(typeof(T))) + { + Debug.Assert(Enum.GetUnderlyingType(typeof(T)) == val.GetType()); + return (T)Enum.ToObject(typeof(T), val); + } + else + { + Debug.Assert(Enum.GetUnderlyingType(val.GetType()) == typeof(T)); + return (T)Convert.ChangeType(val, typeof(T)); + } + } + else + { + return null; + } + } + + internal object GetFieldValue(string name) + { + if (namedFields != null) + { + for (int i = 0; i < namedFields.Length; i++) + { + if (namedFields[i].Name == name) + { + return fieldValues[i]; + } + } + } + return null; + } + + internal bool IsLegacyDeclSecurity + { + get + { + return ReferenceEquals(con, LegacyPermissionSet) + || (con.DeclaringType == con.Module.universe.System_Security_Permissions_PermissionSetAttribute + && blob == null + && (namedFields == null || namedFields.Length == 0) + && namedProperties != null + && namedProperties.Length == 1 + && namedProperties[0].Name == "XML" + && propertyValues[0] is string); + } + } + + internal int WriteLegacyDeclSecurityBlob(ModuleBuilder moduleBuilder) + { + if (blob != null) + { + return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(blob)); + } + else + { + return moduleBuilder.Blobs.Add(ByteBuffer.Wrap(Encoding.Unicode.GetBytes((string)propertyValues[0]))); + } + } + + internal void WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb) + { + if (blob != null) + { + bb.Write(blob); + } + else + { + BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb); + bw.WriteNamedArguments(true); + } + } + + internal CustomAttributeData ToData(Assembly asm) + { + if (blob != null) + { + if (constructorArgs != null) + { + return new CustomAttributeData(asm, con, (int)constructorArgs[0], blob, -1); + } + return new CustomAttributeData(asm, con, new IKVM.Reflection.Reader.ByteReader(blob, 0, blob.Length)); + } + else + { + List namedArgs = new List(); + if (namedProperties != null) + { + for (int i = 0; i < namedProperties.Length; i++) + { + namedArgs.Add(new CustomAttributeNamedArgument(namedProperties[i], RewrapValue(namedProperties[i].PropertyType, propertyValues[i]))); + } + } + if (namedFields != null) + { + for (int i = 0; i < namedFields.Length; i++) + { + namedArgs.Add(new CustomAttributeNamedArgument(namedFields[i], RewrapValue(namedFields[i].FieldType, fieldValues[i]))); + } + } + List args = new List(constructorArgs.Length); + ParameterInfo[] parameters = this.Constructor.GetParameters(); + for (int i = 0; i < constructorArgs.Length; i++) + { + args.Add(RewrapValue(parameters[i].ParameterType, constructorArgs[i])); + } + return new CustomAttributeData(asm.ManifestModule, con, args, namedArgs); + } + } + + private static CustomAttributeTypedArgument RewrapValue(Type type, object value) + { + if (value is Array) + { + Array array = (Array)value; + Type arrayType = type.Module.universe.Import(array.GetType()); + return RewrapArray(arrayType, array); + } + else if (value is CustomAttributeTypedArgument) + { + CustomAttributeTypedArgument arg = (CustomAttributeTypedArgument)value; + if (arg.Value is Array) + { + return RewrapArray(arg.ArgumentType, (Array)arg.Value); + } + return arg; + } + else + { + return new CustomAttributeTypedArgument(type, value); + } + } + + private static CustomAttributeTypedArgument RewrapArray(Type arrayType, Array array) + { + Type elementType = arrayType.GetElementType(); + CustomAttributeTypedArgument[] newArray = new CustomAttributeTypedArgument[array.Length]; + for (int i = 0; i < newArray.Length; i++) + { + newArray[i] = RewrapValue(elementType, array.GetValue(i)); + } + return new CustomAttributeTypedArgument(arrayType, newArray); + } + + internal bool HasBlob + { + get { return blob != null; } + } + + internal CustomAttributeBuilder DecodeBlob(Assembly asm) + { + if (blob == null) + { + return this; + } + else + { + return ToData(asm).__ToBuilder(); + } + } + + internal byte[] GetBlob(Assembly asm) + { + ByteBuffer bb = new ByteBuffer(100); + BlobWriter bw = new BlobWriter(asm, this, bb); + bw.WriteCustomAttributeBlob(); + return bb.ToArray(); + } + + internal KnownCA KnownCA + { + get + { + Type attributeType = con.DeclaringType; + if (attributeType.IsConstructedGenericType) + { + // a constructed generic type doesn't have a TypeName and we already know it's not a Known CA + return KnownCA.Unknown; + } + TypeName typeName = attributeType.TypeName; + switch (typeName.Namespace) + { + case "System": + switch (typeName.Name) + { + case "SerializableAttribute": + return KnownCA.SerializableAttribute; + case "NonSerializedAttribute": + return KnownCA.NonSerializedAttribute; + } + break; + case "System.Runtime.CompilerServices": + switch (typeName.Name) + { + case "MethodImplAttribute": + return KnownCA.MethodImplAttribute; + case "SpecialNameAttribute": + return KnownCA.SpecialNameAttribute; + } + break; + case "System.Runtime.InteropServices": + switch (typeName.Name) + { + case "DllImportAttribute": + return KnownCA.DllImportAttribute; + case "ComImportAttribute": + return KnownCA.ComImportAttribute; + case "MarshalAsAttribute": + return KnownCA.MarshalAsAttribute; + case "PreserveSigAttribute": + return KnownCA.PreserveSigAttribute; + case "InAttribute": + return KnownCA.InAttribute; + case "OutAttribute": + return KnownCA.OutAttribute; + case "OptionalAttribute": + return KnownCA.OptionalAttribute; + case "StructLayoutAttribute": + return KnownCA.StructLayoutAttribute; + case "FieldOffsetAttribute": + return KnownCA.FieldOffsetAttribute; + } + break; + } + if (typeName.Matches("System.Security.SuppressUnmanagedCodeSecurityAttribute")) + { + return KnownCA.SuppressUnmanagedCodeSecurityAttribute; + } + return KnownCA.Unknown; + } + } + } + + // These are the pseudo-custom attributes that are recognized by name by the runtime (i.e. the type identity is not considered). + // The corresponding list in the runtime is at https://github.com/dotnet/coreclr/blob/1afe5ce4f45045d724a4e129df4b816655d486fb/src/md/compiler/custattr_emit.cpp#L38 + // Note that we only need to handle a subset of the types, since we don't need the ones that are only used for validation by the runtime. + enum KnownCA + { + Unknown, + DllImportAttribute, + ComImportAttribute, + SerializableAttribute, + NonSerializedAttribute, + MethodImplAttribute, + MarshalAsAttribute, + PreserveSigAttribute, + InAttribute, + OutAttribute, + OptionalAttribute, + StructLayoutAttribute, + FieldOffsetAttribute, + SpecialNameAttribute, + // the following is not part of the runtime known custom attributes, but we handle it here for efficiency and convenience + SuppressUnmanagedCodeSecurityAttribute, + } } diff --git a/src/IKVM.Reflection/Emit/ILGenerator.cs b/src/IKVM.Reflection/Emit/ILGenerator.cs index 3028615d22..77ce5db7a2 100644 --- a/src/IKVM.Reflection/Emit/ILGenerator.cs +++ b/src/IKVM.Reflection/Emit/ILGenerator.cs @@ -32,1114 +32,1115 @@ Jeroen Frijters namespace IKVM.Reflection.Emit { public struct Label - { - // 1-based here, to make sure that an uninitialized Label isn't valid - private readonly int index1; - - internal Label(int index) - { - this.index1 = index + 1; - } - - internal int Index - { - get { return index1 - 1; } - } - - public bool Equals(Label other) - { - return other.index1 == index1; - } - - public override bool Equals(object obj) - { - return this == obj as Label?; - } - - public override int GetHashCode() - { - return index1; - } - - public static bool operator ==(Label arg1, Label arg2) - { - return arg1.index1 == arg2.index1; - } - - public static bool operator !=(Label arg1, Label arg2) - { - return !(arg1 == arg2); - } - } - - public sealed class LocalBuilder : LocalVariableInfo - { - internal string name; - internal int startOffset; - internal int endOffset; - - internal LocalBuilder(Type localType, int index, bool pinned) - : base(index, localType, pinned) - { - } - - internal LocalBuilder(Type localType, int index, bool pinned, CustomModifiers customModifiers) - : base(index, localType, pinned, customModifiers) - { - } - - public void SetLocalSymInfo(string name) - { - this.name = name; - } - - public void SetLocalSymInfo(string name, int startOffset, int endOffset) - { - this.name = name; - this.startOffset = startOffset; - this.endOffset = endOffset; - } - } - - public sealed class ILGenerator - { - - private readonly ModuleBuilder moduleBuilder; - private readonly ByteBuffer code; - private readonly SignatureHelper locals; - private int localsCount; - private readonly List tokenFixups = new List(); - private readonly List labels = new List(); - private readonly List labelStackHeight = new List(); - private readonly List labelFixups = new List(); - private readonly List sequencePoints = new List(); - private readonly List exceptions = new List(); - private readonly Stack exceptionStack = new Stack(); - private ushort maxStack; - private bool fatHeader; - private int stackHeight; - private Scope scope; - private byte exceptionBlockAssistanceMode = EBAM_COMPAT; - private const byte EBAM_COMPAT = 0; - private const byte EBAM_DISABLE = 1; - private const byte EBAM_CLEVER = 2; - - private struct LabelFixup - { - internal int label; - internal int offset; - } - - internal sealed class ExceptionBlock : IComparer - { - internal readonly int ordinal; - internal Label labelEnd; - internal int tryOffset; - internal int tryLength; - internal int handlerOffset; - internal int handlerLength; - internal int filterOffsetOrExceptionTypeToken; - internal ExceptionHandlingClauseOptions kind; - - internal ExceptionBlock(int ordinal) - { - this.ordinal = ordinal; - } - - internal ExceptionBlock(ExceptionHandler h) - { - this.ordinal = -1; - this.tryOffset = h.TryOffset; - this.tryLength = h.TryLength; - this.handlerOffset = h.HandlerOffset; - this.handlerLength = h.HandlerLength; - this.kind = h.Kind; - this.filterOffsetOrExceptionTypeToken = kind == ExceptionHandlingClauseOptions.Filter ? h.FilterOffset : h.ExceptionTypeToken; - } - - int IComparer.Compare(ExceptionBlock x, ExceptionBlock y) - { - // Mono's sort insists on doing unnecessary comparisons - if (x == y) - { - return 0; - } - else if (x.tryOffset == y.tryOffset && x.tryLength == y.tryLength) - { - return x.ordinal < y.ordinal ? -1 : 1; - } - else if (x.tryOffset >= y.tryOffset && x.handlerOffset + x.handlerLength <= y.handlerOffset + y.handlerLength) - { - return -1; - } - else if (y.tryOffset >= x.tryOffset && y.handlerOffset + y.handlerLength <= x.handlerOffset + x.handlerLength) - { - return 1; - } - else - { - return x.ordinal < y.ordinal ? -1 : 1; - } - } - } - - private struct SequencePoint - { - internal ISymbolDocumentWriter document; - internal int offset; - internal int startLine; - internal int startColumn; - internal int endLine; - internal int endColumn; - } - - private sealed class Scope - { - internal readonly Scope parent; - internal readonly List children = new List(); - internal readonly List locals = new List(); - internal readonly List namespaces = new List(); - internal int startOffset; - internal int endOffset; - - internal Scope(Scope parent) - { - this.parent = parent; - } - } - - internal ILGenerator(ModuleBuilder moduleBuilder, int initialCapacity) - { - this.code = new ByteBuffer(initialCapacity); - this.moduleBuilder = moduleBuilder; - this.locals = SignatureHelper.GetLocalVarSigHelper(moduleBuilder); - if (moduleBuilder.symbolWriter != null) - { - scope = new Scope(null); - } - } - - // non-standard API - public void __DisableExceptionBlockAssistance() - { - exceptionBlockAssistanceMode = EBAM_DISABLE; - } - - // non-standard API - public void __CleverExceptionBlockAssistance() - { - exceptionBlockAssistanceMode = EBAM_CLEVER; - } - - // non-standard API - public int __MaxStackSize - { - get { return maxStack; } - set - { - maxStack = (ushort)value; - fatHeader = true; - } - } - - // non-standard API - // returns -1 if the current position is currently unreachable - public int __StackHeight - { - get { return stackHeight; } - } - - // new in .NET 4.0 - public int ILOffset - { - get { return code.Position; } - } - - public void BeginCatchBlock(Type exceptionType) - { - if (exceptionType == null) - { - // this must be a catch block after a filter - ExceptionBlock block = exceptionStack.Peek(); - if (block.kind != ExceptionHandlingClauseOptions.Filter || block.handlerOffset != 0) - { - throw new ArgumentNullException("exceptionType"); - } - if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) - { - Emit(OpCodes.Endfilter); - } - stackHeight = 0; - UpdateStack(1); - block.handlerOffset = code.Position; - } - else - { - ExceptionBlock block = BeginCatchOrFilterBlock(); - block.kind = ExceptionHandlingClauseOptions.Clause; - block.filterOffsetOrExceptionTypeToken = moduleBuilder.GetTypeTokenForMemberRef(exceptionType); - block.handlerOffset = code.Position; - } - } - - private ExceptionBlock BeginCatchOrFilterBlock() - { - ExceptionBlock block = exceptionStack.Peek(); - if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) - { - Emit(OpCodes.Leave, block.labelEnd); - } - stackHeight = 0; - UpdateStack(1); - if (block.tryLength == 0) - { - block.tryLength = code.Position - block.tryOffset; - } - else - { - block.handlerLength = code.Position - block.handlerOffset; - exceptionStack.Pop(); - ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count); - newBlock.labelEnd = block.labelEnd; - newBlock.tryOffset = block.tryOffset; - newBlock.tryLength = block.tryLength; - block = newBlock; - exceptions.Add(block); - exceptionStack.Push(block); - } - return block; - } - - public Label BeginExceptionBlock() - { - ExceptionBlock block = new ExceptionBlock(exceptions.Count); - block.labelEnd = DefineLabel(); - block.tryOffset = code.Position; - exceptionStack.Push(block); - exceptions.Add(block); - stackHeight = 0; - return block.labelEnd; - } - - public void BeginExceptFilterBlock() - { - ExceptionBlock block = BeginCatchOrFilterBlock(); - block.kind = ExceptionHandlingClauseOptions.Filter; - block.filterOffsetOrExceptionTypeToken = code.Position; - } - - public void BeginFaultBlock() - { - BeginFinallyFaultBlock(ExceptionHandlingClauseOptions.Fault); - } - - public void BeginFinallyBlock() - { - BeginFinallyFaultBlock(ExceptionHandlingClauseOptions.Finally); - } - - private void BeginFinallyFaultBlock(ExceptionHandlingClauseOptions kind) - { - ExceptionBlock block = exceptionStack.Peek(); - if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) - { - Emit(OpCodes.Leave, block.labelEnd); - } - if (block.handlerOffset == 0) - { - block.tryLength = code.Position - block.tryOffset; - } - else - { - block.handlerLength = code.Position - block.handlerOffset; - Label labelEnd; - if (exceptionBlockAssistanceMode != EBAM_COMPAT) - { - labelEnd = block.labelEnd; - } - else - { - MarkLabel(block.labelEnd); - labelEnd = DefineLabel(); - Emit(OpCodes.Leave, labelEnd); - } - exceptionStack.Pop(); - ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count); - newBlock.labelEnd = labelEnd; - newBlock.tryOffset = block.tryOffset; - newBlock.tryLength = code.Position - block.tryOffset; - block = newBlock; - exceptions.Add(block); - exceptionStack.Push(block); - } - block.handlerOffset = code.Position; - block.kind = kind; - stackHeight = 0; - } - - public void EndExceptionBlock() - { - ExceptionBlock block = exceptionStack.Pop(); - if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) - { - if (block.kind != ExceptionHandlingClauseOptions.Finally && block.kind != ExceptionHandlingClauseOptions.Fault) - { - Emit(OpCodes.Leave, block.labelEnd); - } - else - { - Emit(OpCodes.Endfinally); - } - } - MarkLabel(block.labelEnd); - block.handlerLength = code.Position - block.handlerOffset; - } - - public void BeginScope() - { - Scope newScope = new Scope(scope); - scope.children.Add(newScope); - scope = newScope; - scope.startOffset = code.Position; - } - - public void UsingNamespace(string usingNamespace) - { - if (scope != null) - { - scope.namespaces.Add(usingNamespace); - } - } - - public LocalBuilder DeclareLocal(Type localType) - { - return DeclareLocal(localType, false); - } - - public LocalBuilder DeclareLocal(Type localType, bool pinned) - { - LocalBuilder local = new LocalBuilder(localType, localsCount++, pinned); - locals.AddArgument(localType, pinned); - if (scope != null) - { - scope.locals.Add(local); - } - return local; - } - - public LocalBuilder __DeclareLocal(Type localType, bool pinned, CustomModifiers customModifiers) - { - LocalBuilder local = new LocalBuilder(localType, localsCount++, pinned, customModifiers); - locals.__AddArgument(localType, pinned, customModifiers); - if (scope != null) - { - scope.locals.Add(local); - } - return local; - } - - public Label DefineLabel() - { - Label label = new Label(labels.Count); - labels.Add(-1); - labelStackHeight.Add(-1); - return label; - } - - public void Emit(OpCode opc) - { - Debug.Assert(opc != OpCodes.Ret || (opc == OpCodes.Ret && stackHeight <= 1)); - if (opc.Value < 0) - { - code.Write((byte)(opc.Value >> 8)); - } - code.Write((byte)opc.Value); - switch (opc.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Break: - case FlowControl.Return: - case FlowControl.Throw: - stackHeight = -1; - break; - default: - UpdateStack(opc.StackDiff); - break; - } - } - - private void UpdateStack(int stackdiff) - { - if (stackHeight == -1) - { - // we're about to emit code that is either unreachable or reachable only via a backward branch - stackHeight = 0; - } - Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue); - stackHeight += stackdiff; - Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue); - maxStack = Math.Max(maxStack, (ushort)stackHeight); - } - - public void Emit(OpCode opc, byte arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, double arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, FieldInfo field) - { - Emit(opc); - WriteToken(moduleBuilder.GetFieldToken(field).Token); - } - - public void Emit(OpCode opc, short arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, int arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, long arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, Label label) - { - // We need special stackHeight handling for unconditional branches, - // because the branch and next flows have differing stack heights. - // Note that this assumes that unconditional branches do not push/pop. - int flowStackHeight = this.stackHeight; - Emit(opc); - if (opc == OpCodes.Leave || opc == OpCodes.Leave_S) - { - flowStackHeight = 0; - } - else if (opc.FlowControl != FlowControl.Branch) - { - flowStackHeight = this.stackHeight; - } - // if the label has already been marked, we can emit the branch offset directly - if (labels[label.Index] != -1) - { - if (labelStackHeight[label.Index] != flowStackHeight && (labelStackHeight[label.Index] != 0 || flowStackHeight != -1)) - { - // the "backward branch constraint" prohibits this, so we don't need to support it - throw new NotSupportedException("'Backward branch constraints' violated"); - } - if (opc.OperandType == OperandType.ShortInlineBrTarget) - { - WriteByteBranchOffset(labels[label.Index] - (code.Position + 1)); - } - else - { - code.Write(labels[label.Index] - (code.Position + 4)); - } - } - else - { - Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == flowStackHeight || (flowStackHeight == -1 && labelStackHeight[label.Index] == 0)); - labelStackHeight[label.Index] = flowStackHeight; - LabelFixup fix = new LabelFixup(); - fix.label = label.Index; - fix.offset = code.Position; - labelFixups.Add(fix); - if (opc.OperandType == OperandType.ShortInlineBrTarget) - { - code.Write((byte)1); - } - else - { - code.Write(4); - } - } - } - - private void WriteByteBranchOffset(int offset) - { - if (offset < -128 || offset > 127) - { - throw new NotSupportedException("Branch offset of " + offset + " does not fit in one-byte branch target at position " + code.Position); - } - code.Write((byte)offset); - } - - public void Emit(OpCode opc, Label[] labels) - { - Emit(opc); - LabelFixup fix = new LabelFixup(); - fix.label = -1; - fix.offset = code.Position; - labelFixups.Add(fix); - code.Write(labels.Length); - foreach (Label label in labels) - { - code.Write(label.Index); - if (this.labels[label.Index] != -1) - { - if (labelStackHeight[label.Index] != stackHeight) - { - // the "backward branch constraint" prohibits this, so we don't need to support it - throw new NotSupportedException(); - } - } - else - { - Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == stackHeight); - labelStackHeight[label.Index] = stackHeight; - } - } - } - - public void Emit(OpCode opc, LocalBuilder local) - { - if ((opc == OpCodes.Ldloc || opc == OpCodes.Ldloca || opc == OpCodes.Stloc) && local.LocalIndex < 256) - { - if (opc == OpCodes.Ldloc) - { - switch (local.LocalIndex) - { - case 0: - Emit(OpCodes.Ldloc_0); - break; - case 1: - Emit(OpCodes.Ldloc_1); - break; - case 2: - Emit(OpCodes.Ldloc_2); - break; - case 3: - Emit(OpCodes.Ldloc_3); - break; - default: - Emit(OpCodes.Ldloc_S); - code.Write((byte)local.LocalIndex); - break; - } - } - else if (opc == OpCodes.Ldloca) - { - Emit(OpCodes.Ldloca_S); - code.Write((byte)local.LocalIndex); - } - else if (opc == OpCodes.Stloc) - { - switch (local.LocalIndex) - { - case 0: - Emit(OpCodes.Stloc_0); - break; - case 1: - Emit(OpCodes.Stloc_1); - break; - case 2: - Emit(OpCodes.Stloc_2); - break; - case 3: - Emit(OpCodes.Stloc_3); - break; - default: - Emit(OpCodes.Stloc_S); - code.Write((byte)local.LocalIndex); - break; - } - } - } - else - { - Emit(opc); - switch (opc.OperandType) - { - case OperandType.InlineVar: - code.Write((ushort)local.LocalIndex); - break; - case OperandType.ShortInlineVar: - code.Write((byte)local.LocalIndex); - break; - } - } - } - - private void WriteToken(int token) - { - if (ModuleBuilder.IsPseudoToken(token)) - { - tokenFixups.Add(code.Position); - } - code.Write(token); - } - - private void UpdateStack(OpCode opc, bool hasthis, Type returnType, int parameterCount) - { - if (opc == OpCodes.Jmp) - { - stackHeight = -1; - } - else if (opc.FlowControl == FlowControl.Call) - { - int stackdiff = 0; - if ((hasthis && opc != OpCodes.Newobj) || opc == OpCodes.Calli) - { - // pop this - stackdiff--; - } - // pop parameters - stackdiff -= parameterCount; - if (returnType != moduleBuilder.universe.System_Void) - { - // push return value - stackdiff++; - } - UpdateStack(stackdiff); - } - } - - public void Emit(OpCode opc, MethodInfo method) - { - UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount); - Emit(opc); - WriteToken(moduleBuilder.GetMethodTokenForIL(method).Token); - } - - public void Emit(OpCode opc, ConstructorInfo constructor) - { - Emit(opc, constructor.GetMethodInfo()); - } - - public void Emit(OpCode opc, sbyte arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, float arg) - { - Emit(opc); - code.Write(arg); - } - - public void Emit(OpCode opc, string str) - { - Emit(opc); - code.Write(moduleBuilder.GetStringConstant(str).Token); - } - - public void Emit(OpCode opc, Type type) - { - Emit(opc); - if (opc == OpCodes.Ldtoken) - { - code.Write(moduleBuilder.GetTypeToken(type).Token); - } - else - { - code.Write(moduleBuilder.GetTypeTokenForMemberRef(type)); - } - } - - public void Emit(OpCode opcode, SignatureHelper signature) - { - Emit(opcode); - UpdateStack(opcode, signature.HasThis, signature.ReturnType, signature.ParameterCount); - code.Write(moduleBuilder.GetSignatureToken(signature).Token); - } - - public void EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes) - { - __EmitCall(opc, method, optionalParameterTypes, null); - } - - public void __EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) - { - if (optionalParameterTypes == null || optionalParameterTypes.Length == 0) - { - Emit(opc, method); - } - else - { - Emit(opc); - UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount + optionalParameterTypes.Length); - code.Write(moduleBuilder.__GetMethodToken(method, optionalParameterTypes, customModifiers).Token); - } - } - - public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes) - { - EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes); - } - - public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) - { - __EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes, customModifiers); - } - - public void EmitCalli(OpCode opc, CallingConvention callingConvention, Type returnType, Type[] parameterTypes) - { - SignatureHelper sig = SignatureHelper.GetMethodSigHelper(moduleBuilder, callingConvention, returnType); - sig.AddArguments(parameterTypes, null, null); - Emit(opc, sig); - } - - public void EmitCalli(OpCode opc, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) - { - SignatureHelper sig = SignatureHelper.GetMethodSigHelper(moduleBuilder, callingConvention, returnType); - sig.AddArguments(parameterTypes, null, null); - if (optionalParameterTypes != null && optionalParameterTypes.Length != 0) - { - sig.AddSentinel(); - sig.AddArguments(optionalParameterTypes, null, null); - } - Emit(opc, sig); - } - - public void __EmitCalli(OpCode opc, __StandAloneMethodSig sig) - { - Emit(opc); - if (sig.IsUnmanaged) - { - UpdateStack(opc, false, sig.ReturnType, sig.ParameterCount); - } - else - { - CallingConventions callingConvention = sig.CallingConvention; - UpdateStack(opc, (callingConvention & CallingConventions.HasThis | CallingConventions.ExplicitThis) == CallingConventions.HasThis, sig.ReturnType, sig.ParameterCount); - } - ByteBuffer bb = new ByteBuffer(16); - Signature.WriteStandAloneMethodSig(moduleBuilder, bb, sig); - code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(bb))); - } - - public void EmitWriteLine(string text) - { - Universe u = moduleBuilder.universe; - Emit(OpCodes.Ldstr, text); - Emit(OpCodes.Call, u.System_Console.GetMethod("WriteLine", new Type[] { u.System_String })); - } - - public void EmitWriteLine(FieldInfo field) - { - Universe u = moduleBuilder.universe; - Emit(OpCodes.Call, u.System_Console.GetMethod("get_Out")); - if (field.IsStatic) - { - Emit(OpCodes.Ldsfld, field); - } - else - { - Emit(OpCodes.Ldarg_0); - Emit(OpCodes.Ldfld, field); - } - Emit(OpCodes.Callvirt, u.System_IO_TextWriter.GetMethod("WriteLine", new Type[] { field.FieldType })); - } - - public void EmitWriteLine(LocalBuilder local) - { - Universe u = moduleBuilder.universe; - Emit(OpCodes.Call, u.System_Console.GetMethod("get_Out")); - Emit(OpCodes.Ldloc, local); - Emit(OpCodes.Callvirt, u.System_IO_TextWriter.GetMethod("WriteLine", new Type[] { local.LocalType })); - } - - public void EndScope() - { - scope.endOffset = code.Position; - scope = scope.parent; - } - - public void MarkLabel(Label loc) - { - Debug.Assert(stackHeight == -1 || labelStackHeight[loc.Index] == -1 || stackHeight == labelStackHeight[loc.Index]); - labels[loc.Index] = code.Position; - if (labelStackHeight[loc.Index] == -1) - { - if (stackHeight == -1) - { - // We're at a location that can only be reached by a backward branch, - // so according to the "backward branch constraint" that must mean the stack is empty, - // but note that this may be an unused label followed by another label that is used and - // that does have a non-zero stack height, so we don't yet set stackHeight here. - labelStackHeight[loc.Index] = 0; - } - else - { - labelStackHeight[loc.Index] = stackHeight; - } - } - else - { - Debug.Assert(stackHeight == -1 || stackHeight == labelStackHeight[loc.Index]); - stackHeight = labelStackHeight[loc.Index]; - } - } - - public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) - { - SequencePoint sp = new SequencePoint(); - sp.document = document; - sp.offset = code.Position; - sp.startLine = startLine; - sp.startColumn = startColumn; - sp.endLine = endLine; - sp.endColumn = endColumn; - sequencePoints.Add(sp); - } - - public void ThrowException(Type excType) - { - Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes)); - Emit(OpCodes.Throw); - } - - internal int WriteBody(bool initLocals) - { - if (moduleBuilder.symbolWriter != null) - { - Debug.Assert(scope != null && scope.parent == null); - scope.endOffset = code.Position; - } - - ResolveBranches(); - - ByteBuffer bb = moduleBuilder.methodBodies; - - int localVarSigTok = 0; - - int rva; - if (localsCount == 0 && exceptions.Count == 0 && maxStack <= 8 && code.Length < 64 && !fatHeader) - { - rva = WriteTinyHeaderAndCode(bb); - } - else - { - if (localsCount != 0) - { - localVarSigTok = moduleBuilder.GetSignatureToken(locals).Token; - } - rva = WriteFatHeaderAndCode(bb, localVarSigTok, initLocals); - } - - if (moduleBuilder.symbolWriter != null) - { - if (sequencePoints.Count != 0) - { - ISymbolDocumentWriter document = sequencePoints[0].document; - int[] offsets = new int[sequencePoints.Count]; - int[] lines = new int[sequencePoints.Count]; - int[] columns = new int[sequencePoints.Count]; - int[] endLines = new int[sequencePoints.Count]; - int[] endColumns = new int[sequencePoints.Count]; - for (int i = 0; i < sequencePoints.Count; i++) - { - if (sequencePoints[i].document != document) - { - throw new NotImplementedException(); - } - offsets[i] = sequencePoints[i].offset; - lines[i] = sequencePoints[i].startLine; - columns[i] = sequencePoints[i].startColumn; - endLines[i] = sequencePoints[i].endLine; - endColumns[i] = sequencePoints[i].endColumn; - } - moduleBuilder.symbolWriter.DefineSequencePoints(document, offsets, lines, columns, endLines, endColumns); - } - - WriteScope(scope, localVarSigTok); - } - return rva; - } - - private void ResolveBranches() - { - foreach (LabelFixup fixup in labelFixups) - { - // is it a switch? - if (fixup.label == -1) - { - code.Position = fixup.offset; - int count = code.GetInt32AtCurrentPosition(); - int offset = fixup.offset + 4 + 4 * count; - code.Position += 4; - for (int i = 0; i < count; i++) - { - int index = code.GetInt32AtCurrentPosition(); - code.Write(labels[index] - offset); - } - } - else - { - code.Position = fixup.offset; - byte size = code.GetByteAtCurrentPosition(); - int branchOffset = labels[fixup.label] - (code.Position + size); - if (size == 1) - { - WriteByteBranchOffset(branchOffset); - } - else - { - code.Write(branchOffset); - } - } - } - } - - internal static void WriteTinyHeader(ByteBuffer bb, int length) - { - const byte CorILMethod_TinyFormat = 0x2; - bb.Write((byte)(CorILMethod_TinyFormat | (length << 2))); - } - - private int WriteTinyHeaderAndCode(ByteBuffer bb) - { - int rva = bb.Position; - WriteTinyHeader(bb, code.Length); - AddTokenFixups(bb.Position, moduleBuilder.tokenFixupOffsets, tokenFixups); - bb.Write(code); - return rva; - } - - internal static void WriteFatHeader(ByteBuffer bb, bool initLocals, bool exceptions, ushort maxStack, int codeLength, int localVarSigTok) - { - const byte CorILMethod_FatFormat = 0x03; - const byte CorILMethod_MoreSects = 0x08; - const byte CorILMethod_InitLocals = 0x10; - - short flagsAndSize = (short)(CorILMethod_FatFormat | (3 << 12)); - if (initLocals) - { - flagsAndSize |= CorILMethod_InitLocals; - } - - if (exceptions) - { - flagsAndSize |= CorILMethod_MoreSects; - } - - bb.Write(flagsAndSize); - bb.Write(maxStack); - bb.Write(codeLength); - bb.Write(localVarSigTok); - } - - private int WriteFatHeaderAndCode(ByteBuffer bb, int localVarSigTok, bool initLocals) - { - // fat headers require 4-byte alignment - bb.Align(4); - int rva = bb.Position; - WriteFatHeader(bb, initLocals, exceptions.Count > 0, maxStack, code.Length, localVarSigTok); - AddTokenFixups(bb.Position, moduleBuilder.tokenFixupOffsets, tokenFixups); - bb.Write(code); - if (exceptions.Count > 0) - { - exceptions.Sort(exceptions[0]); - WriteExceptionHandlers(bb, exceptions); - } - return rva; - } - - internal static void WriteExceptionHandlers(ByteBuffer bb, List exceptions) - { - bb.Align(4); - - bool fat = false; - if (exceptions.Count * 12 + 4 > 255) - { - fat = true; - } - else - { - foreach (ExceptionBlock block in exceptions) - { - if (block.tryOffset > 65535 || block.tryLength > 255 || block.handlerOffset > 65535 || block.handlerLength > 255) - { - fat = true; - break; - } - } - } - - const byte CorILMethod_Sect_EHTable = 0x1; - const byte CorILMethod_Sect_FatFormat = 0x40; - - if (fat) - { - bb.Write((byte)(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat)); - int dataSize = exceptions.Count * 24 + 4; - bb.Write((byte)dataSize); - bb.Write((short)(dataSize >> 8)); - foreach (ExceptionBlock block in exceptions) - { - bb.Write((int)block.kind); - bb.Write(block.tryOffset); - bb.Write(block.tryLength); - bb.Write(block.handlerOffset); - bb.Write(block.handlerLength); - bb.Write(block.filterOffsetOrExceptionTypeToken); - } - } - else - { - bb.Write(CorILMethod_Sect_EHTable); - bb.Write((byte)(exceptions.Count * 12 + 4)); - bb.Write((short)0); - foreach (ExceptionBlock block in exceptions) - { - bb.Write((short)block.kind); - bb.Write((short)block.tryOffset); - bb.Write((byte)block.tryLength); - bb.Write((short)block.handlerOffset); - bb.Write((byte)block.handlerLength); - bb.Write(block.filterOffsetOrExceptionTypeToken); - } - } - } - - internal static void AddTokenFixups(int codeOffset, List tokenFixupOffsets, IEnumerable tokenFixups) - { - foreach (int fixup in tokenFixups) - { - tokenFixupOffsets.Add(fixup + codeOffset); - } - } - - private void WriteScope(Scope scope, int localVarSigTok) - { - moduleBuilder.symbolWriter.OpenScope(scope.startOffset); - foreach (LocalBuilder local in scope.locals) - { - if (local.name != null) - { - int startOffset = local.startOffset; - int endOffset = local.endOffset; - if (startOffset == 0 && endOffset == 0) - { - startOffset = scope.startOffset; - endOffset = scope.endOffset; - } - moduleBuilder.symbolWriter.DefineLocalVariable2(local.name, 0, localVarSigTok, SymAddressKind.ILOffset, local.LocalIndex, 0, 0, startOffset, endOffset); - } - } - foreach (string ns in scope.namespaces) - { - moduleBuilder.symbolWriter.UsingNamespace(ns); - } - foreach (Scope child in scope.children) - { - WriteScope(child, localVarSigTok); - } - moduleBuilder.symbolWriter.CloseScope(scope.endOffset); - } - } + { + // 1-based here, to make sure that an uninitialized Label isn't valid + private readonly int index1; + + internal Label(int index) + { + this.index1 = index + 1; + } + + internal int Index + { + get { return index1 - 1; } + } + + public bool Equals(Label other) + { + return other.index1 == index1; + } + + public override bool Equals(object obj) + { + return this == obj as Label?; + } + + public override int GetHashCode() + { + return index1; + } + + public static bool operator ==(Label arg1, Label arg2) + { + return arg1.index1 == arg2.index1; + } + + public static bool operator !=(Label arg1, Label arg2) + { + return !(arg1 == arg2); + } + } + + public sealed class LocalBuilder : LocalVariableInfo + { + internal string name; + internal int startOffset; + internal int endOffset; + + internal LocalBuilder(Type localType, int index, bool pinned) + : base(index, localType, pinned) + { + } + + internal LocalBuilder(Type localType, int index, bool pinned, CustomModifiers customModifiers) + : base(index, localType, pinned, customModifiers) + { + } + + public void SetLocalSymInfo(string name) + { + this.name = name; + } + + public void SetLocalSymInfo(string name, int startOffset, int endOffset) + { + this.name = name; + this.startOffset = startOffset; + this.endOffset = endOffset; + } + } + + public sealed class ILGenerator + { + + private readonly ModuleBuilder moduleBuilder; + private readonly ByteBuffer code; + private readonly SignatureHelper locals; + private int localsCount; + private readonly List tokenFixups = new List(); + private readonly List labels = new List(); + private readonly List labelStackHeight = new List(); + private readonly List labelFixups = new List(); + private readonly List sequencePoints = new List(); + private readonly List exceptions = new List(); + private readonly Stack exceptionStack = new Stack(); + private ushort maxStack; + private bool fatHeader; + private int stackHeight; + private Scope scope; + private byte exceptionBlockAssistanceMode = EBAM_COMPAT; + private const byte EBAM_COMPAT = 0; + private const byte EBAM_DISABLE = 1; + private const byte EBAM_CLEVER = 2; + + private struct LabelFixup + { + internal int label; + internal int offset; + } + + internal sealed class ExceptionBlock : IComparer + { + internal readonly int ordinal; + internal Label labelEnd; + internal int tryOffset; + internal int tryLength; + internal int handlerOffset; + internal int handlerLength; + internal int filterOffsetOrExceptionTypeToken; + internal ExceptionHandlingClauseOptions kind; + + internal ExceptionBlock(int ordinal) + { + this.ordinal = ordinal; + } + + internal ExceptionBlock(ExceptionHandler h) + { + this.ordinal = -1; + this.tryOffset = h.TryOffset; + this.tryLength = h.TryLength; + this.handlerOffset = h.HandlerOffset; + this.handlerLength = h.HandlerLength; + this.kind = h.Kind; + this.filterOffsetOrExceptionTypeToken = kind == ExceptionHandlingClauseOptions.Filter ? h.FilterOffset : h.ExceptionTypeToken; + } + + int IComparer.Compare(ExceptionBlock x, ExceptionBlock y) + { + // Mono's sort insists on doing unnecessary comparisons + if (x == y) + { + return 0; + } + else if (x.tryOffset == y.tryOffset && x.tryLength == y.tryLength) + { + return x.ordinal < y.ordinal ? -1 : 1; + } + else if (x.tryOffset >= y.tryOffset && x.handlerOffset + x.handlerLength <= y.handlerOffset + y.handlerLength) + { + return -1; + } + else if (y.tryOffset >= x.tryOffset && y.handlerOffset + y.handlerLength <= x.handlerOffset + x.handlerLength) + { + return 1; + } + else + { + return x.ordinal < y.ordinal ? -1 : 1; + } + } + } + + private struct SequencePoint + { + internal ISymbolDocumentWriter document; + internal int offset; + internal int startLine; + internal int startColumn; + internal int endLine; + internal int endColumn; + } + + private sealed class Scope + { + internal readonly Scope parent; + internal readonly List children = new List(); + internal readonly List locals = new List(); + internal readonly List namespaces = new List(); + internal int startOffset; + internal int endOffset; + + internal Scope(Scope parent) + { + this.parent = parent; + } + } + + internal ILGenerator(ModuleBuilder moduleBuilder, int initialCapacity) + { + this.code = new ByteBuffer(initialCapacity); + this.moduleBuilder = moduleBuilder; + this.locals = SignatureHelper.GetLocalVarSigHelper(moduleBuilder); + if (moduleBuilder.symbolWriter != null) + { + scope = new Scope(null); + } + } + + // non-standard API + public void __DisableExceptionBlockAssistance() + { + exceptionBlockAssistanceMode = EBAM_DISABLE; + } + + // non-standard API + public void __CleverExceptionBlockAssistance() + { + exceptionBlockAssistanceMode = EBAM_CLEVER; + } + + // non-standard API + public int __MaxStackSize + { + get { return maxStack; } + set + { + maxStack = (ushort)value; + fatHeader = true; + } + } + + // non-standard API + // returns -1 if the current position is currently unreachable + public int __StackHeight + { + get { return stackHeight; } + } + + // new in .NET 4.0 + public int ILOffset + { + get { return code.Position; } + } + + public void BeginCatchBlock(Type exceptionType) + { + if (exceptionType == null) + { + // this must be a catch block after a filter + ExceptionBlock block = exceptionStack.Peek(); + if (block.kind != ExceptionHandlingClauseOptions.Filter || block.handlerOffset != 0) + { + throw new ArgumentNullException("exceptionType"); + } + if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) + { + Emit(OpCodes.Endfilter); + } + stackHeight = 0; + UpdateStack(1); + block.handlerOffset = code.Position; + } + else + { + ExceptionBlock block = BeginCatchOrFilterBlock(); + block.kind = ExceptionHandlingClauseOptions.Clause; + block.filterOffsetOrExceptionTypeToken = moduleBuilder.GetTypeTokenForMemberRef(exceptionType); + block.handlerOffset = code.Position; + } + } + + private ExceptionBlock BeginCatchOrFilterBlock() + { + ExceptionBlock block = exceptionStack.Peek(); + if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) + { + Emit(OpCodes.Leave, block.labelEnd); + } + stackHeight = 0; + UpdateStack(1); + if (block.tryLength == 0) + { + block.tryLength = code.Position - block.tryOffset; + } + else + { + block.handlerLength = code.Position - block.handlerOffset; + exceptionStack.Pop(); + ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count); + newBlock.labelEnd = block.labelEnd; + newBlock.tryOffset = block.tryOffset; + newBlock.tryLength = block.tryLength; + block = newBlock; + exceptions.Add(block); + exceptionStack.Push(block); + } + return block; + } + + public Label BeginExceptionBlock() + { + ExceptionBlock block = new ExceptionBlock(exceptions.Count); + block.labelEnd = DefineLabel(); + block.tryOffset = code.Position; + exceptionStack.Push(block); + exceptions.Add(block); + stackHeight = 0; + return block.labelEnd; + } + + public void BeginExceptFilterBlock() + { + ExceptionBlock block = BeginCatchOrFilterBlock(); + block.kind = ExceptionHandlingClauseOptions.Filter; + block.filterOffsetOrExceptionTypeToken = code.Position; + } + + public void BeginFaultBlock() + { + BeginFinallyFaultBlock(ExceptionHandlingClauseOptions.Fault); + } + + public void BeginFinallyBlock() + { + BeginFinallyFaultBlock(ExceptionHandlingClauseOptions.Finally); + } + + private void BeginFinallyFaultBlock(ExceptionHandlingClauseOptions kind) + { + ExceptionBlock block = exceptionStack.Peek(); + if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) + { + Emit(OpCodes.Leave, block.labelEnd); + } + if (block.handlerOffset == 0) + { + block.tryLength = code.Position - block.tryOffset; + } + else + { + block.handlerLength = code.Position - block.handlerOffset; + Label labelEnd; + if (exceptionBlockAssistanceMode != EBAM_COMPAT) + { + labelEnd = block.labelEnd; + } + else + { + MarkLabel(block.labelEnd); + labelEnd = DefineLabel(); + Emit(OpCodes.Leave, labelEnd); + } + exceptionStack.Pop(); + ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count); + newBlock.labelEnd = labelEnd; + newBlock.tryOffset = block.tryOffset; + newBlock.tryLength = code.Position - block.tryOffset; + block = newBlock; + exceptions.Add(block); + exceptionStack.Push(block); + } + block.handlerOffset = code.Position; + block.kind = kind; + stackHeight = 0; + } + + public void EndExceptionBlock() + { + ExceptionBlock block = exceptionStack.Pop(); + if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1)) + { + if (block.kind != ExceptionHandlingClauseOptions.Finally && block.kind != ExceptionHandlingClauseOptions.Fault) + { + Emit(OpCodes.Leave, block.labelEnd); + } + else + { + Emit(OpCodes.Endfinally); + } + } + MarkLabel(block.labelEnd); + block.handlerLength = code.Position - block.handlerOffset; + } + + public void BeginScope() + { + Scope newScope = new Scope(scope); + scope.children.Add(newScope); + scope = newScope; + scope.startOffset = code.Position; + } + + public void UsingNamespace(string usingNamespace) + { + if (scope != null) + { + scope.namespaces.Add(usingNamespace); + } + } + + public LocalBuilder DeclareLocal(Type localType) + { + return DeclareLocal(localType, false); + } + + public LocalBuilder DeclareLocal(Type localType, bool pinned) + { + LocalBuilder local = new LocalBuilder(localType, localsCount++, pinned); + locals.AddArgument(localType, pinned); + if (scope != null) + { + scope.locals.Add(local); + } + return local; + } + + public LocalBuilder __DeclareLocal(Type localType, bool pinned, CustomModifiers customModifiers) + { + LocalBuilder local = new LocalBuilder(localType, localsCount++, pinned, customModifiers); + locals.__AddArgument(localType, pinned, customModifiers); + if (scope != null) + { + scope.locals.Add(local); + } + return local; + } + + public Label DefineLabel() + { + Label label = new Label(labels.Count); + labels.Add(-1); + labelStackHeight.Add(-1); + return label; + } + + public void Emit(OpCode opc) + { + if (opc == OpCodes.Ret && stackHeight > 1) + throw new BadImageFormatException("Unbalanced stack height."); + + if (opc.Value < 0) + code.Write((byte)(opc.Value >> 8)); + + code.Write((byte)opc.Value); + switch (opc.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Break: + case FlowControl.Return: + case FlowControl.Throw: + stackHeight = -1; + break; + default: + UpdateStack(opc.StackDiff); + break; + } + } + + private void UpdateStack(int stackdiff) + { + if (stackHeight == -1) + { + // we're about to emit code that is either unreachable or reachable only via a backward branch + stackHeight = 0; + } + Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue); + stackHeight += stackdiff; + Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue); + maxStack = Math.Max(maxStack, (ushort)stackHeight); + } + + public void Emit(OpCode opc, byte arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, double arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, FieldInfo field) + { + Emit(opc); + WriteToken(moduleBuilder.GetFieldToken(field).Token); + } + + public void Emit(OpCode opc, short arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, int arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, long arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, Label label) + { + // We need special stackHeight handling for unconditional branches, + // because the branch and next flows have differing stack heights. + // Note that this assumes that unconditional branches do not push/pop. + int flowStackHeight = this.stackHeight; + Emit(opc); + if (opc == OpCodes.Leave || opc == OpCodes.Leave_S) + { + flowStackHeight = 0; + } + else if (opc.FlowControl != FlowControl.Branch) + { + flowStackHeight = this.stackHeight; + } + // if the label has already been marked, we can emit the branch offset directly + if (labels[label.Index] != -1) + { + if (labelStackHeight[label.Index] != flowStackHeight && (labelStackHeight[label.Index] != 0 || flowStackHeight != -1)) + { + // the "backward branch constraint" prohibits this, so we don't need to support it + throw new NotSupportedException("'Backward branch constraints' violated"); + } + if (opc.OperandType == OperandType.ShortInlineBrTarget) + { + WriteByteBranchOffset(labels[label.Index] - (code.Position + 1)); + } + else + { + code.Write(labels[label.Index] - (code.Position + 4)); + } + } + else + { + Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == flowStackHeight || (flowStackHeight == -1 && labelStackHeight[label.Index] == 0)); + labelStackHeight[label.Index] = flowStackHeight; + LabelFixup fix = new LabelFixup(); + fix.label = label.Index; + fix.offset = code.Position; + labelFixups.Add(fix); + if (opc.OperandType == OperandType.ShortInlineBrTarget) + { + code.Write((byte)1); + } + else + { + code.Write(4); + } + } + } + + private void WriteByteBranchOffset(int offset) + { + if (offset < -128 || offset > 127) + { + throw new NotSupportedException("Branch offset of " + offset + " does not fit in one-byte branch target at position " + code.Position); + } + code.Write((byte)offset); + } + + public void Emit(OpCode opc, Label[] labels) + { + Emit(opc); + LabelFixup fix = new LabelFixup(); + fix.label = -1; + fix.offset = code.Position; + labelFixups.Add(fix); + code.Write(labels.Length); + foreach (Label label in labels) + { + code.Write(label.Index); + if (this.labels[label.Index] != -1) + { + if (labelStackHeight[label.Index] != stackHeight) + { + // the "backward branch constraint" prohibits this, so we don't need to support it + throw new NotSupportedException(); + } + } + else + { + Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == stackHeight); + labelStackHeight[label.Index] = stackHeight; + } + } + } + + public void Emit(OpCode opc, LocalBuilder local) + { + if ((opc == OpCodes.Ldloc || opc == OpCodes.Ldloca || opc == OpCodes.Stloc) && local.LocalIndex < 256) + { + if (opc == OpCodes.Ldloc) + { + switch (local.LocalIndex) + { + case 0: + Emit(OpCodes.Ldloc_0); + break; + case 1: + Emit(OpCodes.Ldloc_1); + break; + case 2: + Emit(OpCodes.Ldloc_2); + break; + case 3: + Emit(OpCodes.Ldloc_3); + break; + default: + Emit(OpCodes.Ldloc_S); + code.Write((byte)local.LocalIndex); + break; + } + } + else if (opc == OpCodes.Ldloca) + { + Emit(OpCodes.Ldloca_S); + code.Write((byte)local.LocalIndex); + } + else if (opc == OpCodes.Stloc) + { + switch (local.LocalIndex) + { + case 0: + Emit(OpCodes.Stloc_0); + break; + case 1: + Emit(OpCodes.Stloc_1); + break; + case 2: + Emit(OpCodes.Stloc_2); + break; + case 3: + Emit(OpCodes.Stloc_3); + break; + default: + Emit(OpCodes.Stloc_S); + code.Write((byte)local.LocalIndex); + break; + } + } + } + else + { + Emit(opc); + switch (opc.OperandType) + { + case OperandType.InlineVar: + code.Write((ushort)local.LocalIndex); + break; + case OperandType.ShortInlineVar: + code.Write((byte)local.LocalIndex); + break; + } + } + } + + private void WriteToken(int token) + { + if (ModuleBuilder.IsPseudoToken(token)) + { + tokenFixups.Add(code.Position); + } + code.Write(token); + } + + private void UpdateStack(OpCode opc, bool hasthis, Type returnType, int parameterCount) + { + if (opc == OpCodes.Jmp) + { + stackHeight = -1; + } + else if (opc.FlowControl == FlowControl.Call) + { + int stackdiff = 0; + if ((hasthis && opc != OpCodes.Newobj) || opc == OpCodes.Calli) + { + // pop this + stackdiff--; + } + // pop parameters + stackdiff -= parameterCount; + if (returnType != moduleBuilder.universe.System_Void) + { + // push return value + stackdiff++; + } + UpdateStack(stackdiff); + } + } + + public void Emit(OpCode opc, MethodInfo method) + { + UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount); + Emit(opc); + WriteToken(moduleBuilder.GetMethodTokenForIL(method).Token); + } + + public void Emit(OpCode opc, ConstructorInfo constructor) + { + Emit(opc, constructor.GetMethodInfo()); + } + + public void Emit(OpCode opc, sbyte arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, float arg) + { + Emit(opc); + code.Write(arg); + } + + public void Emit(OpCode opc, string str) + { + Emit(opc); + code.Write(moduleBuilder.GetStringConstant(str).Token); + } + + public void Emit(OpCode opc, Type type) + { + Emit(opc); + if (opc == OpCodes.Ldtoken) + { + code.Write(moduleBuilder.GetTypeToken(type).Token); + } + else + { + code.Write(moduleBuilder.GetTypeTokenForMemberRef(type)); + } + } + + public void Emit(OpCode opcode, SignatureHelper signature) + { + Emit(opcode); + UpdateStack(opcode, signature.HasThis, signature.ReturnType, signature.ParameterCount); + code.Write(moduleBuilder.GetSignatureToken(signature).Token); + } + + public void EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes) + { + __EmitCall(opc, method, optionalParameterTypes, null); + } + + public void __EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) + { + if (optionalParameterTypes == null || optionalParameterTypes.Length == 0) + { + Emit(opc, method); + } + else + { + Emit(opc); + UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount + optionalParameterTypes.Length); + code.Write(moduleBuilder.__GetMethodToken(method, optionalParameterTypes, customModifiers).Token); + } + } + + public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes) + { + EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes); + } + + public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) + { + __EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes, customModifiers); + } + + public void EmitCalli(OpCode opc, CallingConvention callingConvention, Type returnType, Type[] parameterTypes) + { + SignatureHelper sig = SignatureHelper.GetMethodSigHelper(moduleBuilder, callingConvention, returnType); + sig.AddArguments(parameterTypes, null, null); + Emit(opc, sig); + } + + public void EmitCalli(OpCode opc, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) + { + SignatureHelper sig = SignatureHelper.GetMethodSigHelper(moduleBuilder, callingConvention, returnType); + sig.AddArguments(parameterTypes, null, null); + if (optionalParameterTypes != null && optionalParameterTypes.Length != 0) + { + sig.AddSentinel(); + sig.AddArguments(optionalParameterTypes, null, null); + } + Emit(opc, sig); + } + + public void __EmitCalli(OpCode opc, __StandAloneMethodSig sig) + { + Emit(opc); + if (sig.IsUnmanaged) + { + UpdateStack(opc, false, sig.ReturnType, sig.ParameterCount); + } + else + { + CallingConventions callingConvention = sig.CallingConvention; + UpdateStack(opc, (callingConvention & CallingConventions.HasThis | CallingConventions.ExplicitThis) == CallingConventions.HasThis, sig.ReturnType, sig.ParameterCount); + } + ByteBuffer bb = new ByteBuffer(16); + Signature.WriteStandAloneMethodSig(moduleBuilder, bb, sig); + code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(bb))); + } + + public void EmitWriteLine(string text) + { + Universe u = moduleBuilder.universe; + Emit(OpCodes.Ldstr, text); + Emit(OpCodes.Call, u.System_Console.GetMethod("WriteLine", new Type[] { u.System_String })); + } + + public void EmitWriteLine(FieldInfo field) + { + Universe u = moduleBuilder.universe; + Emit(OpCodes.Call, u.System_Console.GetMethod("get_Out")); + if (field.IsStatic) + { + Emit(OpCodes.Ldsfld, field); + } + else + { + Emit(OpCodes.Ldarg_0); + Emit(OpCodes.Ldfld, field); + } + Emit(OpCodes.Callvirt, u.System_IO_TextWriter.GetMethod("WriteLine", new Type[] { field.FieldType })); + } + + public void EmitWriteLine(LocalBuilder local) + { + Universe u = moduleBuilder.universe; + Emit(OpCodes.Call, u.System_Console.GetMethod("get_Out")); + Emit(OpCodes.Ldloc, local); + Emit(OpCodes.Callvirt, u.System_IO_TextWriter.GetMethod("WriteLine", new Type[] { local.LocalType })); + } + + public void EndScope() + { + scope.endOffset = code.Position; + scope = scope.parent; + } + + public void MarkLabel(Label loc) + { + Debug.Assert(stackHeight == -1 || labelStackHeight[loc.Index] == -1 || stackHeight == labelStackHeight[loc.Index]); + labels[loc.Index] = code.Position; + if (labelStackHeight[loc.Index] == -1) + { + if (stackHeight == -1) + { + // We're at a location that can only be reached by a backward branch, + // so according to the "backward branch constraint" that must mean the stack is empty, + // but note that this may be an unused label followed by another label that is used and + // that does have a non-zero stack height, so we don't yet set stackHeight here. + labelStackHeight[loc.Index] = 0; + } + else + { + labelStackHeight[loc.Index] = stackHeight; + } + } + else + { + Debug.Assert(stackHeight == -1 || stackHeight == labelStackHeight[loc.Index]); + stackHeight = labelStackHeight[loc.Index]; + } + } + + public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) + { + SequencePoint sp = new SequencePoint(); + sp.document = document; + sp.offset = code.Position; + sp.startLine = startLine; + sp.startColumn = startColumn; + sp.endLine = endLine; + sp.endColumn = endColumn; + sequencePoints.Add(sp); + } + + public void ThrowException(Type excType) + { + Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes)); + Emit(OpCodes.Throw); + } + + internal int WriteBody(bool initLocals) + { + if (moduleBuilder.symbolWriter != null) + { + Debug.Assert(scope != null && scope.parent == null); + scope.endOffset = code.Position; + } + + ResolveBranches(); + + ByteBuffer bb = moduleBuilder.methodBodies; + + int localVarSigTok = 0; + + int rva; + if (localsCount == 0 && exceptions.Count == 0 && maxStack <= 8 && code.Length < 64 && !fatHeader) + { + rva = WriteTinyHeaderAndCode(bb); + } + else + { + if (localsCount != 0) + { + localVarSigTok = moduleBuilder.GetSignatureToken(locals).Token; + } + rva = WriteFatHeaderAndCode(bb, localVarSigTok, initLocals); + } + + if (moduleBuilder.symbolWriter != null) + { + if (sequencePoints.Count != 0) + { + ISymbolDocumentWriter document = sequencePoints[0].document; + int[] offsets = new int[sequencePoints.Count]; + int[] lines = new int[sequencePoints.Count]; + int[] columns = new int[sequencePoints.Count]; + int[] endLines = new int[sequencePoints.Count]; + int[] endColumns = new int[sequencePoints.Count]; + for (int i = 0; i < sequencePoints.Count; i++) + { + if (sequencePoints[i].document != document) + { + throw new NotImplementedException(); + } + offsets[i] = sequencePoints[i].offset; + lines[i] = sequencePoints[i].startLine; + columns[i] = sequencePoints[i].startColumn; + endLines[i] = sequencePoints[i].endLine; + endColumns[i] = sequencePoints[i].endColumn; + } + moduleBuilder.symbolWriter.DefineSequencePoints(document, offsets, lines, columns, endLines, endColumns); + } + + WriteScope(scope, localVarSigTok); + } + return rva; + } + + private void ResolveBranches() + { + foreach (LabelFixup fixup in labelFixups) + { + // is it a switch? + if (fixup.label == -1) + { + code.Position = fixup.offset; + int count = code.GetInt32AtCurrentPosition(); + int offset = fixup.offset + 4 + 4 * count; + code.Position += 4; + for (int i = 0; i < count; i++) + { + int index = code.GetInt32AtCurrentPosition(); + code.Write(labels[index] - offset); + } + } + else + { + code.Position = fixup.offset; + byte size = code.GetByteAtCurrentPosition(); + int branchOffset = labels[fixup.label] - (code.Position + size); + if (size == 1) + { + WriteByteBranchOffset(branchOffset); + } + else + { + code.Write(branchOffset); + } + } + } + } + + internal static void WriteTinyHeader(ByteBuffer bb, int length) + { + const byte CorILMethod_TinyFormat = 0x2; + bb.Write((byte)(CorILMethod_TinyFormat | (length << 2))); + } + + private int WriteTinyHeaderAndCode(ByteBuffer bb) + { + int rva = bb.Position; + WriteTinyHeader(bb, code.Length); + AddTokenFixups(bb.Position, moduleBuilder.tokenFixupOffsets, tokenFixups); + bb.Write(code); + return rva; + } + + internal static void WriteFatHeader(ByteBuffer bb, bool initLocals, bool exceptions, ushort maxStack, int codeLength, int localVarSigTok) + { + const byte CorILMethod_FatFormat = 0x03; + const byte CorILMethod_MoreSects = 0x08; + const byte CorILMethod_InitLocals = 0x10; + + short flagsAndSize = (short)(CorILMethod_FatFormat | (3 << 12)); + if (initLocals) + { + flagsAndSize |= CorILMethod_InitLocals; + } + + if (exceptions) + { + flagsAndSize |= CorILMethod_MoreSects; + } + + bb.Write(flagsAndSize); + bb.Write(maxStack); + bb.Write(codeLength); + bb.Write(localVarSigTok); + } + + private int WriteFatHeaderAndCode(ByteBuffer bb, int localVarSigTok, bool initLocals) + { + // fat headers require 4-byte alignment + bb.Align(4); + int rva = bb.Position; + WriteFatHeader(bb, initLocals, exceptions.Count > 0, maxStack, code.Length, localVarSigTok); + AddTokenFixups(bb.Position, moduleBuilder.tokenFixupOffsets, tokenFixups); + bb.Write(code); + if (exceptions.Count > 0) + { + exceptions.Sort(exceptions[0]); + WriteExceptionHandlers(bb, exceptions); + } + return rva; + } + + internal static void WriteExceptionHandlers(ByteBuffer bb, List exceptions) + { + bb.Align(4); + + bool fat = false; + if (exceptions.Count * 12 + 4 > 255) + { + fat = true; + } + else + { + foreach (ExceptionBlock block in exceptions) + { + if (block.tryOffset > 65535 || block.tryLength > 255 || block.handlerOffset > 65535 || block.handlerLength > 255) + { + fat = true; + break; + } + } + } + + const byte CorILMethod_Sect_EHTable = 0x1; + const byte CorILMethod_Sect_FatFormat = 0x40; + + if (fat) + { + bb.Write((byte)(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat)); + int dataSize = exceptions.Count * 24 + 4; + bb.Write((byte)dataSize); + bb.Write((short)(dataSize >> 8)); + foreach (ExceptionBlock block in exceptions) + { + bb.Write((int)block.kind); + bb.Write(block.tryOffset); + bb.Write(block.tryLength); + bb.Write(block.handlerOffset); + bb.Write(block.handlerLength); + bb.Write(block.filterOffsetOrExceptionTypeToken); + } + } + else + { + bb.Write(CorILMethod_Sect_EHTable); + bb.Write((byte)(exceptions.Count * 12 + 4)); + bb.Write((short)0); + foreach (ExceptionBlock block in exceptions) + { + bb.Write((short)block.kind); + bb.Write((short)block.tryOffset); + bb.Write((byte)block.tryLength); + bb.Write((short)block.handlerOffset); + bb.Write((byte)block.handlerLength); + bb.Write(block.filterOffsetOrExceptionTypeToken); + } + } + } + + internal static void AddTokenFixups(int codeOffset, List tokenFixupOffsets, IEnumerable tokenFixups) + { + foreach (int fixup in tokenFixups) + { + tokenFixupOffsets.Add(fixup + codeOffset); + } + } + + private void WriteScope(Scope scope, int localVarSigTok) + { + moduleBuilder.symbolWriter.OpenScope(scope.startOffset); + foreach (LocalBuilder local in scope.locals) + { + if (local.name != null) + { + int startOffset = local.startOffset; + int endOffset = local.endOffset; + if (startOffset == 0 && endOffset == 0) + { + startOffset = scope.startOffset; + endOffset = scope.endOffset; + } + moduleBuilder.symbolWriter.DefineLocalVariable2(local.name, 0, localVarSigTok, SymAddressKind.ILOffset, local.LocalIndex, 0, 0, startOffset, endOffset); + } + } + foreach (string ns in scope.namespaces) + { + moduleBuilder.symbolWriter.UsingNamespace(ns); + } + foreach (Scope child in scope.children) + { + WriteScope(child, localVarSigTok); + } + moduleBuilder.symbolWriter.CloseScope(scope.endOffset); + } + } } diff --git a/src/IKVM.Reflection/Emit/ModuleBuilder.cs b/src/IKVM.Reflection/Emit/ModuleBuilder.cs index 53546f5e17..e52bbe8c8a 100644 --- a/src/IKVM.Reflection/Emit/ModuleBuilder.cs +++ b/src/IKVM.Reflection/Emit/ModuleBuilder.cs @@ -35,1959 +35,1798 @@ Jeroen Frijters namespace IKVM.Reflection.Emit { + public sealed class ModuleBuilder : Module, ITypeOwner - { - private static readonly bool usePublicKeyAssemblyReference = false; - private Guid mvid; - private uint timestamp; - private long imageBaseAddress = 0x00400000; - private long stackReserve = -1; - private int fileAlignment = 0x200; - private DllCharacteristics dllCharacteristics = DllCharacteristics.DynamicBase | DllCharacteristics.NoSEH | DllCharacteristics.NXCompat | DllCharacteristics.TerminalServerAware; - private readonly AssemblyBuilder asm; - internal readonly string moduleName; - internal readonly string fileName; - internal readonly ISymbolWriterImpl symbolWriter; - private readonly TypeBuilder moduleType; - private readonly List types = new List(); - private readonly Dictionary typeTokens = new Dictionary(); - private readonly Dictionary memberRefTypeTokens = new Dictionary(); - internal readonly ByteBuffer methodBodies = new ByteBuffer(128 * 1024); - internal readonly List tokenFixupOffsets = new List(); - internal readonly ByteBuffer initializedData = new ByteBuffer(512); - internal ResourceSection unmanagedResources; - private readonly Dictionary importedMemberRefs = new Dictionary(); - private readonly Dictionary importedMethodSpecs = new Dictionary(); - private readonly Dictionary referencedAssemblies = new Dictionary(); - private List referencedAssemblyNames; - private int nextPseudoToken = -1; - private readonly List resolvedTokens = new List(); - internal readonly TableHeap Tables = new TableHeap(); - internal readonly StringHeap Strings = new StringHeap(); - internal readonly UserStringHeap UserStrings = new UserStringHeap(); - internal readonly GuidHeap Guids = new GuidHeap(); - internal readonly BlobHeap Blobs = new BlobHeap(); - internal readonly List vtablefixups = new List(); - internal readonly List unmanagedExports = new List(); - private List interfaceImplCustomAttributes; - private readonly List resourceWriters = new List(); - private bool saved; - - private struct ResourceWriterRecord - { - private readonly string name; - private readonly ResourceWriter rw; - private readonly Stream stream; - private readonly ResourceAttributes attributes; - - internal ResourceWriterRecord(string name, Stream stream, ResourceAttributes attributes) - : this(name, null, stream, attributes) - { - - } - - internal ResourceWriterRecord(string name, ResourceWriter rw, Stream stream, ResourceAttributes attributes) - { - this.name = name; - this.rw = rw; - this.stream = stream; - this.attributes = attributes; - } - - internal void Emit(ModuleBuilder mb, int offset) - { - if (rw != null) - { - rw.Generate(); - } - - ManifestResourceTable.Record rec = new ManifestResourceTable.Record(); - rec.Offset = offset; - rec.Flags = (int)attributes; - rec.Name = mb.Strings.Add(name); - rec.Implementation = 0; - mb.ManifestResource.AddRecord(rec); - } - - internal int GetLength() - { - return 4 + (int)stream.Length; - } - - internal void Write(MetadataWriter mw) - { - mw.Write((int)stream.Length); - stream.Position = 0; - byte[] buffer = new byte[8192]; - int length; - while ((length = stream.Read(buffer, 0, buffer.Length)) != 0) - { - mw.Write(buffer, 0, length); - } - } - - internal void Close() - { - if (rw != null) - { - rw.Close(); - } - } - } - - internal struct VTableFixups - { - internal uint initializedDataOffset; - internal ushort count; - internal ushort type; - - internal int SlotWidth - { - get { return (type & 0x02) == 0 ? 4 : 8; } - } - } - - struct InterfaceImplCustomAttribute - { - internal int type; - internal int interfaceType; - internal int pseudoToken; - } - - struct MemberRefKey : IEquatable - { - private readonly Type type; - private readonly string name; - private readonly Signature signature; - - internal MemberRefKey(Type type, string name, Signature signature) - { - this.type = type; - this.name = name; - this.signature = signature; - } - - public bool Equals(MemberRefKey other) - { - return other.type.Equals(type) - && other.name == name - && other.signature.Equals(signature); - } - - public override bool Equals(object obj) - { - MemberRefKey? other = obj as MemberRefKey?; - return other != null && Equals(other.Value); - } - - public override int GetHashCode() - { - return type.GetHashCode() + name.GetHashCode() + signature.GetHashCode(); - } - - internal MethodBase LookupMethod() - { - return type.FindMethod(name, (MethodSignature)signature); - } - } - - struct MethodSpecKey : IEquatable - { - private readonly Type type; - private readonly string name; - private readonly MethodSignature signature; - private readonly Type[] genericParameters; - - internal MethodSpecKey(Type type, string name, MethodSignature signature, Type[] genericParameters) - { - this.type = type; - this.name = name; - this.signature = signature; - this.genericParameters = genericParameters; - } - - public bool Equals(MethodSpecKey other) - { - return other.type.Equals(type) - && other.name == name - && other.signature.Equals(signature) - && Util.ArrayEquals(other.genericParameters, genericParameters); - } - - public override bool Equals(object obj) - { - MethodSpecKey? other = obj as MethodSpecKey?; - return other != null && Equals(other.Value); - } - - public override int GetHashCode() - { - return type.GetHashCode() + name.GetHashCode() + signature.GetHashCode() + Util.GetHashCode(genericParameters); - } - } - - internal ModuleBuilder(AssemblyBuilder asm, string moduleName, string fileName, bool emitSymbolInfo) - : base(asm.universe) - { - this.asm = asm; - this.moduleName = moduleName; - this.fileName = fileName; - if (emitSymbolInfo) - { - symbolWriter = SymbolSupport.CreateSymbolWriterFor(this); - if (universe.Deterministic && !symbolWriter.IsDeterministic) - { - throw new NotSupportedException(); - } - } - if (!universe.Deterministic) - { - __PEHeaderTimeDateStamp = DateTime.UtcNow; - mvid = Guid.NewGuid(); - } - // must be the first record in the TypeDef table - moduleType = new TypeBuilder(this, null, ""); - types.Add(moduleType); - } - - internal void PopulatePropertyAndEventTables() - { - // LAMESPEC the PropertyMap and EventMap tables are not required to be sorted by the CLI spec, - // but .NET sorts them and Mono requires them to be sorted, so we have to populate the - // tables in the right order - foreach (TypeBuilder type in types) - { - type.PopulatePropertyAndEventTables(); - } - } - - internal void WriteTypeDefTable(MetadataWriter mw) - { - int fieldList = 1; - int methodList = 1; - foreach (TypeBuilder type in types) - { - type.WriteTypeDefRecord(mw, ref fieldList, ref methodList); - } - } - - internal void WriteMethodDefTable(int baseRVA, MetadataWriter mw) - { - int paramList = 1; - foreach (TypeBuilder type in types) - { - type.WriteMethodDefRecords(baseRVA, mw, ref paramList); - } - } - - internal void WriteParamTable(MetadataWriter mw) - { - foreach (TypeBuilder type in types) - { - type.WriteParamRecords(mw); - } - } - - internal void WriteFieldTable(MetadataWriter mw) - { - foreach (TypeBuilder type in types) - { - type.WriteFieldRecords(mw); - } - } - - internal int AllocPseudoToken() - { - return nextPseudoToken--; - } - - public TypeBuilder DefineType(string name) - { - return DefineType(name, TypeAttributes.Class); - } - - public TypeBuilder DefineType(string name, TypeAttributes attr) - { - return DefineType(name, attr, null); - } - - public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent) - { - return DefineType(name, attr, parent, PackingSize.Unspecified, 0); - } - - public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, int typesize) - { - return DefineType(name, attr, parent, PackingSize.Unspecified, typesize); - } - - public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, PackingSize packsize) - { - return DefineType(name, attr, parent, packsize, 0); - } - - public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces) - { - TypeBuilder tb = DefineType(name, attr, parent); - foreach (Type iface in interfaces) - { - tb.AddInterfaceImplementation(iface); - } - return tb; - } - - public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, PackingSize packingSize, int typesize) - { - string ns = null; - int lastdot = name.LastIndexOf('.'); - if (lastdot > 0) - { - ns = name.Substring(0, lastdot); - name = name.Substring(lastdot + 1); - } - TypeBuilder typeBuilder = __DefineType(ns, name); - typeBuilder.__SetAttributes(attr); - typeBuilder.SetParent(parent); - if (packingSize != PackingSize.Unspecified || typesize != 0) - { - typeBuilder.__SetLayout((int)packingSize, typesize); - } - return typeBuilder; - } - - public TypeBuilder __DefineType(string ns, string name) - { - return DefineType(this, ns, name); - } - - internal TypeBuilder DefineType(ITypeOwner owner, string ns, string name) - { - TypeBuilder typeBuilder = new TypeBuilder(owner, ns, name); - types.Add(typeBuilder); - return typeBuilder; - } - - public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type underlyingType) - { - TypeBuilder tb = DefineType(name, (visibility & TypeAttributes.VisibilityMask) | TypeAttributes.Sealed, universe.System_Enum); - FieldBuilder fb = tb.DefineField("value__", underlyingType, FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); - return new EnumBuilder(tb, fb); - } - - public FieldBuilder __DefineField(string name, Type type, CustomModifiers customModifiers, FieldAttributes attributes) - { - return moduleType.__DefineField(name, type, customModifiers, attributes); - } - - [Obsolete("Please use __DefineField(string, Type, CustomModifiers, FieldAttributes) instead.")] - public FieldBuilder __DefineField(string name, Type type, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, FieldAttributes attributes) - { - return moduleType.DefineField(name, type, requiredCustomModifiers, optionalCustomModifiers, attributes); - } - - public ConstructorBuilder __DefineModuleInitializer(MethodAttributes visibility) - { - return moduleType.DefineConstructor(visibility | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, Type.EmptyTypes); - } - - public FieldBuilder DefineUninitializedData(string name, int size, FieldAttributes attributes) - { - return moduleType.DefineUninitializedData(name, size, attributes); - } - - public FieldBuilder DefineInitializedData(string name, byte[] data, FieldAttributes attributes) - { - return moduleType.DefineInitializedData(name, data, attributes); - } - - public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes) - { - return moduleType.DefineMethod(name, attributes, returnType, parameterTypes); - } - - public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) - { - return moduleType.DefineMethod(name, attributes, callingConvention, returnType, parameterTypes); - } - - public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) - { - return moduleType.DefineMethod(name, attributes, callingConvention, returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers, parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers); - } - - public MethodBuilder DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) - { - return moduleType.DefinePInvokeMethod(name, dllName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); - } - - public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) - { - return moduleType.DefinePInvokeMethod(name, dllName, entryName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); - } - - public void CreateGlobalFunctions() - { - moduleType.CreateType(); - } - - internal void AddTypeForwarder(Type type, bool includeNested) - { - ExportType(type); - if (includeNested && !type.__IsMissing) - { - foreach (Type nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)) - { - // we export all nested types (i.e. even the private ones) - // (this behavior is the same as the C# compiler) - AddTypeForwarder(nested, true); - } - } - } - - private int ExportType(Type type) - { - ExportedTypeTable.Record rec = new ExportedTypeTable.Record(); - if (asm.ImageRuntimeVersion == "v2.0.50727") - { - // HACK we should *not* set the TypeDefId in this case, but 2.0 and 3.5 peverify gives a warning if it is missing (4.5 doesn't) - rec.TypeDefId = type.MetadataToken; - } - SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); - if (type.IsNested) - { - rec.Flags = 0; - rec.Implementation = ExportType(type.DeclaringType); - } - else - { - rec.Flags = 0x00200000; // CorTypeAttr.tdForwarder - rec.Implementation = ImportAssemblyRef(type.Assembly); - } - return 0x27000000 | this.ExportedType.FindOrAddRecord(rec); - } - - private void SetTypeNameAndTypeNamespace(TypeName name, out int typeName, out int typeNamespace) - { - typeName = this.Strings.Add(name.Name); - typeNamespace = name.Namespace == null ? 0 : this.Strings.Add(name.Namespace); - } - - public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute)); - } - - public void SetCustomAttribute(CustomAttributeBuilder customBuilder) - { - SetCustomAttribute(0x00000001, customBuilder); - } - - internal void SetCustomAttribute(int token, CustomAttributeBuilder customBuilder) - { - CustomAttributeTable.Record rec = new CustomAttributeTable.Record(); - rec.Parent = token; - rec.Type = asm.IsWindowsRuntime ? customBuilder.Constructor.ImportTo(this) : GetConstructorToken(customBuilder.Constructor).Token; - rec.Value = customBuilder.WriteBlob(this); - this.CustomAttribute.AddRecord(rec); - } - - private void AddDeclSecurityRecord(int token, int action, int blob) - { - DeclSecurityTable.Record rec = new DeclSecurityTable.Record(); - rec.Action = (short)action; - rec.Parent = token; - rec.PermissionSet = blob; - this.DeclSecurity.AddRecord(rec); - } - - internal void AddDeclarativeSecurity(int token, System.Security.Permissions.SecurityAction securityAction, System.Security.PermissionSet permissionSet) - { - // like Ref.Emit, we're using the .NET 1.x xml format - AddDeclSecurityRecord(token, (int)securityAction, this.Blobs.Add(ByteBuffer.Wrap(System.Text.Encoding.Unicode.GetBytes(permissionSet.ToXml().ToString())))); - } - - internal void AddDeclarativeSecurity(int token, List declarativeSecurity) - { - Dictionary> ordered = new Dictionary>(); - foreach (CustomAttributeBuilder cab in declarativeSecurity) - { - int action; - // check for HostProtectionAttribute without SecurityAction - if (cab.ConstructorArgumentCount == 0) - { - action = (int)System.Security.Permissions.SecurityAction.LinkDemand; - } - else - { - action = (int)cab.GetConstructorArgument(0); - } - if (cab.IsLegacyDeclSecurity) - { - AddDeclSecurityRecord(token, action, cab.WriteLegacyDeclSecurityBlob(this)); - continue; - } - List list; - if (!ordered.TryGetValue(action, out list)) - { - list = new List(); - ordered.Add(action, list); - } - list.Add(cab); - } - foreach (KeyValuePair> kv in ordered) - { - AddDeclSecurityRecord(token, kv.Key, WriteDeclSecurityBlob(kv.Value)); - } - } - - private int WriteDeclSecurityBlob(List list) - { - ByteBuffer namedArgs = new ByteBuffer(100); - ByteBuffer bb = new ByteBuffer(list.Count * 100); - bb.Write((byte)'.'); - bb.WriteCompressedUInt(list.Count); - foreach (CustomAttributeBuilder cab in list) - { - bb.Write(cab.Constructor.DeclaringType.AssemblyQualifiedName); - namedArgs.Clear(); - cab.WriteNamedArgumentsForDeclSecurity(this, namedArgs); - bb.WriteCompressedUInt(namedArgs.Length); - bb.Write(namedArgs); - } - return this.Blobs.Add(bb); - } - - public void DefineManifestResource(string name, Stream stream, ResourceAttributes attribute) - { - resourceWriters.Add(new ResourceWriterRecord(name, stream, attribute)); - } - - public IResourceWriter DefineResource(string name, string description) - { - return DefineResource(name, description, ResourceAttributes.Public); - } - - public IResourceWriter DefineResource(string name, string description, ResourceAttributes attribute) - { - // FXBUG we ignore the description, because there is no such thing - - MemoryStream mem = new MemoryStream(); - ResourceWriter rw = new ResourceWriter(mem); - resourceWriters.Add(new ResourceWriterRecord(name, rw, mem, attribute)); - return rw; - } - - internal void EmitResources() - { - int offset = 0; - foreach (ResourceWriterRecord rwr in resourceWriters) - { - // resources must be 8-byte aligned - offset = (offset + 7) & ~7; - rwr.Emit(this, offset); - offset += rwr.GetLength(); - } - } - - internal void WriteResources(MetadataWriter mw) - { - int offset = 0; - foreach (ResourceWriterRecord rwr in resourceWriters) - { - // resources must be 8-byte aligned - int alignment = ((offset + 7) & ~7) - offset; - for (int i = 0; i < alignment; i++) - { - mw.Write((byte)0); - } - rwr.Write(mw); - offset += rwr.GetLength() + alignment; - } - } - - internal void CloseResources() - { - foreach (ResourceWriterRecord rwr in resourceWriters) - { - rwr.Close(); - } - } - - internal int GetManifestResourcesLength() - { - int length = 0; - foreach (ResourceWriterRecord rwr in resourceWriters) - { - // resources must be 8-byte aligned - length = (length + 7) & ~7; - length += rwr.GetLength(); - } - return length; - } - - public override Assembly Assembly - { - get { return asm; } - } - - internal override Type FindType(TypeName name) - { - foreach (Type type in types) - { - if (type.TypeName == name) - { - return type; - } - } - return null; - } - - internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) - { - foreach (Type type in types) - { - if (type.TypeName.ToLowerInvariant() == lowerCaseName) - { - return type; - } - } - return null; - } - - internal override void GetTypesImpl(List list) - { - foreach (Type type in types) - { - if (type != moduleType) - { - list.Add(type); - } - } - } - - public ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) - { - return symbolWriter.DefineDocument(url, language, languageVendor, documentType); - } - - public int __GetAssemblyToken(Assembly assembly) - { - return ImportAssemblyRef(assembly); - } - - public TypeToken GetTypeToken(string name) - { - return new TypeToken(GetType(name, true, false).MetadataToken); - } - - public TypeToken GetTypeToken(Type type) - { - if (type.Module == this && !asm.IsWindowsRuntime) - { - return new TypeToken(type.GetModuleBuilderToken()); - } - else - { - return new TypeToken(ImportType(type)); - } - } - - internal int GetTypeTokenForMemberRef(Type type) - { - if (type.__IsMissing) - { - return ImportType(type); - } - else if (type.IsGenericTypeDefinition) - { - int token; - if (!memberRefTypeTokens.TryGetValue(type, out token)) - { - ByteBuffer spec = new ByteBuffer(5); - Signature.WriteTypeSpec(this, spec, type); - token = 0x1B000000 | this.TypeSpec.AddRecord(this.Blobs.Add(spec)); - memberRefTypeTokens.Add(type, token); - } - return token; - } - else if (type.IsModulePseudoType) - { - return 0x1A000000 | this.ModuleRef.FindOrAddRecord(this.Strings.Add(type.Module.ScopeName)); - } - else - { - return GetTypeToken(type).Token; - } - } - - private static bool IsFromGenericTypeDefinition(MemberInfo member) - { - Type decl = member.DeclaringType; - return decl != null && !decl.__IsMissing && decl.IsGenericTypeDefinition; - } - - public FieldToken GetFieldToken(FieldInfo field) - { - // NOTE for some reason, when TypeBuilder.GetFieldToken() is used on a field in a generic type definition, - // a memberref token is returned (confirmed on .NET) unlike for Get(Method|Constructor)Token which always - // simply returns the MethodDef token (if the method is from the same module). - FieldBuilder fb = field as FieldBuilder; - if (fb != null && fb.Module == this && !IsFromGenericTypeDefinition(fb)) - { - return new FieldToken(fb.MetadataToken); - } - else - { - return new FieldToken(field.ImportTo(this)); - } - } - - public MethodToken GetMethodToken(MethodInfo method) - { - MethodBuilder mb = method as MethodBuilder; - if (mb != null && mb.ModuleBuilder == this) - { - return new MethodToken(mb.MetadataToken); - } - else - { - return new MethodToken(method.ImportTo(this)); - } - } - - // new in .NET 4.5 - public MethodToken GetMethodToken(MethodInfo method, IEnumerable optionalParameterTypes) - { - return __GetMethodToken(method, Util.ToArray(optionalParameterTypes), null); - } - - public MethodToken __GetMethodToken(MethodInfo method, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) - { - ByteBuffer sig = new ByteBuffer(16); - method.MethodSignature.WriteMethodRefSig(this, sig, optionalParameterTypes, customModifiers); - MemberRefTable.Record record = new MemberRefTable.Record(); - if (method.Module == this) - { - record.Class = method.MetadataToken; - } - else - { - record.Class = GetTypeTokenForMemberRef(method.DeclaringType ?? method.Module.GetModuleType()); - } - record.Name = Strings.Add(method.Name); - record.Signature = Blobs.Add(sig); - return new MethodToken(0x0A000000 | MemberRef.FindOrAddRecord(record)); - } - - // when we refer to a method on a generic type definition in the IL stream, - // we need to use a MemberRef (even if the method is in the same module) - internal MethodToken GetMethodTokenForIL(MethodInfo method) - { - if (method.IsGenericMethodDefinition) - { - method = method.MakeGenericMethod(method.GetGenericArguments()); - } - if (IsFromGenericTypeDefinition(method)) - { - return new MethodToken(method.ImportTo(this)); - } - else - { - return GetMethodToken(method); - } - } - - internal int GetMethodTokenWinRT(MethodInfo method) - { - return asm.IsWindowsRuntime ? method.ImportTo(this) : GetMethodToken(method).Token; - } - - public MethodToken GetConstructorToken(ConstructorInfo constructor) - { - return GetMethodToken(constructor.GetMethodInfo()); - } - - // new in .NET 4.5 - public MethodToken GetConstructorToken(ConstructorInfo constructor, IEnumerable optionalParameterTypes) - { - return GetMethodToken(constructor.GetMethodInfo(), optionalParameterTypes); - } - - public MethodToken __GetConstructorToken(ConstructorInfo constructor, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) - { - return __GetMethodToken(constructor.GetMethodInfo(), optionalParameterTypes, customModifiers); - } - - internal int ImportMethodOrField(Type declaringType, string name, Signature sig) - { - int token; - MemberRefKey key = new MemberRefKey(declaringType, name, sig); - if (!importedMemberRefs.TryGetValue(key, out token)) - { - MemberRefTable.Record rec = new MemberRefTable.Record(); - rec.Class = GetTypeTokenForMemberRef(declaringType); - rec.Name = this.Strings.Add(name); - ByteBuffer bb = new ByteBuffer(16); - sig.WriteSig(this, bb); - rec.Signature = this.Blobs.Add(bb); - token = 0x0A000000 | this.MemberRef.AddRecord(rec); - importedMemberRefs.Add(key, token); - } - return token; - } - - internal int ImportMethodSpec(Type declaringType, MethodInfo method, Type[] genericParameters) - { - Debug.Assert(method.__IsMissing || method.GetMethodOnTypeDefinition() == method); - int token; - MethodSpecKey key = new MethodSpecKey(declaringType, method.Name, method.MethodSignature, genericParameters); - if (!importedMethodSpecs.TryGetValue(key, out token)) - { - MethodSpecTable.Record rec = new MethodSpecTable.Record(); - MethodBuilder mb = method as MethodBuilder; - if (mb != null && mb.ModuleBuilder == this && !declaringType.IsGenericType) - { - rec.Method = mb.MetadataToken; - } - else - { - // we're calling ImportMethodOrField directly here, because 'method' may be a MethodDef on a generic TypeDef and 'declaringType' the type instance - // (in order words the method and type have already been decoupled by the caller) - rec.Method = ImportMethodOrField(declaringType, method.Name, method.MethodSignature); - } - Writer.ByteBuffer spec = new Writer.ByteBuffer(10); - Signature.WriteMethodSpec(this, spec, genericParameters); - rec.Instantiation = this.Blobs.Add(spec); - token = 0x2B000000 | this.MethodSpec.FindOrAddRecord(rec); - importedMethodSpecs.Add(key, token); - } - return token; - } - - internal int ImportType(Type type) - { - int token; - if (!typeTokens.TryGetValue(type, out token)) - { - if (type.HasElementType || type.IsConstructedGenericType || type.__IsFunctionPointer) - { - ByteBuffer spec = new ByteBuffer(5); - Signature.WriteTypeSpec(this, spec, type); - token = 0x1B000000 | this.TypeSpec.AddRecord(this.Blobs.Add(spec)); - } - else - { - TypeRefTable.Record rec = new TypeRefTable.Record(); - if (type.IsNested) - { - rec.ResolutionScope = GetTypeToken(type.DeclaringType).Token; - } - else if (type.Module == this) - { - rec.ResolutionScope = 1; - } - else - { - rec.ResolutionScope = ImportAssemblyRef(type.Assembly); - } - SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); - token = 0x01000000 | this.TypeRef.AddRecord(rec); - } - typeTokens.Add(type, token); - } - return token; - } - - private int ImportAssemblyRef(Assembly asm) - { - int token; - if (!referencedAssemblies.TryGetValue(asm, out token)) - { - // We can't write the AssemblyRef record here yet, because the identity of the assembly can still change - // (if it's an AssemblyBuilder). - token = AllocPseudoToken(); - referencedAssemblies.Add(asm, token); - } - return token; - } - - internal void FillAssemblyRefTable() - { - foreach (KeyValuePair kv in referencedAssemblies) - { - if (IsPseudoToken(kv.Value)) - { - RegisterTokenFixup(kv.Value, FindOrAddAssemblyRef(kv.Key.GetName(), false)); - } - } - } - - private int FindOrAddAssemblyRef(AssemblyName name, bool alwaysAdd) - { - AssemblyRefTable.Record rec = new AssemblyRefTable.Record(); - Version ver = name.Version ?? new Version(0, 0, 0, 0); - rec.MajorVersion = (ushort)ver.Major; - rec.MinorVersion = (ushort)ver.Minor; - rec.BuildNumber = (ushort)ver.Build; - rec.RevisionNumber = (ushort)ver.Revision; - rec.Flags = (int)(name.Flags & ~AssemblyNameFlags.PublicKey); - const AssemblyNameFlags afPA_Specified = (AssemblyNameFlags)0x0080; - const AssemblyNameFlags afPA_Mask = (AssemblyNameFlags)0x0070; - if ((name.RawFlags & afPA_Specified) != 0) - { - rec.Flags |= (int)(name.RawFlags & afPA_Mask); - } - if (name.ContentType == AssemblyContentType.WindowsRuntime) - { - rec.Flags |= 0x0200; - } - byte[] publicKeyOrToken = null; - if (usePublicKeyAssemblyReference) - { - publicKeyOrToken = name.GetPublicKey(); - } - if (publicKeyOrToken == null || publicKeyOrToken.Length == 0) - { - publicKeyOrToken = name.GetPublicKeyToken() ?? Empty.Array; - } - else - { - const int PublicKey = 0x0001; - rec.Flags |= PublicKey; - } - rec.PublicKeyOrToken = this.Blobs.Add(ByteBuffer.Wrap(publicKeyOrToken)); - rec.Name = this.Strings.Add(name.Name); - rec.Culture = name.CultureName == null ? 0 : this.Strings.Add(name.CultureName); - if (name.hash != null) - { - rec.HashValue = this.Blobs.Add(ByteBuffer.Wrap(name.hash)); - } - else - { - rec.HashValue = 0; - } - return 0x23000000 | (alwaysAdd ? this.AssemblyRef.AddRecord(rec) : this.AssemblyRef.FindOrAddRecord(rec)); - } - - internal void WriteSymbolTokenMap() - { - for (int i = 0; i < resolvedTokens.Count; i++) - { - int newToken = resolvedTokens[i]; - // The symbol API doesn't support remapping arbitrary integers, the types have to be the same, - // so we copy the type from the newToken, because our pseudo tokens don't have a type. - // (see MethodToken.SymbolToken) - int oldToken = (i + 1) | (newToken & ~0xFFFFFF); - SymbolSupport.RemapToken(symbolWriter, oldToken, newToken); - } - } - - internal void RegisterTokenFixup(int pseudoToken, int realToken) - { - int index = -(pseudoToken + 1); - while (resolvedTokens.Count <= index) - { - resolvedTokens.Add(0); - } - resolvedTokens[index] = realToken; - } - - internal static bool IsPseudoToken(int token) - { - return token < 0; - } - - internal int ResolvePseudoToken(int pseudoToken) - { - int index = -(pseudoToken + 1); - return resolvedTokens[index]; - } - - internal void ApplyUnmanagedExports(ImageFileMachine imageFileMachine) - { - if (unmanagedExports.Count != 0) - { - int type; - int size; - switch (imageFileMachine) - { - case ImageFileMachine.I386: - case ImageFileMachine.ARM: - type = 0x05; - size = 4; - break; - case ImageFileMachine.AMD64: - type = 0x06; - size = 8; - break; - default: - throw new NotSupportedException(); - } - List methods = new List(); - for (int i = 0; i < unmanagedExports.Count; i++) - { - if (unmanagedExports[i].mb != null) - { - methods.Add(unmanagedExports[i].mb); - } - } - if (methods.Count != 0) - { - RelativeVirtualAddress rva = __AddVTableFixups(methods.ToArray(), type); - for (int i = 0; i < unmanagedExports.Count; i++) - { - if (unmanagedExports[i].mb != null) - { - UnmanagedExport exp = unmanagedExports[i]; - exp.rva = new RelativeVirtualAddress(rva.initializedDataOffset + (uint)(methods.IndexOf(unmanagedExports[i].mb) * size)); - unmanagedExports[i] = exp; - } - } - } - } - } - - internal void FixupMethodBodyTokens() - { - int methodToken = 0x06000001; - int fieldToken = 0x04000001; - int parameterToken = 0x08000001; - foreach (TypeBuilder type in types) - { - type.ResolveMethodAndFieldTokens(ref methodToken, ref fieldToken, ref parameterToken); - } - foreach (int offset in tokenFixupOffsets) - { - methodBodies.Position = offset; - int pseudoToken = methodBodies.GetInt32AtCurrentPosition(); - methodBodies.Write(ResolvePseudoToken(pseudoToken)); - } - foreach (VTableFixups fixup in vtablefixups) - { - for (int i = 0; i < fixup.count; i++) - { - initializedData.Position = (int)fixup.initializedDataOffset + i * fixup.SlotWidth; - initializedData.Write(ResolvePseudoToken(initializedData.GetInt32AtCurrentPosition())); - } - } - } - - private int GetHeaderLength() - { - return - 4 + // Signature - 2 + // MajorVersion - 2 + // MinorVersion - 4 + // Reserved - 4 + // ImageRuntimeVersion Length - StringToPaddedUTF8Length(asm.ImageRuntimeVersion) + - 2 + // Flags - 2 + // Streams - 4 + // #~ Offset - 4 + // #~ Size - 4 + // StringToPaddedUTF8Length("#~") - 4 + // #Strings Offset - 4 + // #Strings Size - 12 + // StringToPaddedUTF8Length("#Strings") - 4 + // #US Offset - 4 + // #US Size - 4 + // StringToPaddedUTF8Length("#US") - 4 + // #GUID Offset - 4 + // #GUID Size - 8 + // StringToPaddedUTF8Length("#GUID") - (Blobs.IsEmpty ? 0 : - ( - 4 + // #Blob Offset - 4 + // #Blob Size - 8 // StringToPaddedUTF8Length("#Blob") - )); - } - - internal int MetadataLength - { - get - { - return GetHeaderLength() + (Blobs.IsEmpty ? 0 : Blobs.Length) + Tables.Length + Strings.Length + UserStrings.Length + Guids.Length; - } - } - - internal void WriteMetadata(MetadataWriter mw, out uint guidHeapOffset) - { - mw.Write(0x424A5342); // Signature ("BSJB") - mw.Write((ushort)1); // MajorVersion - mw.Write((ushort)1); // MinorVersion - mw.Write(0); // Reserved - byte[] version = StringToPaddedUTF8(asm.ImageRuntimeVersion); - mw.Write(version.Length); // Length - mw.Write(version); - mw.Write((ushort)0); // Flags - // #Blob is the only optional heap - if (Blobs.IsEmpty) - { - mw.Write((ushort)4); // Streams - } - else - { - mw.Write((ushort)5); // Streams - } - - int offset = GetHeaderLength(); - - // Streams - mw.Write(offset); // Offset - mw.Write(Tables.Length); // Size - mw.Write(StringToPaddedUTF8("#~")); - offset += Tables.Length; - - mw.Write(offset); // Offset - mw.Write(Strings.Length); // Size - mw.Write(StringToPaddedUTF8("#Strings")); - offset += Strings.Length; - - mw.Write(offset); // Offset - mw.Write(UserStrings.Length); // Size - mw.Write(StringToPaddedUTF8("#US")); - offset += UserStrings.Length; - - mw.Write(offset); // Offset - mw.Write(Guids.Length); // Size - mw.Write(StringToPaddedUTF8("#GUID")); - offset += Guids.Length; - - if (!Blobs.IsEmpty) - { - mw.Write(offset); // Offset - mw.Write(Blobs.Length); // Size - mw.Write(StringToPaddedUTF8("#Blob")); - } - - Tables.Write(mw); - Strings.Write(mw); - UserStrings.Write(mw); - guidHeapOffset = mw.Position; - Guids.Write(mw); - if (!Blobs.IsEmpty) - { - Blobs.Write(mw); - } - } - - private static int StringToPaddedUTF8Length(string str) - { - return (System.Text.Encoding.UTF8.GetByteCount(str) + 4) & ~3; - } - - private static byte[] StringToPaddedUTF8(string str) - { - byte[] buf = new byte[(System.Text.Encoding.UTF8.GetByteCount(str) + 4) & ~3]; - System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0); - return buf; - } - - internal override void ExportTypes(int fileToken, ModuleBuilder manifestModule) - { - manifestModule.ExportTypes(types.ToArray(), fileToken); - } - - internal void ExportTypes(Type[] types, int fileToken) - { - Dictionary declaringTypes = new Dictionary(); - foreach (Type type in types) - { - if (!type.IsModulePseudoType && IsVisible(type)) - { - ExportedTypeTable.Record rec = new ExportedTypeTable.Record(); - rec.Flags = (int)type.Attributes; - // LAMESPEC ECMA says that TypeDefId is a row index, but it should be a token - rec.TypeDefId = type.MetadataToken; - SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); - if (type.IsNested) - { - rec.Implementation = declaringTypes[type.DeclaringType]; - } - else - { - rec.Implementation = fileToken; - } - int exportTypeToken = 0x27000000 | this.ExportedType.AddRecord(rec); - declaringTypes.Add(type, exportTypeToken); - } - } - } - - private static bool IsVisible(Type type) - { - // NOTE this is not the same as Type.IsVisible, because that doesn't take into account family access - return type.IsPublic || ((type.IsNestedFamily || type.IsNestedFamORAssem || type.IsNestedPublic) && IsVisible(type.DeclaringType)); - } - - internal void AddConstant(int parentToken, object defaultValue) - { - ConstantTable.Record rec = new ConstantTable.Record(); - rec.Parent = parentToken; - ByteBuffer val = new ByteBuffer(16); - if (defaultValue == null) - { - rec.Type = Signature.ELEMENT_TYPE_CLASS; - val.Write((int)0); - } - else if (defaultValue is bool) - { - rec.Type = Signature.ELEMENT_TYPE_BOOLEAN; - val.Write((bool)defaultValue ? (byte)1 : (byte)0); - } - else if (defaultValue is char) - { - rec.Type = Signature.ELEMENT_TYPE_CHAR; - val.Write((char)defaultValue); - } - else if (defaultValue is sbyte) - { - rec.Type = Signature.ELEMENT_TYPE_I1; - val.Write((sbyte)defaultValue); - } - else if (defaultValue is byte) - { - rec.Type = Signature.ELEMENT_TYPE_U1; - val.Write((byte)defaultValue); - } - else if (defaultValue is short) - { - rec.Type = Signature.ELEMENT_TYPE_I2; - val.Write((short)defaultValue); - } - else if (defaultValue is ushort) - { - rec.Type = Signature.ELEMENT_TYPE_U2; - val.Write((ushort)defaultValue); - } - else if (defaultValue is int) - { - rec.Type = Signature.ELEMENT_TYPE_I4; - val.Write((int)defaultValue); - } - else if (defaultValue is uint) - { - rec.Type = Signature.ELEMENT_TYPE_U4; - val.Write((uint)defaultValue); - } - else if (defaultValue is long) - { - rec.Type = Signature.ELEMENT_TYPE_I8; - val.Write((long)defaultValue); - } - else if (defaultValue is ulong) - { - rec.Type = Signature.ELEMENT_TYPE_U8; - val.Write((ulong)defaultValue); - } - else if (defaultValue is float) - { - rec.Type = Signature.ELEMENT_TYPE_R4; - val.Write((float)defaultValue); - } - else if (defaultValue is double) - { - rec.Type = Signature.ELEMENT_TYPE_R8; - val.Write((double)defaultValue); - } - else if (defaultValue is string) - { - rec.Type = Signature.ELEMENT_TYPE_STRING; - foreach (char c in (string)defaultValue) - { - val.Write(c); - } - } - else if (defaultValue is DateTime) - { - rec.Type = Signature.ELEMENT_TYPE_I8; - val.Write(((DateTime)defaultValue).Ticks); - } - else - { - throw new ArgumentException(); - } - rec.Value = this.Blobs.Add(val); - this.Constant.AddRecord(rec); - } - - ModuleBuilder ITypeOwner.ModuleBuilder - { - get { return this; } - } - - internal override Type ResolveType(int metadataToken, IGenericContext context) - { - if (metadataToken >> 24 != TypeDefTable.Index) - { - throw new NotImplementedException(); - } - return types[(metadataToken & 0xFFFFFF) - 1]; - } - - public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) - { - if (genericTypeArguments != null || genericMethodArguments != null) - { - throw new NotImplementedException(); - } - // this method is inefficient, but since it isn't used we don't care - if ((metadataToken >> 24) == MemberRefTable.Index) - { - foreach (KeyValuePair kv in importedMemberRefs) - { - if (kv.Value == metadataToken) - { - return kv.Key.LookupMethod(); - } - } - } - // HACK if we're given a SymbolToken, we need to convert back - if ((metadataToken & 0xFF000000) == 0x06000000) - { - metadataToken = -(metadataToken & 0x00FFFFFF); - } - foreach (Type type in types) - { - MethodBase method = ((TypeBuilder)type).LookupMethod(metadataToken); - if (method != null) - { - return method; - } - } - return ((TypeBuilder)moduleType).LookupMethod(metadataToken); - } - - public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) - { - throw new NotImplementedException(); - } - - public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) - { - throw new NotImplementedException(); - } - - public override string ResolveString(int metadataToken) - { - throw new NotImplementedException(); - } - - public override string FullyQualifiedName - { - get { return Path.GetFullPath(Path.Combine(asm.dir, fileName)); } - } - - public override string Name - { - get { return fileName; } - } - - internal Guid GetModuleVersionIdOrEmpty() - { - return mvid; - } - - public override Guid ModuleVersionId - { - get - { - if (mvid == Guid.Empty && universe.Deterministic) - { - // if a deterministic GUID is used, it can't be queried before the assembly has been written - throw new InvalidOperationException(); - } - return mvid; - } - } - - public void __SetModuleVersionId(Guid guid) - { - if (guid == Guid.Empty && universe.Deterministic) - { - // if you want to use Guid.Empty, don't set UniverseOptions.DeterministicOutput - throw new ArgumentOutOfRangeException(); - } - mvid = guid; - } - - internal uint GetTimeDateStamp() - { - return timestamp; - } - - public DateTime __PEHeaderTimeDateStamp - { - get { return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); } - set - { - if (value < new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) || value > new DateTime(2106, 2, 7, 6, 28, 15, DateTimeKind.Utc)) - { - throw new ArgumentOutOfRangeException(); - } - timestamp = (uint)(value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - } - } - - public override Type[] __ResolveOptionalParameterTypes(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers) - { - throw new NotImplementedException(); - } - - public override string ScopeName - { - get { return moduleName; } - } - - public ISymbolWriter GetSymWriter() - { - return symbolWriter; - } - - public void DefineUnmanagedResource(string resourceFileName) - { - // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section, - // also setting the Resource Directory entry. - unmanagedResources = new ResourceSection(); - unmanagedResources.ExtractResources(System.IO.File.ReadAllBytes(resourceFileName)); - } - - public bool IsTransient() - { - return false; - } - - public void SetUserEntryPoint(MethodInfo entryPoint) - { - int token = entryPoint.MetadataToken; - if (token < 0) - { - token = -token | 0x06000000; - } - if (symbolWriter != null) - { - symbolWriter.SetUserEntryPoint(new SymbolToken(token)); - } - } - - public StringToken GetStringConstant(string str) - { - return new StringToken(this.UserStrings.Add(str) | (0x70 << 24)); - } - - public SignatureToken GetSignatureToken(SignatureHelper sigHelper) - { - return new SignatureToken(this.StandAloneSig.FindOrAddRecord(this.Blobs.Add(sigHelper.GetSignature(this))) | (StandAloneSigTable.Index << 24)); - } - - public SignatureToken GetSignatureToken(byte[] sigBytes, int sigLength) - { - return new SignatureToken(this.StandAloneSig.FindOrAddRecord(this.Blobs.Add(ByteBuffer.Wrap(sigBytes, sigLength))) | (StandAloneSigTable.Index << 24)); - } - - public MethodInfo GetArrayMethod(Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) - { - return new ArrayMethod(this, arrayClass, methodName, callingConvention, returnType, parameterTypes); - } - - public MethodToken GetArrayMethodToken(Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) - { - return GetMethodToken(GetArrayMethod(arrayClass, methodName, callingConvention, returnType, parameterTypes)); - } - - internal override Type GetModuleType() - { - return moduleType; - } - - internal override IKVM.Reflection.Reader.ByteReader GetBlob(int blobIndex) - { - return Blobs.GetBlob(blobIndex); - } - - internal int GetSignatureBlobIndex(Signature sig) - { - ByteBuffer bb = new ByteBuffer(16); - sig.WriteSig(this, bb); - return this.Blobs.Add(bb); - } - - // non-standard API - public new long __ImageBase - { - get { return imageBaseAddress; } - set { imageBaseAddress = value; } - } - - protected override long GetImageBaseImpl() - { - return imageBaseAddress; - } - - public new long __StackReserve - { - get { return stackReserve; } - set { stackReserve = value; } - } - - protected override long GetStackReserveImpl() - { - return stackReserve; - } - - [Obsolete("Use __StackReserve property.")] - public void __SetStackReserve(long stackReserve) - { - __StackReserve = stackReserve; - } - - internal ulong GetStackReserve(ulong defaultValue) - { - return stackReserve == -1 ? defaultValue : (ulong)stackReserve; - } - - public new int __FileAlignment - { - get { return fileAlignment; } - set { fileAlignment = value; } - } - - protected override int GetFileAlignmentImpl() - { - return fileAlignment; - } - - public new DllCharacteristics __DllCharacteristics - { - get { return dllCharacteristics; } - set { dllCharacteristics = value; } - } - - protected override DllCharacteristics GetDllCharacteristicsImpl() - { - return dllCharacteristics; - } - - public override int MDStreamVersion - { - get { return asm.mdStreamVersion; } - } - - private int AddTypeRefByName(int resolutionScope, string ns, string name) - { - TypeRefTable.Record rec = new TypeRefTable.Record(); - rec.ResolutionScope = resolutionScope; - SetTypeNameAndTypeNamespace(new TypeName(ns, name), out rec.TypeName, out rec.TypeNamespace); - return 0x01000000 | this.TypeRef.AddRecord(rec); - } - - public void __Save(PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - SaveImpl(null, portableExecutableKind, imageFileMachine); - } - - public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0) - { - throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream"); - } - SaveImpl(stream, portableExecutableKind, imageFileMachine); - } - - private void SaveImpl(Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) - { - SetIsSaved(); - PopulatePropertyAndEventTables(); - IList attributes = asm.GetCustomAttributesData(null); - if (attributes.Count > 0) - { - int mscorlib = ImportAssemblyRef(universe.CoreLib); - int[] placeholderTokens = new int[4]; - string[] placeholderTypeNames = new string[] { "AssemblyAttributesGoHere", "AssemblyAttributesGoHereM", "AssemblyAttributesGoHereS", "AssemblyAttributesGoHereSM" }; - foreach (CustomAttributeData cad in attributes) - { - int index; - if (cad.Constructor.DeclaringType.BaseType == universe.System_Security_Permissions_CodeAccessSecurityAttribute) - { - if (cad.Constructor.DeclaringType.IsAllowMultipleCustomAttribute) - { - index = 3; - } - else - { - index = 2; - } - } - else if (cad.Constructor.DeclaringType.IsAllowMultipleCustomAttribute) - { - index = 1; - } - else - { - index = 0; - } - if (placeholderTokens[index] == 0) - { - // we manually add a TypeRef without looking it up in mscorlib, because Mono and Silverlight's mscorlib don't have these types - placeholderTokens[index] = AddTypeRefByName(mscorlib, "System.Runtime.CompilerServices", placeholderTypeNames[index]); - } - SetCustomAttribute(placeholderTokens[index], cad.__ToBuilder()); - } - } - FillAssemblyRefTable(); - EmitResources(); - ModuleWriter.WriteModule(null, null, this, PEFileKinds.Dll, portableExecutableKind, imageFileMachine, unmanagedResources, 0, streamOrNull); - CloseResources(); - } - - public void __AddAssemblyReference(AssemblyName assemblyName) - { - __AddAssemblyReference(assemblyName, null); - } - - public void __AddAssemblyReference(AssemblyName assemblyName, Assembly assembly) - { - if (referencedAssemblyNames == null) - { - referencedAssemblyNames = new List(); - } - referencedAssemblyNames.Add((AssemblyName)assemblyName.Clone()); - int token = FindOrAddAssemblyRef(assemblyName, true); - if (assembly != null) - { - referencedAssemblies.Add(assembly, token); - } - } - - public override AssemblyName[] __GetReferencedAssemblies() - { - List list = new List(); - if (referencedAssemblyNames != null) - { - foreach (AssemblyName name in referencedAssemblyNames) - { - if (!list.Contains(name)) - { - list.Add(name); - } - } - } - foreach (Assembly asm in referencedAssemblies.Keys) - { - AssemblyName name = asm.GetName(); - if (!list.Contains(name)) - { - list.Add(name); - } - } - return list.ToArray(); - } - - public void __AddModuleReference(string module) - { - this.ModuleRef.FindOrAddRecord(module == null ? 0 : this.Strings.Add(module)); - } - - public override string[] __GetReferencedModules() - { - string[] arr = new string[this.ModuleRef.RowCount]; - for (int i = 0; i < arr.Length; i++) - { - arr[i] = this.Strings.Find(this.ModuleRef.records[i]); - } - return arr; - } - - public override Type[] __GetReferencedTypes() - { - List list = new List(); - foreach (KeyValuePair kv in typeTokens) - { - if (kv.Value >> 24 == TypeRefTable.Index) - { - list.Add(kv.Key); - } - } - return list.ToArray(); - } - - public override Type[] __GetExportedTypes() - { - throw new NotImplementedException(); - } - - public int __AddModule(int flags, string name, byte[] hash) - { - FileTable.Record file = new FileTable.Record(); - file.Flags = flags; - file.Name = this.Strings.Add(name); - file.HashValue = this.Blobs.Add(ByteBuffer.Wrap(hash)); - return 0x26000000 + this.File.AddRecord(file); - } - - public int __AddManifestResource(int offset, ResourceAttributes flags, string name, int implementation) - { - ManifestResourceTable.Record res = new ManifestResourceTable.Record(); - res.Offset = offset; - res.Flags = (int)flags; - res.Name = this.Strings.Add(name); - res.Implementation = implementation; - return 0x28000000 + this.ManifestResource.AddRecord(res); - } - - public void __SetCustomAttributeFor(int token, CustomAttributeBuilder customBuilder) - { - SetCustomAttribute(token, customBuilder); - } - - public RelativeVirtualAddress __AddVTableFixups(MethodBuilder[] methods, int type) - { - initializedData.Align(8); - VTableFixups fixups; - fixups.initializedDataOffset = (uint)initializedData.Position; - fixups.count = (ushort)methods.Length; - fixups.type = (ushort)type; - foreach (MethodBuilder mb in methods) - { - initializedData.Write(mb.MetadataToken); - if (fixups.SlotWidth == 8) - { - initializedData.Write(0); - } - } - vtablefixups.Add(fixups); - return new RelativeVirtualAddress(fixups.initializedDataOffset); - } - - public void __AddUnmanagedExportStub(string name, int ordinal, RelativeVirtualAddress rva) - { - AddUnmanagedExport(name, ordinal, null, rva); - } - - internal void AddUnmanagedExport(string name, int ordinal, MethodBuilder methodBuilder, RelativeVirtualAddress rva) - { - UnmanagedExport export; - export.name = name; - export.ordinal = ordinal; - export.mb = methodBuilder; - export.rva = rva; - unmanagedExports.Add(export); - } - - internal void SetInterfaceImplementationCustomAttribute(TypeBuilder typeBuilder, Type interfaceType, CustomAttributeBuilder cab) - { - // NOTE since interfaceimpls are extremely common and custom attributes on them are extremely rare, - // we store (and resolve) the custom attributes in such away as to avoid impacting the common case performance - if (interfaceImplCustomAttributes == null) - { - interfaceImplCustomAttributes = new List(); - } - InterfaceImplCustomAttribute rec; - rec.type = typeBuilder.MetadataToken; - int token = GetTypeToken(interfaceType).Token; - switch (token >> 24) - { - case TypeDefTable.Index: - token = (token & 0xFFFFFF) << 2 | 0; - break; - case TypeRefTable.Index: - token = (token & 0xFFFFFF) << 2 | 1; - break; - case TypeSpecTable.Index: - token = (token & 0xFFFFFF) << 2 | 2; - break; - default: - throw new InvalidOperationException(); - } - rec.interfaceType = token; - rec.pseudoToken = AllocPseudoToken(); - interfaceImplCustomAttributes.Add(rec); - SetCustomAttribute(rec.pseudoToken, cab); - } - - internal void ResolveInterfaceImplPseudoTokens() - { - if (interfaceImplCustomAttributes != null) - { - foreach (InterfaceImplCustomAttribute rec in interfaceImplCustomAttributes) - { - for (int i = 0; i < InterfaceImpl.records.Length; i++) - { - if (InterfaceImpl.records[i].Class == rec.type && InterfaceImpl.records[i].Interface == rec.interfaceType) - { - RegisterTokenFixup(rec.pseudoToken, (InterfaceImplTable.Index << 24) | (i + 1)); - break; - } - } - } - } - } - - internal void FixupPseudoToken(ref int token) - { - if (IsPseudoToken(token)) - { - token = ResolvePseudoToken(token); - } - } - - internal void SetIsSaved() - { - if (saved) - { - throw new InvalidOperationException(); - } - saved = true; - } - - internal bool IsSaved - { - get { return saved; } - } - - internal override string GetString(int index) - { - return this.Strings.Find(index); - } - } - - struct UnmanagedExport - { - internal string name; - internal int ordinal; - internal RelativeVirtualAddress rva; - internal MethodBuilder mb; - } - - public struct RelativeVirtualAddress - { - internal readonly uint initializedDataOffset; - - internal RelativeVirtualAddress(uint initializedDataOffset) - { - this.initializedDataOffset = initializedDataOffset; - } - - public static RelativeVirtualAddress operator +(RelativeVirtualAddress rva, int offset) - { - return new RelativeVirtualAddress(rva.initializedDataOffset + (uint)offset); - } - } - - class ArrayMethod : MethodInfo - { - private readonly Module module; - private readonly Type arrayClass; - private readonly string methodName; - private readonly CallingConventions callingConvention; - private readonly Type returnType; - protected readonly Type[] parameterTypes; - private MethodSignature methodSignature; - - internal ArrayMethod(Module module, Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) - { - this.module = module; - this.arrayClass = arrayClass; - this.methodName = methodName; - this.callingConvention = callingConvention; - this.returnType = returnType ?? module.universe.System_Void; - this.parameterTypes = Util.Copy(parameterTypes); - } - - public override MethodBody GetMethodBody() - { - throw new InvalidOperationException(); - } - - public override int __MethodRVA - { - get { throw new InvalidOperationException(); } - } - - public override MethodImplAttributes GetMethodImplementationFlags() - { - throw new NotSupportedException(); - } - - public override ParameterInfo[] GetParameters() - { - throw new NotSupportedException(); - } - - internal override int ImportTo(ModuleBuilder module) - { - return module.ImportMethodOrField(arrayClass, methodName, MethodSignature); - } - - public override MethodAttributes Attributes - { - get { throw new NotSupportedException(); } - } - - public override CallingConventions CallingConvention - { - get { return callingConvention; } - } - - public override Type DeclaringType - { - get { return arrayClass; } - } - - internal override MethodSignature MethodSignature - { - get - { - if (methodSignature == null) - { - methodSignature = MethodSignature.MakeFromBuilder(returnType, parameterTypes, new PackedCustomModifiers(), callingConvention, 0); - } - return methodSignature; - } - } - - public override Module Module - { - // FXBUG like .NET, we return the module that GetArrayMethod was called on, not the module associated with the array type - get { return module; } - } - - public override string Name - { - get { return methodName; } - } - - internal override int ParameterCount - { - get { return parameterTypes.Length; } - } - - public override ParameterInfo ReturnParameter - { - // FXBUG like .NET, we throw NotImplementedException - get { throw new NotImplementedException(); } - } - - public override Type ReturnType - { - get { return returnType; } - } - - internal override bool HasThis - { - get { return (callingConvention & (CallingConventions.HasThis | CallingConventions.ExplicitThis)) == CallingConventions.HasThis; } - } - - internal override int GetCurrentToken() - { - return this.MetadataToken; - } - - internal override bool IsBaked - { - get { return arrayClass.IsBaked; } - } - } + { + + static readonly bool usePublicKeyAssemblyReference = false; + + Guid mvid; + uint timestamp; + long imageBaseAddress = 0x00400000; + long stackReserve = -1; + int fileAlignment = 0x200; + DllCharacteristics dllCharacteristics = DllCharacteristics.DynamicBase | DllCharacteristics.NoSEH | DllCharacteristics.NXCompat | DllCharacteristics.TerminalServerAware; + readonly AssemblyBuilder asm; + internal readonly string moduleName; + internal readonly string fileName; + internal readonly ISymbolWriterImpl symbolWriter; + readonly TypeBuilder moduleType; + readonly List types = new List(); + readonly Dictionary typeTokens = new Dictionary(); + readonly Dictionary memberRefTypeTokens = new Dictionary(); + internal readonly ByteBuffer methodBodies = new ByteBuffer(128 * 1024); + internal readonly List tokenFixupOffsets = new List(); + internal readonly ByteBuffer initializedData = new ByteBuffer(512); + internal ResourceSection unmanagedResources; + readonly Dictionary importedMemberRefs = new Dictionary(); + readonly Dictionary importedMethodSpecs = new Dictionary(); + readonly Dictionary referencedAssemblies = new Dictionary(); + List referencedAssemblyNames; + int nextPseudoToken = -1; + readonly List resolvedTokens = new List(); + internal readonly TableHeap Tables = new TableHeap(); + internal readonly StringHeap Strings = new StringHeap(); + internal readonly UserStringHeap UserStrings = new UserStringHeap(); + internal readonly GuidHeap Guids = new GuidHeap(); + internal readonly BlobHeap Blobs = new BlobHeap(); + internal readonly List vtablefixups = new List(); + internal readonly List unmanagedExports = new List(); + List interfaceImplCustomAttributes; + readonly List resourceWriters = new List(); + bool saved; + + struct ResourceWriterRecord + { + + readonly string name; + readonly ResourceWriter rw; + readonly Stream stream; + readonly ResourceAttributes attributes; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal ResourceWriterRecord(string name, Stream stream, ResourceAttributes attributes) : + this(name, null, stream, attributes) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + internal ResourceWriterRecord(string name, ResourceWriter rw, Stream stream, ResourceAttributes attributes) + { + this.name = name; + this.rw = rw; + this.stream = stream; + this.attributes = attributes; + } + + internal void Emit(ModuleBuilder mb, int offset) + { + if (rw != null) + rw.Generate(); + + var rec = new ManifestResourceTable.Record(); + rec.Offset = offset; + rec.Flags = (int)attributes; + rec.Name = mb.Strings.Add(name); + rec.Implementation = 0; + mb.ManifestResource.AddRecord(rec); + } + + internal int GetLength() + { + return 4 + (int)stream.Length; + } + + internal void Write(MetadataWriter mw) + { + mw.Write((int)stream.Length); + stream.Position = 0; + byte[] buffer = new byte[8192]; + int length; + while ((length = stream.Read(buffer, 0, buffer.Length)) != 0) + mw.Write(buffer, 0, length); + } + + internal void Close() + { + if (rw != null) + rw.Close(); + } + + } + + internal struct VTableFixups + { + + internal uint initializedDataOffset; + internal ushort count; + internal ushort type; + + internal int SlotWidth + { + get { return (type & 0x02) == 0 ? 4 : 8; } + } + + } + + struct InterfaceImplCustomAttribute + { + + internal int type; + internal int interfaceType; + internal int pseudoToken; + + } + + struct MemberRefKey : IEquatable + { + + readonly Type type; + readonly string name; + readonly Signature signature; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal MemberRefKey(Type type, string name, Signature signature) + { + this.type = type; + this.name = name; + this.signature = signature; + } + + public bool Equals(MemberRefKey other) + { + return other.type.Equals(type) + && other.name == name + && other.signature.Equals(signature); + } + + public override bool Equals(object obj) + { + MemberRefKey? other = obj as MemberRefKey?; + return other != null && Equals(other.Value); + } + + public override int GetHashCode() + { + return type.GetHashCode() + name.GetHashCode() + signature.GetHashCode(); + } + + internal MethodBase LookupMethod() + { + return type.FindMethod(name, (MethodSignature)signature); + } + + } + + struct MethodSpecKey : IEquatable + { + + readonly Type type; + readonly string name; + readonly MethodSignature signature; + readonly Type[] genericParameters; + + internal MethodSpecKey(Type type, string name, MethodSignature signature, Type[] genericParameters) + { + this.type = type; + this.name = name; + this.signature = signature; + this.genericParameters = genericParameters; + } + + public bool Equals(MethodSpecKey other) + { + return other.type.Equals(type) && other.name == name && other.signature.Equals(signature) && Util.ArrayEquals(other.genericParameters, genericParameters); + } + + public override bool Equals(object obj) + { + var other = obj as MethodSpecKey?; + return other != null && Equals(other.Value); + } + + public override int GetHashCode() + { + return type.GetHashCode() + name.GetHashCode() + signature.GetHashCode() + Util.GetHashCode(genericParameters); + } + + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + internal ModuleBuilder(AssemblyBuilder asm, string moduleName, string fileName, bool emitSymbolInfo) : base(asm.universe) + { + this.asm = asm; + this.moduleName = moduleName; + this.fileName = fileName; + if (emitSymbolInfo) + { + symbolWriter = SymbolSupport.CreateSymbolWriterFor(this); + if (universe.Deterministic && !symbolWriter.IsDeterministic) + throw new NotSupportedException(); + } + + if (!universe.Deterministic) + { + __PEHeaderTimeDateStamp = DateTime.UtcNow; + mvid = Guid.NewGuid(); + } + + // must be the first record in the TypeDef table + moduleType = new TypeBuilder(this, null, ""); + types.Add(moduleType); + } + + internal void PopulatePropertyAndEventTables() + { + // LAMESPEC the PropertyMap and EventMap tables are not required to be sorted by the CLI spec, + // but .NET sorts them and Mono requires them to be sorted, so we have to populate the + // tables in the right order + foreach (var type in types) + type.PopulatePropertyAndEventTables(); + } + + internal void WriteTypeDefTable(MetadataWriter mw) + { + int fieldList = 1; + int methodList = 1; + foreach (var type in types) + type.WriteTypeDefRecord(mw, ref fieldList, ref methodList); + } + + internal void WriteMethodDefTable(int baseRVA, MetadataWriter mw) + { + int paramList = 1; + foreach (var type in types) + type.WriteMethodDefRecords(baseRVA, mw, ref paramList); + } + + internal void WriteParamTable(MetadataWriter mw) + { + foreach (var type in types) + type.WriteParamRecords(mw); + } + + internal void WriteFieldTable(MetadataWriter mw) + { + foreach (var type in types) + type.WriteFieldRecords(mw); + } + + internal int AllocPseudoToken() + { + return nextPseudoToken--; + } + + public TypeBuilder DefineType(string name) + { + return DefineType(name, TypeAttributes.Class); + } + + public TypeBuilder DefineType(string name, TypeAttributes attr) + { + return DefineType(name, attr, null); + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent) + { + return DefineType(name, attr, parent, PackingSize.Unspecified, 0); + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, int typesize) + { + return DefineType(name, attr, parent, PackingSize.Unspecified, typesize); + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, PackingSize packsize) + { + return DefineType(name, attr, parent, packsize, 0); + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces) + { + var tb = DefineType(name, attr, parent); + foreach (Type iface in interfaces) + tb.AddInterfaceImplementation(iface); + + return tb; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, PackingSize packingSize, int typesize) + { + string ns = null; + int lastdot = name.LastIndexOf('.'); + if (lastdot > 0) + { + ns = name.Substring(0, lastdot); + name = name.Substring(lastdot + 1); + } + + var typeBuilder = __DefineType(ns, name); + typeBuilder.__SetAttributes(attr); + typeBuilder.SetParent(parent); + if (packingSize != PackingSize.Unspecified || typesize != 0) + typeBuilder.__SetLayout((int)packingSize, typesize); + + return typeBuilder; + } + + public TypeBuilder __DefineType(string ns, string name) + { + return DefineType(this, ns, name); + } + + internal TypeBuilder DefineType(ITypeOwner owner, string ns, string name) + { + var typeBuilder = new TypeBuilder(owner, ns, name); + types.Add(typeBuilder); + return typeBuilder; + } + + public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type underlyingType) + { + var tb = DefineType(name, (visibility & TypeAttributes.VisibilityMask) | TypeAttributes.Sealed, universe.System_Enum); + var fb = tb.DefineField("value__", underlyingType, FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); + return new EnumBuilder(tb, fb); + } + + public FieldBuilder __DefineField(string name, Type type, CustomModifiers customModifiers, FieldAttributes attributes) + { + return moduleType.__DefineField(name, type, customModifiers, attributes); + } + + [Obsolete("Please use __DefineField(string, Type, CustomModifiers, FieldAttributes) instead.")] + public FieldBuilder __DefineField(string name, Type type, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, FieldAttributes attributes) + { + return moduleType.DefineField(name, type, requiredCustomModifiers, optionalCustomModifiers, attributes); + } + + public ConstructorBuilder __DefineModuleInitializer(MethodAttributes visibility) + { + return moduleType.DefineConstructor(visibility | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, Type.EmptyTypes); + } + + public FieldBuilder DefineUninitializedData(string name, int size, FieldAttributes attributes) + { + return moduleType.DefineUninitializedData(name, size, attributes); + } + + public FieldBuilder DefineInitializedData(string name, byte[] data, FieldAttributes attributes) + { + return moduleType.DefineInitializedData(name, data, attributes); + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes) + { + return moduleType.DefineMethod(name, attributes, returnType, parameterTypes); + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return moduleType.DefineMethod(name, attributes, callingConvention, returnType, parameterTypes); + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) + { + return moduleType.DefineMethod(name, attributes, callingConvention, returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers, parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers); + } + + public MethodBuilder DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return moduleType.DefinePInvokeMethod(name, dllName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); + } + + public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return moduleType.DefinePInvokeMethod(name, dllName, entryName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); + } + + public void CreateGlobalFunctions() + { + moduleType.CreateType(); + } + + internal void AddTypeForwarder(Type type, bool includeNested) + { + ExportType(type); + if (includeNested && !type.__IsMissing) + { + foreach (Type nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)) + { + // we export all nested types (i.e. even the private ones) + // (this behavior is the same as the C# compiler) + AddTypeForwarder(nested, true); + } + } + } + + int ExportType(Type type) + { + var rec = new ExportedTypeTable.Record(); + if (asm.ImageRuntimeVersion == "v2.0.50727") + { + // HACK we should *not* set the TypeDefId in this case, but 2.0 and 3.5 peverify gives a warning if it is missing (4.5 doesn't) + rec.TypeDefId = type.MetadataToken; + } + + SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); + if (type.IsNested) + { + rec.Flags = 0; + rec.Implementation = ExportType(type.DeclaringType); + } + else + { + rec.Flags = 0x00200000; // CorTypeAttr.tdForwarder + rec.Implementation = ImportAssemblyRef(type.Assembly); + } + return 0x27000000 | this.ExportedType.FindOrAddRecord(rec); + } + + void SetTypeNameAndTypeNamespace(TypeName name, out int typeName, out int typeNamespace) + { + typeName = this.Strings.Add(name.Name); + typeNamespace = name.Namespace == null ? 0 : this.Strings.Add(name.Namespace); + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute)); + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + SetCustomAttribute(0x00000001, customBuilder); + } + + internal void SetCustomAttribute(int token, CustomAttributeBuilder customBuilder) + { + var rec = new CustomAttributeTable.Record(); + rec.Parent = token; + rec.Type = asm.IsWindowsRuntime ? customBuilder.Constructor.ImportTo(this) : GetConstructorToken(customBuilder.Constructor).Token; + rec.Value = customBuilder.WriteBlob(this); + CustomAttribute.AddRecord(rec); + } + + private void AddDeclSecurityRecord(int token, int action, int blob) + { + var rec = new DeclSecurityTable.Record(); + rec.Action = (short)action; + rec.Parent = token; + rec.PermissionSet = blob; + DeclSecurity.AddRecord(rec); + } + + internal void AddDeclarativeSecurity(int token, System.Security.Permissions.SecurityAction securityAction, System.Security.PermissionSet permissionSet) + { + AddDeclSecurityRecord(token, (int)securityAction, Blobs.Add(ByteBuffer.Wrap(System.Text.Encoding.Unicode.GetBytes(permissionSet.ToXml().ToString())))); + } + + internal void AddDeclarativeSecurity(int token, List declarativeSecurity) + { + var ordered = new Dictionary>(); + foreach (var cab in declarativeSecurity) + { + int action; + // check for HostProtectionAttribute without SecurityAction + if (cab.ConstructorArgumentCount == 0) + { + action = (int)System.Security.Permissions.SecurityAction.LinkDemand; + } + else + { + action = (int)cab.GetConstructorArgument(0); + } + + if (cab.IsLegacyDeclSecurity) + { + AddDeclSecurityRecord(token, action, cab.WriteLegacyDeclSecurityBlob(this)); + continue; + } + + if (!ordered.TryGetValue(action, out var list)) + { + list = new List(); + ordered.Add(action, list); + } + + list.Add(cab); + } + + foreach (KeyValuePair> kv in ordered) + AddDeclSecurityRecord(token, kv.Key, WriteDeclSecurityBlob(kv.Value)); + } + + int WriteDeclSecurityBlob(List list) + { + ByteBuffer namedArgs = new ByteBuffer(100); + ByteBuffer bb = new ByteBuffer(list.Count * 100); + bb.Write((byte)'.'); + bb.WriteCompressedUInt(list.Count); + foreach (CustomAttributeBuilder cab in list) + { + bb.Write(cab.Constructor.DeclaringType.AssemblyQualifiedName); + namedArgs.Clear(); + cab.WriteNamedArgumentsForDeclSecurity(this, namedArgs); + bb.WriteCompressedUInt(namedArgs.Length); + bb.Write(namedArgs); + } + return this.Blobs.Add(bb); + } + + public void DefineManifestResource(string name, Stream stream, ResourceAttributes attribute) + { + resourceWriters.Add(new ResourceWriterRecord(name, stream, attribute)); + } + + public IResourceWriter DefineResource(string name, string description) + { + return DefineResource(name, description, ResourceAttributes.Public); + } + + public IResourceWriter DefineResource(string name, string description, ResourceAttributes attribute) + { + // FXBUG we ignore the description, because there is no such thing + var mem = new MemoryStream(); + var rw = new ResourceWriter(mem); + resourceWriters.Add(new ResourceWriterRecord(name, rw, mem, attribute)); + return rw; + } + + internal void EmitResources() + { + int offset = 0; + foreach (var rwr in resourceWriters) + { + // resources must be 8-byte aligned + offset = (offset + 7) & ~7; + rwr.Emit(this, offset); + offset += rwr.GetLength(); + } + } + + internal void WriteResources(MetadataWriter mw) + { + int offset = 0; + foreach (var rwr in resourceWriters) + { + // resources must be 8-byte aligned + int alignment = ((offset + 7) & ~7) - offset; + for (int i = 0; i < alignment; i++) + mw.Write((byte)0); + rwr.Write(mw); + offset += rwr.GetLength() + alignment; + } + } + + internal void CloseResources() + { + foreach (var rwr in resourceWriters) + rwr.Close(); + } + + internal int GetManifestResourcesLength() + { + int length = 0; + foreach (var rwr in resourceWriters) + { + // resources must be 8-byte aligned + length = (length + 7) & ~7; + length += rwr.GetLength(); + } + + return length; + } + + public override Assembly Assembly + { + get { return asm; } + } + + internal override Type FindType(TypeName name) + { + foreach (var type in types) + if (type.TypeName == name) + return type; + + return null; + } + + internal override Type FindTypeIgnoreCase(TypeName lowerCaseName) + { + foreach (var type in types) + if (type.TypeName.ToLowerInvariant() == lowerCaseName) + return type; + + return null; + } + + internal override void GetTypesImpl(List list) + { + foreach (var type in types) + if (type != moduleType) + list.Add(type); + } + + public ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) + { + return symbolWriter.DefineDocument(url, language, languageVendor, documentType); + } + + public int __GetAssemblyToken(Assembly assembly) + { + return ImportAssemblyRef(assembly); + } + + public TypeToken GetTypeToken(string name) + { + return new TypeToken(GetType(name, true, false).MetadataToken); + } + + public TypeToken GetTypeToken(Type type) + { + if (type.Module == this && !asm.IsWindowsRuntime) + return new TypeToken(type.GetModuleBuilderToken()); + else + return new TypeToken(ImportType(type)); + } + + internal int GetTypeTokenForMemberRef(Type type) + { + if (type.__IsMissing) + { + return ImportType(type); + } + else if (type.IsGenericTypeDefinition) + { + if (!memberRefTypeTokens.TryGetValue(type, out var token)) + { + var spec = new ByteBuffer(5); + Signature.WriteTypeSpec(this, spec, type); + token = 0x1B000000 | this.TypeSpec.AddRecord(this.Blobs.Add(spec)); + memberRefTypeTokens.Add(type, token); + } + return token; + } + else if (type.IsModulePseudoType) + { + return 0x1A000000 | this.ModuleRef.FindOrAddRecord(this.Strings.Add(type.Module.ScopeName)); + } + else + { + return GetTypeToken(type).Token; + } + } + + static bool IsFromGenericTypeDefinition(MemberInfo member) + { + var decl = member.DeclaringType; + return decl != null && !decl.__IsMissing && decl.IsGenericTypeDefinition; + } + + public FieldToken GetFieldToken(FieldInfo field) + { + // NOTE for some reason, when TypeBuilder.GetFieldToken() is used on a field in a generic type definition, + // a memberref token is returned (confirmed on .NET) unlike for Get(Method|Constructor)Token which always + // simply returns the MethodDef token (if the method is from the same module). + var fb = field as FieldBuilder; + if (fb != null && fb.Module == this && !IsFromGenericTypeDefinition(fb)) + { + return new FieldToken(fb.MetadataToken); + } + else + { + return new FieldToken(field.ImportTo(this)); + } + } + + public MethodToken GetMethodToken(MethodInfo method) + { + var mb = method as MethodBuilder; + if (mb != null && mb.ModuleBuilder == this) + return new MethodToken(mb.MetadataToken); + else + return new MethodToken(method.ImportTo(this)); + } + + public MethodToken GetMethodToken(MethodInfo method, IEnumerable optionalParameterTypes) + { + return __GetMethodToken(method, Util.ToArray(optionalParameterTypes), null); + } + + public MethodToken __GetMethodToken(MethodInfo method, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) + { + var sig = new ByteBuffer(16); + method.MethodSignature.WriteMethodRefSig(this, sig, optionalParameterTypes, customModifiers); + + var record = new MemberRefTable.Record(); + record.Class = method.Module == this ? method.MetadataToken : GetTypeTokenForMemberRef(method.DeclaringType ?? method.Module.GetModuleType()); + record.Name = Strings.Add(method.Name); + record.Signature = Blobs.Add(sig); + return new MethodToken(0x0A000000 | MemberRef.FindOrAddRecord(record)); + } + + // when we refer to a method on a generic type definition in the IL stream, + // we need to use a MemberRef (even if the method is in the same module) + internal MethodToken GetMethodTokenForIL(MethodInfo method) + { + if (method.IsGenericMethodDefinition) + method = method.MakeGenericMethod(method.GetGenericArguments()); + + if (IsFromGenericTypeDefinition(method)) + return new MethodToken(method.ImportTo(this)); + else + return GetMethodToken(method); + } + + internal int GetMethodTokenWinRT(MethodInfo method) + { + return asm.IsWindowsRuntime ? method.ImportTo(this) : GetMethodToken(method).Token; + } + + public MethodToken GetConstructorToken(ConstructorInfo constructor) + { + return GetMethodToken(constructor.GetMethodInfo()); + } + + public MethodToken GetConstructorToken(ConstructorInfo constructor, IEnumerable optionalParameterTypes) + { + return GetMethodToken(constructor.GetMethodInfo(), optionalParameterTypes); + } + + public MethodToken __GetConstructorToken(ConstructorInfo constructor, Type[] optionalParameterTypes, CustomModifiers[] customModifiers) + { + return __GetMethodToken(constructor.GetMethodInfo(), optionalParameterTypes, customModifiers); + } + + internal int ImportMethodOrField(Type declaringType, string name, Signature sig) + { + var key = new MemberRefKey(declaringType, name, sig); + if (!importedMemberRefs.TryGetValue(key, out var token)) + { + var rec = new MemberRefTable.Record(); + rec.Class = GetTypeTokenForMemberRef(declaringType); + rec.Name = Strings.Add(name); + var bb = new ByteBuffer(16); + sig.WriteSig(this, bb); + rec.Signature = Blobs.Add(bb); + token = 0x0A000000 | MemberRef.AddRecord(rec); + importedMemberRefs.Add(key, token); + } + + return token; + } + + internal int ImportMethodSpec(Type declaringType, MethodInfo method, Type[] genericParameters) + { + Debug.Assert(method.__IsMissing || method.GetMethodOnTypeDefinition() == method); + + var key = new MethodSpecKey(declaringType, method.Name, method.MethodSignature, genericParameters); + if (!importedMethodSpecs.TryGetValue(key, out var token)) + { + var rec = new MethodSpecTable.Record(); + var mb = method as MethodBuilder; + if (mb != null && mb.ModuleBuilder == this && !declaringType.IsGenericType) + { + rec.Method = mb.MetadataToken; + } + else + { + // we're calling ImportMethodOrField directly here, because 'method' may be a MethodDef on a generic TypeDef and 'declaringType' the type instance + // (in order words the method and type have already been decoupled by the caller) + rec.Method = ImportMethodOrField(declaringType, method.Name, method.MethodSignature); + } + var spec = new ByteBuffer(10); + Signature.WriteMethodSpec(this, spec, genericParameters); + rec.Instantiation = Blobs.Add(spec); + token = 0x2B000000 | MethodSpec.FindOrAddRecord(rec); + importedMethodSpecs.Add(key, token); + } + + return token; + } + + internal int ImportType(Type type) + { + int token; + if (!typeTokens.TryGetValue(type, out token)) + { + if (type.HasElementType || type.IsConstructedGenericType || type.__IsFunctionPointer) + { + var spec = new ByteBuffer(5); + Signature.WriteTypeSpec(this, spec, type); + token = 0x1B000000 | this.TypeSpec.AddRecord(this.Blobs.Add(spec)); + } + else + { + var rec = new TypeRefTable.Record(); + if (type.IsNested) + rec.ResolutionScope = GetTypeToken(type.DeclaringType).Token; + else if (type.Module == this) + rec.ResolutionScope = 1; + else + rec.ResolutionScope = ImportAssemblyRef(type.Assembly); + + SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); + token = 0x01000000 | this.TypeRef.AddRecord(rec); + } + typeTokens.Add(type, token); + } + + return token; + } + + int ImportAssemblyRef(Assembly asm) + { + if (!referencedAssemblies.TryGetValue(asm, out var token)) + { + // We can't write the AssemblyRef record here yet, because the identity of the assembly can still change + // (if it's an AssemblyBuilder). + token = AllocPseudoToken(); + referencedAssemblies.Add(asm, token); + } + + return token; + } + + internal void FillAssemblyRefTable() + { + foreach (var kv in referencedAssemblies) + if (IsPseudoToken(kv.Value)) + RegisterTokenFixup(kv.Value, FindOrAddAssemblyRef(kv.Key.GetName(), false)); + } + + private int FindOrAddAssemblyRef(AssemblyName name, bool alwaysAdd) + { + var rec = new AssemblyRefTable.Record(); + var ver = name.Version ?? new Version(0, 0, 0, 0); + rec.MajorVersion = (ushort)ver.Major; + rec.MinorVersion = (ushort)ver.Minor; + rec.BuildNumber = (ushort)ver.Build; + rec.RevisionNumber = (ushort)ver.Revision; + rec.Flags = (int)(name.Flags & ~AssemblyNameFlags.PublicKey); + const AssemblyNameFlags afPA_Specified = (AssemblyNameFlags)0x0080; + const AssemblyNameFlags afPA_Mask = (AssemblyNameFlags)0x0070; + if ((name.RawFlags & afPA_Specified) != 0) + { + rec.Flags |= (int)(name.RawFlags & afPA_Mask); + } + if (name.ContentType == AssemblyContentType.WindowsRuntime) + { + rec.Flags |= 0x0200; + } + byte[] publicKeyOrToken = null; + if (usePublicKeyAssemblyReference) + { + publicKeyOrToken = name.GetPublicKey(); + } + if (publicKeyOrToken == null || publicKeyOrToken.Length == 0) + { + publicKeyOrToken = name.GetPublicKeyToken() ?? Empty.Array; + } + else + { + const int PublicKey = 0x0001; + rec.Flags |= PublicKey; + } + rec.PublicKeyOrToken = this.Blobs.Add(ByteBuffer.Wrap(publicKeyOrToken)); + rec.Name = this.Strings.Add(name.Name); + rec.Culture = name.CultureName == null ? 0 : this.Strings.Add(name.CultureName); + if (name.hash != null) + { + rec.HashValue = this.Blobs.Add(ByteBuffer.Wrap(name.hash)); + } + else + { + rec.HashValue = 0; + } + + return 0x23000000 | (alwaysAdd ? this.AssemblyRef.AddRecord(rec) : this.AssemblyRef.FindOrAddRecord(rec)); + } + + internal void WriteSymbolTokenMap() + { + for (int i = 0; i < resolvedTokens.Count; i++) + { + int newToken = resolvedTokens[i]; + // The symbol API doesn't support remapping arbitrary integers, the types have to be the same, + // so we copy the type from the newToken, because our pseudo tokens don't have a type. + // (see MethodToken.SymbolToken) + int oldToken = (i + 1) | (newToken & ~0xFFFFFF); + SymbolSupport.RemapToken(symbolWriter, oldToken, newToken); + } + } + + internal void RegisterTokenFixup(int pseudoToken, int realToken) + { + int index = -(pseudoToken + 1); + while (resolvedTokens.Count <= index) + resolvedTokens.Add(0); + + resolvedTokens[index] = realToken; + } + + internal static bool IsPseudoToken(int token) + { + return token < 0; + } + + internal int ResolvePseudoToken(int pseudoToken) + { + int index = -(pseudoToken + 1); + return resolvedTokens[index]; + } + + internal void ApplyUnmanagedExports(ImageFileMachine imageFileMachine) + { + if (unmanagedExports.Count != 0) + { + int type; + int size; + switch (imageFileMachine) + { + case ImageFileMachine.I386: + case ImageFileMachine.ARM: + type = 0x05; + size = 4; + break; + case ImageFileMachine.AMD64: + type = 0x06; + size = 8; + break; + default: + throw new NotSupportedException(); + } + + var methods = new List(); + for (int i = 0; i < unmanagedExports.Count; i++) + if (unmanagedExports[i].mb != null) + methods.Add(unmanagedExports[i].mb); + + if (methods.Count != 0) + { + var rva = __AddVTableFixups(methods.ToArray(), type); + for (int i = 0; i < unmanagedExports.Count; i++) + { + if (unmanagedExports[i].mb != null) + { + var exp = unmanagedExports[i]; + exp.rva = new RelativeVirtualAddress(rva.initializedDataOffset + (uint)(methods.IndexOf(unmanagedExports[i].mb) * size)); + unmanagedExports[i] = exp; + } + } + } + } + } + + internal void FixupMethodBodyTokens() + { + int methodToken = 0x06000001; + int fieldToken = 0x04000001; + int parameterToken = 0x08000001; + foreach (TypeBuilder type in types) + { + type.ResolveMethodAndFieldTokens(ref methodToken, ref fieldToken, ref parameterToken); + } + foreach (int offset in tokenFixupOffsets) + { + methodBodies.Position = offset; + int pseudoToken = methodBodies.GetInt32AtCurrentPosition(); + methodBodies.Write(ResolvePseudoToken(pseudoToken)); + } + foreach (VTableFixups fixup in vtablefixups) + { + for (int i = 0; i < fixup.count; i++) + { + initializedData.Position = (int)fixup.initializedDataOffset + i * fixup.SlotWidth; + initializedData.Write(ResolvePseudoToken(initializedData.GetInt32AtCurrentPosition())); + } + } + } + + int GetHeaderLength() + { + return + 4 + // Signature + 2 + // MajorVersion + 2 + // MinorVersion + 4 + // Reserved + 4 + // ImageRuntimeVersion Length + StringToPaddedUTF8Length(asm.ImageRuntimeVersion) + + 2 + // Flags + 2 + // Streams + 4 + // #~ Offset + 4 + // #~ Size + 4 + // StringToPaddedUTF8Length("#~") + 4 + // #Strings Offset + 4 + // #Strings Size + 12 + // StringToPaddedUTF8Length("#Strings") + 4 + // #US Offset + 4 + // #US Size + 4 + // StringToPaddedUTF8Length("#US") + 4 + // #GUID Offset + 4 + // #GUID Size + 8 + // StringToPaddedUTF8Length("#GUID") + (Blobs.IsEmpty ? 0 : + ( + 4 + // #Blob Offset + 4 + // #Blob Size + 8 // StringToPaddedUTF8Length("#Blob") + )); + } + + internal int MetadataLength + { + get + { + return GetHeaderLength() + (Blobs.IsEmpty ? 0 : Blobs.Length) + Tables.Length + Strings.Length + UserStrings.Length + Guids.Length; + } + } + + internal void WriteMetadata(MetadataWriter mw, out uint guidHeapOffset) + { + mw.Write(0x424A5342); // Signature ("BSJB") + mw.Write((ushort)1); // MajorVersion + mw.Write((ushort)1); // MinorVersion + mw.Write(0); // Reserved + byte[] version = StringToPaddedUTF8(asm.ImageRuntimeVersion); + mw.Write(version.Length); // Length + mw.Write(version); + mw.Write((ushort)0); // Flags + // #Blob is the only optional heap + if (Blobs.IsEmpty) + { + mw.Write((ushort)4); // Streams + } + else + { + mw.Write((ushort)5); // Streams + } + + int offset = GetHeaderLength(); + + // Streams + mw.Write(offset); // Offset + mw.Write(Tables.Length); // Size + mw.Write(StringToPaddedUTF8("#~")); + offset += Tables.Length; + + mw.Write(offset); // Offset + mw.Write(Strings.Length); // Size + mw.Write(StringToPaddedUTF8("#Strings")); + offset += Strings.Length; + + mw.Write(offset); // Offset + mw.Write(UserStrings.Length); // Size + mw.Write(StringToPaddedUTF8("#US")); + offset += UserStrings.Length; + + mw.Write(offset); // Offset + mw.Write(Guids.Length); // Size + mw.Write(StringToPaddedUTF8("#GUID")); + offset += Guids.Length; + + if (!Blobs.IsEmpty) + { + mw.Write(offset); // Offset + mw.Write(Blobs.Length); // Size + mw.Write(StringToPaddedUTF8("#Blob")); + } + + Tables.Write(mw); + Strings.Write(mw); + UserStrings.Write(mw); + guidHeapOffset = mw.Position; + Guids.Write(mw); + if (!Blobs.IsEmpty) + { + Blobs.Write(mw); + } + } + + static int StringToPaddedUTF8Length(string str) + { + return (System.Text.Encoding.UTF8.GetByteCount(str) + 4) & ~3; + } + + static byte[] StringToPaddedUTF8(string str) + { + byte[] buf = new byte[(System.Text.Encoding.UTF8.GetByteCount(str) + 4) & ~3]; + System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0); + return buf; + } + + internal override void ExportTypes(int fileToken, ModuleBuilder manifestModule) + { + manifestModule.ExportTypes(types.ToArray(), fileToken); + } + + internal void ExportTypes(Type[] types, int fileToken) + { + Dictionary declaringTypes = new Dictionary(); + foreach (Type type in types) + { + if (!type.IsModulePseudoType && IsVisible(type)) + { + ExportedTypeTable.Record rec = new ExportedTypeTable.Record(); + rec.Flags = (int)type.Attributes; + // LAMESPEC ECMA says that TypeDefId is a row index, but it should be a token + rec.TypeDefId = type.MetadataToken; + SetTypeNameAndTypeNamespace(type.TypeName, out rec.TypeName, out rec.TypeNamespace); + if (type.IsNested) + { + rec.Implementation = declaringTypes[type.DeclaringType]; + } + else + { + rec.Implementation = fileToken; + } + int exportTypeToken = 0x27000000 | this.ExportedType.AddRecord(rec); + declaringTypes.Add(type, exportTypeToken); + } + } + } + + private static bool IsVisible(Type type) + { + // NOTE this is not the same as Type.IsVisible, because that doesn't take into account family access + return type.IsPublic || ((type.IsNestedFamily || type.IsNestedFamORAssem || type.IsNestedPublic) && IsVisible(type.DeclaringType)); + } + + internal void AddConstant(int parentToken, object defaultValue) + { + ConstantTable.Record rec = new ConstantTable.Record(); + rec.Parent = parentToken; + ByteBuffer val = new ByteBuffer(16); + if (defaultValue == null) + { + rec.Type = Signature.ELEMENT_TYPE_CLASS; + val.Write((int)0); + } + else if (defaultValue is bool) + { + rec.Type = Signature.ELEMENT_TYPE_BOOLEAN; + val.Write((bool)defaultValue ? (byte)1 : (byte)0); + } + else if (defaultValue is char) + { + rec.Type = Signature.ELEMENT_TYPE_CHAR; + val.Write((char)defaultValue); + } + else if (defaultValue is sbyte) + { + rec.Type = Signature.ELEMENT_TYPE_I1; + val.Write((sbyte)defaultValue); + } + else if (defaultValue is byte) + { + rec.Type = Signature.ELEMENT_TYPE_U1; + val.Write((byte)defaultValue); + } + else if (defaultValue is short) + { + rec.Type = Signature.ELEMENT_TYPE_I2; + val.Write((short)defaultValue); + } + else if (defaultValue is ushort) + { + rec.Type = Signature.ELEMENT_TYPE_U2; + val.Write((ushort)defaultValue); + } + else if (defaultValue is int) + { + rec.Type = Signature.ELEMENT_TYPE_I4; + val.Write((int)defaultValue); + } + else if (defaultValue is uint) + { + rec.Type = Signature.ELEMENT_TYPE_U4; + val.Write((uint)defaultValue); + } + else if (defaultValue is long) + { + rec.Type = Signature.ELEMENT_TYPE_I8; + val.Write((long)defaultValue); + } + else if (defaultValue is ulong) + { + rec.Type = Signature.ELEMENT_TYPE_U8; + val.Write((ulong)defaultValue); + } + else if (defaultValue is float) + { + rec.Type = Signature.ELEMENT_TYPE_R4; + val.Write((float)defaultValue); + } + else if (defaultValue is double) + { + rec.Type = Signature.ELEMENT_TYPE_R8; + val.Write((double)defaultValue); + } + else if (defaultValue is string) + { + rec.Type = Signature.ELEMENT_TYPE_STRING; + foreach (char c in (string)defaultValue) + { + val.Write(c); + } + } + else if (defaultValue is DateTime) + { + rec.Type = Signature.ELEMENT_TYPE_I8; + val.Write(((DateTime)defaultValue).Ticks); + } + else + { + throw new ArgumentException(); + } + rec.Value = this.Blobs.Add(val); + this.Constant.AddRecord(rec); + } + + ModuleBuilder ITypeOwner.ModuleBuilder + { + get { return this; } + } + + internal override Type ResolveType(int metadataToken, IGenericContext context) + { + if (metadataToken >> 24 != TypeDefTable.Index) + { + throw new NotImplementedException(); + } + return types[(metadataToken & 0xFFFFFF) - 1]; + } + + public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + if (genericTypeArguments != null || genericMethodArguments != null) + { + throw new NotImplementedException(); + } + // this method is inefficient, but since it isn't used we don't care + if ((metadataToken >> 24) == MemberRefTable.Index) + { + foreach (KeyValuePair kv in importedMemberRefs) + { + if (kv.Value == metadataToken) + { + return kv.Key.LookupMethod(); + } + } + } + // HACK if we're given a SymbolToken, we need to convert back + if ((metadataToken & 0xFF000000) == 0x06000000) + { + metadataToken = -(metadataToken & 0x00FFFFFF); + } + foreach (Type type in types) + { + MethodBase method = ((TypeBuilder)type).LookupMethod(metadataToken); + if (method != null) + { + return method; + } + } + return ((TypeBuilder)moduleType).LookupMethod(metadataToken); + } + + public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + throw new NotImplementedException(); + } + + public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + throw new NotImplementedException(); + } + + public override string ResolveString(int metadataToken) + { + throw new NotImplementedException(); + } + + public override string FullyQualifiedName + { + get { return Path.GetFullPath(Path.Combine(asm.dir, fileName)); } + } + + public override string Name + { + get { return fileName; } + } + + internal Guid GetModuleVersionIdOrEmpty() + { + return mvid; + } + + public override Guid ModuleVersionId + { + get + { + if (mvid == Guid.Empty && universe.Deterministic) + { + // if a deterministic GUID is used, it can't be queried before the assembly has been written + throw new InvalidOperationException(); + } + return mvid; + } + } + + public void __SetModuleVersionId(Guid guid) + { + if (guid == Guid.Empty && universe.Deterministic) + { + // if you want to use Guid.Empty, don't set UniverseOptions.DeterministicOutput + throw new ArgumentOutOfRangeException(); + } + mvid = guid; + } + + internal uint GetTimeDateStamp() + { + return timestamp; + } + + public DateTime __PEHeaderTimeDateStamp + { + get { return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); } + set + { + if (value < new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) || value > new DateTime(2106, 2, 7, 6, 28, 15, DateTimeKind.Utc)) + { + throw new ArgumentOutOfRangeException(); + } + timestamp = (uint)(value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; + } + } + + public override Type[] __ResolveOptionalParameterTypes(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers) + { + throw new NotImplementedException(); + } + + public override string ScopeName + { + get { return moduleName; } + } + + public ISymbolWriter GetSymWriter() + { + return symbolWriter; + } + + public void DefineUnmanagedResource(string resourceFileName) + { + // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section, + // also setting the Resource Directory entry. + unmanagedResources = new ResourceSection(); + unmanagedResources.ExtractResources(System.IO.File.ReadAllBytes(resourceFileName)); + } + + public bool IsTransient() + { + return false; + } + + public void SetUserEntryPoint(MethodInfo entryPoint) + { + int token = entryPoint.MetadataToken; + if (token < 0) + { + token = -token | 0x06000000; + } + if (symbolWriter != null) + { + symbolWriter.SetUserEntryPoint(new SymbolToken(token)); + } + } + + public StringToken GetStringConstant(string str) + { + return new StringToken(this.UserStrings.Add(str) | (0x70 << 24)); + } + + public SignatureToken GetSignatureToken(SignatureHelper sigHelper) + { + return new SignatureToken(this.StandAloneSig.FindOrAddRecord(this.Blobs.Add(sigHelper.GetSignature(this))) | (StandAloneSigTable.Index << 24)); + } + + public SignatureToken GetSignatureToken(byte[] sigBytes, int sigLength) + { + return new SignatureToken(this.StandAloneSig.FindOrAddRecord(this.Blobs.Add(ByteBuffer.Wrap(sigBytes, sigLength))) | (StandAloneSigTable.Index << 24)); + } + + public MethodInfo GetArrayMethod(Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return new ArrayMethod(this, arrayClass, methodName, callingConvention, returnType, parameterTypes); + } + + public MethodToken GetArrayMethodToken(Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return GetMethodToken(GetArrayMethod(arrayClass, methodName, callingConvention, returnType, parameterTypes)); + } + + internal override Type GetModuleType() + { + return moduleType; + } + + internal override IKVM.Reflection.Reader.ByteReader GetBlob(int blobIndex) + { + return Blobs.GetBlob(blobIndex); + } + + internal int GetSignatureBlobIndex(Signature sig) + { + ByteBuffer bb = new ByteBuffer(16); + sig.WriteSig(this, bb); + return this.Blobs.Add(bb); + } + + // non-standard API + public new long __ImageBase + { + get { return imageBaseAddress; } + set { imageBaseAddress = value; } + } + + protected override long GetImageBaseImpl() + { + return imageBaseAddress; + } + + public new long __StackReserve + { + get { return stackReserve; } + set { stackReserve = value; } + } + + protected override long GetStackReserveImpl() + { + return stackReserve; + } + + [Obsolete("Use __StackReserve property.")] + public void __SetStackReserve(long stackReserve) + { + __StackReserve = stackReserve; + } + + internal ulong GetStackReserve(ulong defaultValue) + { + return stackReserve == -1 ? defaultValue : (ulong)stackReserve; + } + + public new int __FileAlignment + { + get { return fileAlignment; } + set { fileAlignment = value; } + } + + protected override int GetFileAlignmentImpl() + { + return fileAlignment; + } + + public new DllCharacteristics __DllCharacteristics + { + get { return dllCharacteristics; } + set { dllCharacteristics = value; } + } + + protected override DllCharacteristics GetDllCharacteristicsImpl() + { + return dllCharacteristics; + } + + public override int MDStreamVersion + { + get { return asm.mdStreamVersion; } + } + + private int AddTypeRefByName(int resolutionScope, string ns, string name) + { + TypeRefTable.Record rec = new TypeRefTable.Record(); + rec.ResolutionScope = resolutionScope; + SetTypeNameAndTypeNamespace(new TypeName(ns, name), out rec.TypeName, out rec.TypeNamespace); + return 0x01000000 | this.TypeRef.AddRecord(rec); + } + + public void __Save(PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + SaveImpl(null, portableExecutableKind, imageFileMachine); + } + + public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0) + { + throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream"); + } + SaveImpl(stream, portableExecutableKind, imageFileMachine); + } + + private void SaveImpl(Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine) + { + SetIsSaved(); + PopulatePropertyAndEventTables(); + IList attributes = asm.GetCustomAttributesData(null); + if (attributes.Count > 0) + { + int mscorlib = ImportAssemblyRef(universe.CoreLib); + int[] placeholderTokens = new int[4]; + string[] placeholderTypeNames = new string[] { "AssemblyAttributesGoHere", "AssemblyAttributesGoHereM", "AssemblyAttributesGoHereS", "AssemblyAttributesGoHereSM" }; + foreach (CustomAttributeData cad in attributes) + { + int index; + if (cad.Constructor.DeclaringType.BaseType == universe.System_Security_Permissions_CodeAccessSecurityAttribute) + { + if (cad.Constructor.DeclaringType.IsAllowMultipleCustomAttribute) + { + index = 3; + } + else + { + index = 2; + } + } + else if (cad.Constructor.DeclaringType.IsAllowMultipleCustomAttribute) + { + index = 1; + } + else + { + index = 0; + } + if (placeholderTokens[index] == 0) + { + // we manually add a TypeRef without looking it up in mscorlib, because Mono and Silverlight's mscorlib don't have these types + placeholderTokens[index] = AddTypeRefByName(mscorlib, "System.Runtime.CompilerServices", placeholderTypeNames[index]); + } + SetCustomAttribute(placeholderTokens[index], cad.__ToBuilder()); + } + } + FillAssemblyRefTable(); + EmitResources(); + ModuleWriter.WriteModule(null, null, this, PEFileKinds.Dll, portableExecutableKind, imageFileMachine, unmanagedResources, 0, streamOrNull); + CloseResources(); + } + + public void __AddAssemblyReference(AssemblyName assemblyName) + { + __AddAssemblyReference(assemblyName, null); + } + + public void __AddAssemblyReference(AssemblyName assemblyName, Assembly assembly) + { + if (referencedAssemblyNames == null) + { + referencedAssemblyNames = new List(); + } + referencedAssemblyNames.Add((AssemblyName)assemblyName.Clone()); + int token = FindOrAddAssemblyRef(assemblyName, true); + if (assembly != null) + { + referencedAssemblies.Add(assembly, token); + } + } + + public override AssemblyName[] __GetReferencedAssemblies() + { + List list = new List(); + if (referencedAssemblyNames != null) + { + foreach (AssemblyName name in referencedAssemblyNames) + { + if (!list.Contains(name)) + { + list.Add(name); + } + } + } + foreach (Assembly asm in referencedAssemblies.Keys) + { + AssemblyName name = asm.GetName(); + if (!list.Contains(name)) + { + list.Add(name); + } + } + return list.ToArray(); + } + + public void __AddModuleReference(string module) + { + this.ModuleRef.FindOrAddRecord(module == null ? 0 : this.Strings.Add(module)); + } + + public override string[] __GetReferencedModules() + { + string[] arr = new string[this.ModuleRef.RowCount]; + for (int i = 0; i < arr.Length; i++) + { + arr[i] = this.Strings.Find(this.ModuleRef.records[i]); + } + return arr; + } + + public override Type[] __GetReferencedTypes() + { + List list = new List(); + foreach (KeyValuePair kv in typeTokens) + { + if (kv.Value >> 24 == TypeRefTable.Index) + { + list.Add(kv.Key); + } + } + return list.ToArray(); + } + + public override Type[] __GetExportedTypes() + { + throw new NotImplementedException(); + } + + public int __AddModule(int flags, string name, byte[] hash) + { + FileTable.Record file = new FileTable.Record(); + file.Flags = flags; + file.Name = this.Strings.Add(name); + file.HashValue = this.Blobs.Add(ByteBuffer.Wrap(hash)); + return 0x26000000 + this.File.AddRecord(file); + } + + public int __AddManifestResource(int offset, ResourceAttributes flags, string name, int implementation) + { + ManifestResourceTable.Record res = new ManifestResourceTable.Record(); + res.Offset = offset; + res.Flags = (int)flags; + res.Name = this.Strings.Add(name); + res.Implementation = implementation; + return 0x28000000 + this.ManifestResource.AddRecord(res); + } + + public void __SetCustomAttributeFor(int token, CustomAttributeBuilder customBuilder) + { + SetCustomAttribute(token, customBuilder); + } + + public RelativeVirtualAddress __AddVTableFixups(MethodBuilder[] methods, int type) + { + initializedData.Align(8); + VTableFixups fixups; + fixups.initializedDataOffset = (uint)initializedData.Position; + fixups.count = (ushort)methods.Length; + fixups.type = (ushort)type; + foreach (MethodBuilder mb in methods) + { + initializedData.Write(mb.MetadataToken); + if (fixups.SlotWidth == 8) + { + initializedData.Write(0); + } + } + vtablefixups.Add(fixups); + return new RelativeVirtualAddress(fixups.initializedDataOffset); + } + + public void __AddUnmanagedExportStub(string name, int ordinal, RelativeVirtualAddress rva) + { + AddUnmanagedExport(name, ordinal, null, rva); + } + + internal void AddUnmanagedExport(string name, int ordinal, MethodBuilder methodBuilder, RelativeVirtualAddress rva) + { + UnmanagedExport export; + export.name = name; + export.ordinal = ordinal; + export.mb = methodBuilder; + export.rva = rva; + unmanagedExports.Add(export); + } + + internal void SetInterfaceImplementationCustomAttribute(TypeBuilder typeBuilder, Type interfaceType, CustomAttributeBuilder cab) + { + // NOTE since interfaceimpls are extremely common and custom attributes on them are extremely rare, + // we store (and resolve) the custom attributes in such away as to avoid impacting the common case performance + if (interfaceImplCustomAttributes == null) + { + interfaceImplCustomAttributes = new List(); + } + InterfaceImplCustomAttribute rec; + rec.type = typeBuilder.MetadataToken; + int token = GetTypeToken(interfaceType).Token; + switch (token >> 24) + { + case TypeDefTable.Index: + token = (token & 0xFFFFFF) << 2 | 0; + break; + case TypeRefTable.Index: + token = (token & 0xFFFFFF) << 2 | 1; + break; + case TypeSpecTable.Index: + token = (token & 0xFFFFFF) << 2 | 2; + break; + default: + throw new InvalidOperationException(); + } + rec.interfaceType = token; + rec.pseudoToken = AllocPseudoToken(); + interfaceImplCustomAttributes.Add(rec); + SetCustomAttribute(rec.pseudoToken, cab); + } + + internal void ResolveInterfaceImplPseudoTokens() + { + if (interfaceImplCustomAttributes != null) + { + foreach (InterfaceImplCustomAttribute rec in interfaceImplCustomAttributes) + { + for (int i = 0; i < InterfaceImpl.records.Length; i++) + { + if (InterfaceImpl.records[i].Class == rec.type && InterfaceImpl.records[i].Interface == rec.interfaceType) + { + RegisterTokenFixup(rec.pseudoToken, (InterfaceImplTable.Index << 24) | (i + 1)); + break; + } + } + } + } + } + + internal void FixupPseudoToken(ref int token) + { + if (IsPseudoToken(token)) + { + token = ResolvePseudoToken(token); + } + } + + internal void SetIsSaved() + { + if (saved) + { + throw new InvalidOperationException(); + } + saved = true; + } + + internal bool IsSaved + { + get { return saved; } + } + + internal override string GetString(int index) + { + return this.Strings.Find(index); + } + + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Add.cs b/src/IKVM.Reflection/Emit/RelativeVirtualAddress.cs similarity index 65% rename from src/ikvmc/IKVM/Internal/MapXml/Add.cs rename to src/IKVM.Reflection/Emit/RelativeVirtualAddress.cs index 4f31d97af8..83998cc35a 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Add.cs +++ b/src/IKVM.Reflection/Emit/RelativeVirtualAddress.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2008-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,17 +22,20 @@ Jeroen Frijters */ -using System.Xml.Serialization; +namespace IKVM.Reflection.Emit +{ + public struct RelativeVirtualAddress + { + internal readonly uint initializedDataOffset; -using IKVM.Reflection.Emit; + internal RelativeVirtualAddress(uint initializedDataOffset) + { + this.initializedDataOffset = initializedDataOffset; + } -namespace IKVM.Internal.MapXml -{ - [XmlType("add")] - public sealed class Add : Simple - { - public Add() : base(OpCodes.Add) - { - } - } + public static RelativeVirtualAddress operator +(RelativeVirtualAddress rva, int offset) + { + return new RelativeVirtualAddress(rva.initializedDataOffset + (uint)offset); + } + } } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Throws.cs b/src/IKVM.Reflection/Emit/UnmanagedExport.cs similarity index 80% rename from src/ikvmc/IKVM/Internal/MapXml/Throws.cs rename to src/IKVM.Reflection/Emit/UnmanagedExport.cs index 18bf10ac2b..b01e5ce2b4 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Throws.cs +++ b/src/IKVM.Reflection/Emit/UnmanagedExport.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2008-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,13 +22,13 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml +namespace IKVM.Reflection.Emit { - public sealed class Throws - { - [XmlAttribute("class")] - public string Class; - } + struct UnmanagedExport + { + internal string name; + internal int ordinal; + internal RelativeVirtualAddress rva; + internal MethodBuilder mb; + } } diff --git a/src/IKVM.Reflection/Fusion.cs b/src/IKVM.Reflection/Fusion.cs index 69a00b9f4f..10c7582a9e 100644 --- a/src/IKVM.Reflection/Fusion.cs +++ b/src/IKVM.Reflection/Fusion.cs @@ -29,70 +29,69 @@ Jeroen Frijters namespace IKVM.Reflection { - struct ParsedAssemblyName - { - internal string Name; - internal string Version; - internal string Culture; - internal string PublicKeyToken; - internal bool? Retargetable; - internal ProcessorArchitecture ProcessorArchitecture; - internal bool HasPublicKey; - internal bool WindowsRuntime; - } - - enum ParseAssemblyResult - { - OK, - GenericError, - DuplicateKey, - } static class Fusion { + static readonly Version FrameworkVersion = new Version(4, 0, 0, 0); static readonly Version FrameworkVersionNext = new Version(5, 0, 0, 0); static readonly Version SilverlightVersion = new Version(2, 0, 5, 0); static readonly Version SilverlightVersionMinimum = new Version(2, 0, 0, 0); static readonly Version SilverlightVersionMaximum = new Version(5, 9, 0, 0); + const string PublicKeyTokenEcma = "b77a5c561934e089"; const string PublicKeyTokenMicrosoft = "b03f5f7f11d50a3a"; const string PublicKeyTokenSilverlight = "7cec85d7bea7798e"; const string PublicKeyTokenWinFX = "31bf3856ad364e35"; + /// + /// Native implementation of CompareAssemblyIdentity. + /// + /// + /// + /// + /// + /// + /// internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result) - { - bool equivalent; - Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result)); - return equivalent; - } - - [DllImport("fusion", CharSet = CharSet.Unicode)] - private static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult); + { + Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out var equivalent, out result)); + return equivalent; + } - // internal for use by mcs + [DllImport("fusion", CharSet = CharSet.Unicode)] + static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult); + + /// + /// Managed implementation of CompareAssemblyIdentity. + /// + /// + /// + /// + /// + /// + /// + /// + /// internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result) { ParsedAssemblyName name1; ParsedAssemblyName name2; - ParseAssemblyResult r1 = ParseAssemblyName(assemblyIdentity1, out name1); - ParseAssemblyResult r2 = ParseAssemblyName(assemblyIdentity2, out name2); + var r1 = ParseAssemblyName(assemblyIdentity1, out name1); + var r2 = ParseAssemblyName(assemblyIdentity2, out name2); Version version1; if (unified1) { - if (name1.Name == null || !ParseVersion(name1.Version, out version1) || version1 == null || version1.Revision == -1 - || name1.Culture == null || name1.PublicKeyToken == null || name1.PublicKeyToken.Length < 2) + if (name1.Name == null || !ParseVersion(name1.Version, out version1) || version1 == null || version1.Revision == -1 || name1.Culture == null || name1.PublicKeyToken == null || name1.PublicKeyToken.Length < 2) { result = AssemblyComparisonResult.NonEquivalent; throw new ArgumentException(); } } - Version version2 = null; - if (!ParseVersion(name2.Version, out version2) || version2 == null || version2.Revision == -1 - || name2.Culture == null || name2.PublicKeyToken == null || name2.PublicKeyToken.Length < 2) + if (!ParseVersion(name2.Version, out var version2) || version2 == null || version2.Revision == -1 || name2.Culture == null || name2.PublicKeyToken == null || name2.PublicKeyToken.Length < 2) { result = AssemblyComparisonResult.NonEquivalent; throw new ArgumentException(); @@ -115,27 +114,21 @@ internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool if (r1 != ParseAssemblyResult.OK) { result = AssemblyComparisonResult.NonEquivalent; - switch (r1) + throw r1 switch { - case ParseAssemblyResult.DuplicateKey: - throw new System.IO.FileLoadException(); - case ParseAssemblyResult.GenericError: - default: - throw new ArgumentException(); - } + ParseAssemblyResult.DuplicateKey => new System.IO.FileLoadException(), + _ => new ArgumentException(), + }; } if (r2 != ParseAssemblyResult.OK) { result = AssemblyComparisonResult.NonEquivalent; - switch (r2) + throw r2 switch { - case ParseAssemblyResult.DuplicateKey: - throw new System.IO.FileLoadException(); - case ParseAssemblyResult.GenericError: - default: - throw new ArgumentException(); - } + ParseAssemblyResult.DuplicateKey => new System.IO.FileLoadException(), + _ => new ArgumentException(), + }; } if (!ParseVersion(name1.Version, out version1)) @@ -339,7 +332,6 @@ static bool IsFrameworkAssembly(ParsedAssemblyName name) case "System.Xml.Linq": case "System.Xml.Serialization": return name.PublicKeyToken == PublicKeyTokenEcma; - case "Microsoft.CSharp": case "Microsoft.VisualBasic": case "System.Collections": @@ -409,7 +401,6 @@ static bool IsFrameworkAssembly(ParsedAssemblyName name) case "System.Xml.XDocument": case "System.Xml.XmlSerializer": return name.PublicKeyToken == PublicKeyTokenMicrosoft; - case "System.ComponentModel.DataAnnotations": case "System.ServiceModel.Web": case "System.Web.Abstractions": @@ -426,13 +417,11 @@ static bool IsFrameworkAssembly(ParsedAssemblyName name) static string GetRemappedPublicKeyToken(ref ParsedAssemblyName name, Version version) { if (name.Retargetable.GetValueOrDefault() && version < SilverlightVersion) - { return null; - } + if (name.PublicKeyToken == "ddd0da4d3e678217" && name.Name == "System.ComponentModel.DataAnnotations" && name.Retargetable.GetValueOrDefault()) - { return PublicKeyTokenWinFX; - } + if (SilverlightVersionMinimum <= version && version <= SilverlightVersionMaximum) { switch (name.PublicKeyToken) @@ -481,6 +470,7 @@ static string GetRemappedPublicKeyToken(ref ParsedAssemblyName name, Version ver break; } } + return null; } @@ -488,43 +478,37 @@ internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out { pos = 0; if (!TryParse(fullName, ref pos, out simpleName) || simpleName.Length == 0) - { return ParseAssemblyResult.GenericError; - } + if (pos == fullName.Length && fullName[fullName.Length - 1] == ',') - { return ParseAssemblyResult.GenericError; - } + return ParseAssemblyResult.OK; } - private static bool TryParse(string fullName, ref int pos, out string value) + static bool TryParse(string fullName, ref int pos, out string value) { value = null; - StringBuilder sb = new StringBuilder(); + + var sb = new StringBuilder(); while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos])) - { pos++; - } + int quote = -1; if (pos < fullName.Length && (fullName[pos] == '"' || fullName[pos] == '\'')) - { quote = fullName[pos++]; - } + for (; pos < fullName.Length; pos++) { - char ch = fullName[pos]; + var ch = fullName[pos]; if (ch == '\\') { if (++pos == fullName.Length) - { return false; - } + ch = fullName[pos]; if (ch == '\\') - { return false; - } } else if (ch == quote) { @@ -532,13 +516,9 @@ private static bool TryParse(string fullName, ref int pos, out string value) { ch = fullName[pos]; if (ch == ',' || ch == '=') - { break; - } if (!char.IsWhiteSpace(ch)) - { return false; - } } break; } @@ -550,8 +530,10 @@ private static bool TryParse(string fullName, ref int pos, out string value) { break; } + sb.Append(ch); } + value = sb.ToString().Trim(); return value.Length != 0 || quote != -1; } @@ -563,6 +545,7 @@ private static bool TryConsume(string fullName, char ch, ref int pos) pos++; return true; } + return false; } @@ -577,158 +560,154 @@ private static bool TryParseAssemblyAttribute(string fullName, ref int pos, ref internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName) { parsedName = new ParsedAssemblyName(); - int pos; - ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name); + var res = ParseAssemblySimpleName(fullName, out var pos, out parsedName.Name); if (res != ParseAssemblyResult.OK) - { return res; - } - else - { - const int ERROR_SXS_IDENTITIES_DIFFERENT = unchecked((int)0x80073716); - System.Collections.Generic.Dictionary unknownAttributes = null; - bool hasProcessorArchitecture = false; - bool hasContentType = false; - bool hasPublicKeyToken = false; - string publicKeyToken; - while (pos != fullName.Length) + + const int ERROR_SXS_IDENTITIES_DIFFERENT = unchecked((int)0x80073716); + System.Collections.Generic.Dictionary unknownAttributes = null; + bool hasProcessorArchitecture = false; + bool hasContentType = false; + bool hasPublicKeyToken = false; + string publicKeyToken; + while (pos != fullName.Length) + { + string key = null; + string value = null; + if (!TryParseAssemblyAttribute(fullName, ref pos, ref key, ref value)) { - string key = null; - string value = null; - if (!TryParseAssemblyAttribute(fullName, ref pos, ref key, ref value)) - { - return ParseAssemblyResult.GenericError; - } - key = key.ToLowerInvariant(); - switch (key) - { - case "version": - if (parsedName.Version != null) - { - return ParseAssemblyResult.DuplicateKey; - } - parsedName.Version = value; - break; - case "culture": - if (parsedName.Culture != null) - { - return ParseAssemblyResult.DuplicateKey; - } - if (!ParseCulture(value, out parsedName.Culture)) - { - return ParseAssemblyResult.GenericError; - } - break; - case "publickeytoken": - if (hasPublicKeyToken) - { - return ParseAssemblyResult.DuplicateKey; - } - if (!ParsePublicKeyToken(value, out publicKeyToken)) - { - return ParseAssemblyResult.GenericError; - } - if (parsedName.HasPublicKey && parsedName.PublicKeyToken != publicKeyToken) - { - Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT); - } - parsedName.PublicKeyToken = publicKeyToken; - hasPublicKeyToken = true; - break; - case "publickey": - if (parsedName.HasPublicKey) - { - return ParseAssemblyResult.DuplicateKey; - } - if (!ParsePublicKey(value, out publicKeyToken)) - { - return ParseAssemblyResult.GenericError; - } - if (hasPublicKeyToken && parsedName.PublicKeyToken != publicKeyToken) - { - Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT); - } - parsedName.PublicKeyToken = publicKeyToken; - parsedName.HasPublicKey = true; - break; - case "retargetable": - if (parsedName.Retargetable.HasValue) - { - return ParseAssemblyResult.DuplicateKey; - } - switch (value.ToLowerInvariant()) - { - case "yes": - parsedName.Retargetable = true; - break; - case "no": - parsedName.Retargetable = false; - break; - default: - return ParseAssemblyResult.GenericError; - } - break; - case "processorarchitecture": - if (hasProcessorArchitecture) - { - return ParseAssemblyResult.DuplicateKey; - } - hasProcessorArchitecture = true; - switch (value.ToLowerInvariant()) - { - case "none": - parsedName.ProcessorArchitecture = ProcessorArchitecture.None; - break; - case "msil": - parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL; - break; - case "x86": - parsedName.ProcessorArchitecture = ProcessorArchitecture.X86; - break; - case "ia64": - parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64; - break; - case "amd64": - parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64; - break; - case "arm": - parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm; - break; - default: - return ParseAssemblyResult.GenericError; - } - break; - case "contenttype": - if (hasContentType) - { - return ParseAssemblyResult.DuplicateKey; - } - hasContentType = true; - if (!value.Equals("windowsruntime", StringComparison.OrdinalIgnoreCase)) - { + return ParseAssemblyResult.GenericError; + } + key = key.ToLowerInvariant(); + switch (key) + { + case "version": + if (parsedName.Version != null) + { + return ParseAssemblyResult.DuplicateKey; + } + parsedName.Version = value; + break; + case "culture": + if (parsedName.Culture != null) + { + return ParseAssemblyResult.DuplicateKey; + } + if (!ParseCulture(value, out parsedName.Culture)) + { + return ParseAssemblyResult.GenericError; + } + break; + case "publickeytoken": + if (hasPublicKeyToken) + { + return ParseAssemblyResult.DuplicateKey; + } + if (!ParsePublicKeyToken(value, out publicKeyToken)) + { + return ParseAssemblyResult.GenericError; + } + if (parsedName.HasPublicKey && parsedName.PublicKeyToken != publicKeyToken) + { + Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT); + } + parsedName.PublicKeyToken = publicKeyToken; + hasPublicKeyToken = true; + break; + case "publickey": + if (parsedName.HasPublicKey) + { + return ParseAssemblyResult.DuplicateKey; + } + if (!ParsePublicKey(value, out publicKeyToken)) + { + return ParseAssemblyResult.GenericError; + } + if (hasPublicKeyToken && parsedName.PublicKeyToken != publicKeyToken) + { + Marshal.ThrowExceptionForHR(ERROR_SXS_IDENTITIES_DIFFERENT); + } + parsedName.PublicKeyToken = publicKeyToken; + parsedName.HasPublicKey = true; + break; + case "retargetable": + if (parsedName.Retargetable.HasValue) + { + return ParseAssemblyResult.DuplicateKey; + } + switch (value.ToLowerInvariant()) + { + case "yes": + parsedName.Retargetable = true; + break; + case "no": + parsedName.Retargetable = false; + break; + default: return ParseAssemblyResult.GenericError; - } - parsedName.WindowsRuntime = true; - break; - default: - if (key.Length == 0) - { + } + break; + case "processorarchitecture": + if (hasProcessorArchitecture) + { + return ParseAssemblyResult.DuplicateKey; + } + hasProcessorArchitecture = true; + switch (value.ToLowerInvariant()) + { + case "none": + parsedName.ProcessorArchitecture = ProcessorArchitecture.None; + break; + case "msil": + parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL; + break; + case "x86": + parsedName.ProcessorArchitecture = ProcessorArchitecture.X86; + break; + case "ia64": + parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64; + break; + case "amd64": + parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64; + break; + case "arm": + parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm; + break; + default: return ParseAssemblyResult.GenericError; - } - if (unknownAttributes == null) - { - unknownAttributes = new System.Collections.Generic.Dictionary(); - } - if (unknownAttributes.ContainsKey(key)) - { - return ParseAssemblyResult.DuplicateKey; - } - unknownAttributes.Add(key, null); - break; - } + } + break; + case "contenttype": + if (hasContentType) + { + return ParseAssemblyResult.DuplicateKey; + } + hasContentType = true; + if (!value.Equals("windowsruntime", StringComparison.OrdinalIgnoreCase)) + { + return ParseAssemblyResult.GenericError; + } + parsedName.WindowsRuntime = true; + break; + default: + if (key.Length == 0) + { + return ParseAssemblyResult.GenericError; + } + if (unknownAttributes == null) + { + unknownAttributes = new System.Collections.Generic.Dictionary(); + } + if (unknownAttributes.ContainsKey(key)) + { + return ParseAssemblyResult.DuplicateKey; + } + unknownAttributes.Add(key, null); + break; } - return ParseAssemblyResult.OK; } + + return ParseAssemblyResult.OK; } private static bool ParseVersion(string str, out Version version) @@ -738,20 +717,23 @@ private static bool ParseVersion(string str, out Version version) version = null; return true; } - string[] parts = str.Split('.'); + + var parts = str.Split('.'); if (parts.Length < 2 || parts.Length > 4) { version = null; - ushort dummy; + // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name - return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out dummy); + return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out _); } + if (parts[0] == "" || parts[1] == "") { // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name version = null; return true; } + ushort major, minor, build = 65535, revision = 65535; if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major) && ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor) @@ -759,41 +741,39 @@ private static bool ParseVersion(string str, out Version version) && (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision)))) { if (parts.Length == 4 && parts[3] != "" && parts[2] != "") - { version = new Version(major, minor, build, revision); - } else if (parts.Length == 3 && parts[2] != "") - { version = new Version(major, minor, build); - } else - { version = new Version(major, minor); - } + return true; } + version = null; return false; } - private static bool ParseCulture(string str, out string culture) + static bool ParseCulture(string str, out string culture) { if (str == null) { culture = null; return false; } + culture = str; return true; } - private static bool ParsePublicKeyToken(string str, out string publicKeyToken) + static bool ParsePublicKeyToken(string str, out string publicKeyToken) { if (str == null) { publicKeyToken = null; return false; } + publicKeyToken = str.ToLowerInvariant(); return true; } @@ -805,6 +785,7 @@ private static bool ParsePublicKey(string str, out string publicKeyToken) publicKeyToken = null; return false; } + publicKeyToken = AssemblyName.ComputePublicKeyToken(str); return true; } @@ -818,5 +799,7 @@ private static bool IsStrongNamed(ParsedAssemblyName name) { return name.PublicKeyToken != null && name.PublicKeyToken != "null"; } + } + } diff --git a/src/IKVM.Reflection/IKVM.Reflection.csproj b/src/IKVM.Reflection/IKVM.Reflection.csproj index cc0bed1239..27779754a3 100644 --- a/src/IKVM.Reflection/IKVM.Reflection.csproj +++ b/src/IKVM.Reflection/IKVM.Reflection.csproj @@ -4,6 +4,7 @@ net461;netcoreapp3.1 IKVM implementation of System.Reflection[.Emit] $(DefineConstants);EMITTERS + true diff --git a/src/ikvmc/IKVM/Internal/MapXml/Element.cs b/src/IKVM.Reflection/ParseAssemblyResult.cs similarity index 83% rename from src/ikvmc/IKVM/Internal/MapXml/Element.cs rename to src/IKVM.Reflection/ParseAssemblyResult.cs index 447babac7a..58ffb05904 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Element.cs +++ b/src/IKVM.Reflection/ParseAssemblyResult.cs @@ -1,5 +1,6 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2010-2013 Jeroen Frijters + Copyright (C) 2011 Marek Safar This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,13 +23,12 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml +namespace IKVM.Reflection { - public sealed class Element + enum ParseAssemblyResult { - [XmlText] - public string Value; + OK, + GenericError, + DuplicateKey, } } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Assembly.cs b/src/IKVM.Reflection/ParsedAssemblyName.cs similarity index 67% rename from src/ikvmc/IKVM/Internal/MapXml/Assembly.cs rename to src/IKVM.Reflection/ParsedAssemblyName.cs index b838aa99b4..488e2c914f 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Assembly.cs +++ b/src/IKVM.Reflection/ParsedAssemblyName.cs @@ -1,5 +1,6 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2010-2013 Jeroen Frijters + Copyright (C) 2011 Marek Safar This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,15 +23,17 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml +namespace IKVM.Reflection { - public sealed class Assembly + struct ParsedAssemblyName { - [XmlElement("class")] - public Class[] Classes; - [XmlElement("attribute")] - public Attribute[] Attributes; + internal string Name; + internal string Version; + internal string Culture; + internal string PublicKeyToken; + internal bool? Retargetable; + internal ProcessorArchitecture ProcessorArchitecture; + internal bool HasPublicKey; + internal bool WindowsRuntime; } } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Mul.cs b/src/IKVM.Reflection/ResolveEventArgs.cs similarity index 60% rename from src/ikvmc/IKVM/Internal/MapXml/Mul.cs rename to src/IKVM.Reflection/ResolveEventArgs.cs index 51125710ad..b61af6450b 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Mul.cs +++ b/src/IKVM.Reflection/ResolveEventArgs.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2009-2013 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,18 +21,35 @@ Jeroen Frijters jeroen@frijters.net */ +using System; -using System.Xml.Serialization; - -using IKVM.Reflection.Emit; - -namespace IKVM.Internal.MapXml +namespace IKVM.Reflection { - [XmlType("mul")] - public sealed class Mul : Simple + public sealed class ResolveEventArgs : EventArgs { - public Mul() : base(OpCodes.Mul) + + private readonly string name; + private readonly Assembly requestingAssembly; + + public ResolveEventArgs(string name) + : this(name, null) + { + } + + public ResolveEventArgs(string name, Assembly requestingAssembly) + { + this.name = name; + this.requestingAssembly = requestingAssembly; + } + + public string Name + { + get { return name; } + } + + public Assembly RequestingAssembly { + get { return requestingAssembly; } } } } diff --git a/src/IKVM.Reflection/StrongNameKeyPair.cs b/src/IKVM.Reflection/StrongNameKeyPair.cs index 4b6a2fac7c..d08cfd203c 100644 --- a/src/IKVM.Reflection/StrongNameKeyPair.cs +++ b/src/IKVM.Reflection/StrongNameKeyPair.cs @@ -28,222 +28,211 @@ Jeroen Frijters namespace IKVM.Reflection { public sealed class StrongNameKeyPair - { - private readonly byte[] keyPairArray; - private readonly string keyPairContainer; - - public StrongNameKeyPair(string keyPairContainer) - { - if (keyPairContainer == null) - { - throw new ArgumentNullException("keyPairContainer"); - } - - if (Universe.MonoRuntime && Environment.OSVersion.Platform == PlatformID.Win32NT) - { - throw new NotSupportedException("IKVM.Reflection does not support key containers when running on Mono"); - } - - this.keyPairContainer = keyPairContainer; - } - - public StrongNameKeyPair(byte[] keyPairArray) - { - if (keyPairArray == null) - { - throw new ArgumentNullException("keyPairArray"); - } - this.keyPairArray = (byte[])keyPairArray.Clone(); - } - - public StrongNameKeyPair(FileStream keyPairFile) - : this(ReadAllBytes(keyPairFile)) - { - } - - private static byte[] ReadAllBytes(FileStream keyPairFile) - { - if (keyPairFile == null) - { - throw new ArgumentNullException("keyPairFile"); - } - byte[] buf = new byte[keyPairFile.Length - keyPairFile.Position]; - keyPairFile.Read(buf, 0, buf.Length); - return buf; - } - - public byte[] PublicKey - { - get - { - if (Universe.MonoRuntime) - { - // MONOBUG workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=5299 - return MonoGetPublicKey(); - } - - using (RSACryptoServiceProvider rsa = CreateRSA()) - { - var rsaParameters = rsa.ExportParameters(false); - byte[] cspBlob = ExportPublicKey(rsaParameters); - byte[] publicKey = new byte[12 + cspBlob.Length]; - Buffer.BlockCopy(cspBlob, 0, publicKey, 12, cspBlob.Length); - publicKey[1] = 36; - publicKey[4] = 4; - publicKey[5] = 128; - publicKey[8] = (byte)(cspBlob.Length >> 0); - publicKey[9] = (byte)(cspBlob.Length >> 8); - publicKey[10] = (byte)(cspBlob.Length >> 16); - publicKey[11] = (byte)(cspBlob.Length >> 24); - return publicKey; - } - } - } - - internal RSACryptoServiceProvider CreateRSA() - { - try - { - if (keyPairArray != null) - { - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); - // we import from parameters, as using ImportCspBlob - // causes the exception "KeySet not found" when signing a hash later. - rsa.ImportParameters(RSAParametersFromByteArray(keyPairArray)); - return rsa; - } - else - { - CspParameters parm = new CspParameters(); - parm.KeyContainerName = keyPairContainer; - // MONOBUG Mono doesn't like it when Flags or KeyNumber are set - if (!Universe.MonoRuntime) - { - parm.Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseExistingKey; - parm.KeyNumber = 2; // Signature - } - return new RSACryptoServiceProvider(parm); - } - } - catch - { - throw new ArgumentException("Unable to obtain public key for StrongNameKeyPair."); - } - } - - // helper functions ExportPublicKey, RSAParametersFromStream and RSAParametersFromByteArray - // based on code in the following article:- - // https://www.developerfusion.com/article/84422/the-key-to-strong-names/ - static byte[] ExportPublicKey(RSAParameters rsaParameters) - { - if (rsaParameters.Modulus == null || rsaParameters.Exponent == null) - { - throw new ArgumentNullException(nameof(rsaParameters)); - } - - using (MemoryStream ms = new MemoryStream()) - { - using (BinaryWriter bw = new BinaryWriter(ms)) - { - var keyBitLength = rsaParameters.Modulus.Length * 8; - bw.Write((byte)0x06); - bw.Write((byte)0x02); - bw.Write((UInt16)0x0000); - bw.Write((UInt32)0x2400); - bw.Write("RSA1".ToCharArray()); - bw.Write((UInt32)keyBitLength); - bw.Write(rsaParameters.Exponent); - bw.Write((byte)0x00); - byte[] modulus = (byte[])rsaParameters.Modulus.Clone(); - Array.Reverse(modulus); - bw.Write(modulus); - - return ms.ToArray(); - } - } - } - - static RSAParameters RSAParametersFromByteArray(byte[] array) - { - using (MemoryStream ms = new MemoryStream(array)) - { - return RSAParametersFromStream(ms); - } - } - - static RSAParameters RSAParametersFromStream(Stream str) - { - RSAParameters rsaParameters = new RSAParameters(); - - using (var br = new BinaryReader(str)) - { - // Read BLOBHEADER - byte keyType = br.ReadByte(); - if (keyType != 6 && keyType != 7) - { - throw new CryptographicException("SNK file not in correct format"); - } - byte blobVersion = br.ReadByte(); - UInt16 reserverd = br.ReadUInt16(); - UInt32 algorithmID = br.ReadUInt32(); - // Read RSAPUBKEY - string magic = new string(br.ReadChars(4)); - if (!magic.Equals("RSA1") && !magic.Equals("RSA2")) - { - throw new CryptographicException("SNK file not in correct format"); - } - UInt32 keyBitLength = br.ReadUInt32(); - byte[] publicExponent = br.ReadBytes(3); - br.ReadByte(); - rsaParameters.Exponent = publicExponent; - - // Read Modulus - byte[] modulus = br.ReadBytes( - (int)keyBitLength / 8); - Array.Reverse(modulus); - rsaParameters.Modulus = modulus; - - if (keyType == 7) - { - // Read Private Key Parameters - byte[] prime1 = br.ReadBytes( - (int)keyBitLength / 16); - byte[] prime2 = br.ReadBytes( - (int)keyBitLength / 16); - byte[] exponent1 = br.ReadBytes( - (int)keyBitLength / 16); - byte[] exponent2 = br.ReadBytes( - (int)keyBitLength / 16); - byte[] coefficient = br.ReadBytes( - (int)keyBitLength / 16); - byte[] privateExponent = br.ReadBytes( - (int)keyBitLength / 8); - - Array.Reverse(prime1); - Array.Reverse(prime2); - Array.Reverse(exponent1); - Array.Reverse(exponent2); - Array.Reverse(coefficient); - Array.Reverse(privateExponent); - rsaParameters.P = prime1; - rsaParameters.Q = prime2; - rsaParameters.DP = exponent1; - rsaParameters.DQ = exponent2; - rsaParameters.InverseQ = coefficient; - rsaParameters.D = privateExponent; - } - } - - return rsaParameters; - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private byte[] MonoGetPublicKey() - { - return keyPairArray != null - ? new System.Reflection.StrongNameKeyPair(keyPairArray).PublicKey - : new System.Reflection.StrongNameKeyPair(keyPairContainer).PublicKey; - } - - } + { + private readonly byte[] keyPairArray; + private readonly string keyPairContainer; + + public StrongNameKeyPair(string keyPairContainer) + { + if (keyPairContainer == null) + { + throw new ArgumentNullException("keyPairContainer"); + } + + if (Universe.MonoRuntime && Environment.OSVersion.Platform == PlatformID.Win32NT) + { + throw new NotSupportedException("IKVM.Reflection does not support key containers when running on Mono"); + } + + this.keyPairContainer = keyPairContainer; + } + + public StrongNameKeyPair(byte[] keyPairArray) + { + if (keyPairArray == null) + { + throw new ArgumentNullException("keyPairArray"); + } + this.keyPairArray = (byte[])keyPairArray.Clone(); + } + + public StrongNameKeyPair(FileStream keyPairFile) + : this(ReadAllBytes(keyPairFile)) + { + } + + private static byte[] ReadAllBytes(FileStream keyPairFile) + { + if (keyPairFile == null) + { + throw new ArgumentNullException("keyPairFile"); + } + byte[] buf = new byte[keyPairFile.Length - keyPairFile.Position]; + keyPairFile.Read(buf, 0, buf.Length); + return buf; + } + + public byte[] PublicKey + { + get + { + if (Universe.MonoRuntime) + { + // MONOBUG workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=5299 + return MonoGetPublicKey(); + } + + using (RSACryptoServiceProvider rsa = CreateRSA()) + { + var rsaParameters = rsa.ExportParameters(false); + byte[] cspBlob = ExportPublicKey(rsaParameters); + byte[] publicKey = new byte[12 + cspBlob.Length]; + Buffer.BlockCopy(cspBlob, 0, publicKey, 12, cspBlob.Length); + publicKey[1] = 36; + publicKey[4] = 4; + publicKey[5] = 128; + publicKey[8] = (byte)(cspBlob.Length >> 0); + publicKey[9] = (byte)(cspBlob.Length >> 8); + publicKey[10] = (byte)(cspBlob.Length >> 16); + publicKey[11] = (byte)(cspBlob.Length >> 24); + return publicKey; + } + } + } + + internal RSACryptoServiceProvider CreateRSA() + { + try + { + if (keyPairArray != null) + { + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); + // we import from parameters, as using ImportCspBlob + // causes the exception "KeySet not found" when signing a hash later. + rsa.ImportParameters(RSAParametersFromByteArray(keyPairArray)); + return rsa; + } + else + { + CspParameters parm = new CspParameters(); + parm.KeyContainerName = keyPairContainer; + // MONOBUG Mono doesn't like it when Flags or KeyNumber are set + if (!Universe.MonoRuntime) + { + parm.Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseExistingKey; + parm.KeyNumber = 2; // Signature + } + return new RSACryptoServiceProvider(parm); + } + } + catch + { + throw new ArgumentException("Unable to obtain public key for StrongNameKeyPair."); + } + } + + // helper functions ExportPublicKey, RSAParametersFromStream and RSAParametersFromByteArray + // based on code in the following article:- + // https://www.developerfusion.com/article/84422/the-key-to-strong-names/ + static byte[] ExportPublicKey(RSAParameters rsaParameters) + { + if (rsaParameters.Modulus == null || rsaParameters.Exponent == null) + { + throw new ArgumentNullException(nameof(rsaParameters)); + } + + using (MemoryStream ms = new MemoryStream()) + { + using (BinaryWriter bw = new BinaryWriter(ms)) + { + var keyBitLength = rsaParameters.Modulus.Length * 8; + bw.Write((byte)0x06); + bw.Write((byte)0x02); + bw.Write((UInt16)0x0000); + bw.Write((UInt32)0x2400); + bw.Write("RSA1".ToCharArray()); + bw.Write((UInt32)keyBitLength); + bw.Write(rsaParameters.Exponent); + bw.Write((byte)0x00); + byte[] modulus = (byte[])rsaParameters.Modulus.Clone(); + Array.Reverse(modulus); + bw.Write(modulus); + + return ms.ToArray(); + } + } + } + + static RSAParameters RSAParametersFromByteArray(byte[] array) + { + using (MemoryStream ms = new MemoryStream(array)) + { + return RSAParametersFromStream(ms); + } + } + + static RSAParameters RSAParametersFromStream(Stream str) + { + var rsaParameters = new RSAParameters(); + + using (var br = new BinaryReader(str)) + { + // Read BLOBHEADER + var keyType = br.ReadByte(); + if (keyType != 6 && keyType != 7) + throw new CryptographicException("SNK file not in correct format"); + + var blobVersion = br.ReadByte(); + var reserverd = br.ReadUInt16(); + var algorithmID = br.ReadUInt32(); + // Read RSAPUBKEY + var magic = new string(br.ReadChars(4)); + if (!magic.Equals("RSA1") && !magic.Equals("RSA2")) + throw new CryptographicException("SNK file not in correct format"); + var keyBitLength = br.ReadUInt32(); + var publicExponent = br.ReadBytes(3); + br.ReadByte(); + rsaParameters.Exponent = publicExponent; + + // Read Modulus + byte[] modulus = br.ReadBytes((int)keyBitLength / 8); + Array.Reverse(modulus); + rsaParameters.Modulus = modulus; + + if (keyType == 7) + { + // Read Private Key Parameters + byte[] prime1 = br.ReadBytes((int)keyBitLength / 16); + byte[] prime2 = br.ReadBytes((int)keyBitLength / 16); + byte[] exponent1 = br.ReadBytes((int)keyBitLength / 16); + byte[] exponent2 = br.ReadBytes((int)keyBitLength / 16); + byte[] coefficient = br.ReadBytes((int)keyBitLength / 16); + byte[] privateExponent = br.ReadBytes((int)keyBitLength / 8); + + Array.Reverse(prime1); + Array.Reverse(prime2); + Array.Reverse(exponent1); + Array.Reverse(exponent2); + Array.Reverse(coefficient); + Array.Reverse(privateExponent); + rsaParameters.P = prime1; + rsaParameters.Q = prime2; + rsaParameters.DP = exponent1; + rsaParameters.DQ = exponent2; + rsaParameters.InverseQ = coefficient; + rsaParameters.D = privateExponent; + } + } + + return rsaParameters; + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + byte[] MonoGetPublicKey() + { + return keyPairArray != null ? new System.Reflection.StrongNameKeyPair(keyPairArray).PublicKey : new System.Reflection.StrongNameKeyPair(keyPairContainer).PublicKey; + } + + } + } diff --git a/src/IKVM.Reflection/TypeName.cs b/src/IKVM.Reflection/TypeName.cs new file mode 100644 index 0000000000..5a53e5af5d --- /dev/null +++ b/src/IKVM.Reflection/TypeName.cs @@ -0,0 +1,121 @@ +/* + Copyright (C) 2009-2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +namespace IKVM.Reflection +{ + // this respresents a type name as in metadata: + // - ns will be null for empty the namespace (never the empty string) + // - the strings are not escaped + struct TypeName : IEquatable + { + private readonly string ns; + private readonly string name; + + internal TypeName(string ns, string name) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + this.ns = ns; + this.name = name; + } + + internal string Name + { + get { return name; } + } + + internal string Namespace + { + get { return ns; } + } + + public static bool operator ==(TypeName o1, TypeName o2) + { + return o1.ns == o2.ns && o1.name == o2.name; + } + + public static bool operator !=(TypeName o1, TypeName o2) + { + return o1.ns != o2.ns || o1.name != o2.name; + } + + public override int GetHashCode() + { + return ns == null ? name.GetHashCode() : ns.GetHashCode() * 37 + name.GetHashCode(); + } + + public override bool Equals(object obj) + { + TypeName? other = obj as TypeName?; + return other != null && other.Value == this; + } + + public override string ToString() + { + return ns == null ? name : ns + "." + name; + } + + bool IEquatable.Equals(TypeName other) + { + return this == other; + } + + internal bool Matches(string fullName) + { + if (ns == null) + { + return name == fullName; + } + if (ns.Length + 1 + name.Length == fullName.Length) + { + return fullName.StartsWith(ns, StringComparison.Ordinal) + && fullName[ns.Length] == '.' + && fullName.EndsWith(name, StringComparison.Ordinal); + } + return false; + } + + internal TypeName ToLowerInvariant() + { + return new TypeName(ns == null ? null : ns.ToLowerInvariant(), name.ToLowerInvariant()); + } + + internal static TypeName Split(string name) + { + int dot = name.LastIndexOf('.'); + if (dot == -1) + { + return new TypeName(null, name); + } + else + { + return new TypeName(name.Substring(0, dot), name.Substring(dot + 1)); + } + } + } + +} diff --git a/src/IKVM.Reflection/TypeNameParser.cs b/src/IKVM.Reflection/TypeNameParser.cs index 33dd92bb48..3c4f5145fd 100644 --- a/src/IKVM.Reflection/TypeNameParser.cs +++ b/src/IKVM.Reflection/TypeNameParser.cs @@ -22,604 +22,498 @@ Jeroen Frijters */ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace IKVM.Reflection { - // this respresents a type name as in metadata: - // - ns will be null for empty the namespace (never the empty string) - // - the strings are not escaped - struct TypeName : IEquatable - { - private readonly string ns; - private readonly string name; - - internal TypeName(string ns, string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - this.ns = ns; - this.name = name; - } - - internal string Name - { - get { return name; } - } - - internal string Namespace - { - get { return ns; } - } - - public static bool operator ==(TypeName o1, TypeName o2) - { - return o1.ns == o2.ns && o1.name == o2.name; - } - - public static bool operator !=(TypeName o1, TypeName o2) - { - return o1.ns != o2.ns || o1.name != o2.name; - } - - public override int GetHashCode() - { - return ns == null ? name.GetHashCode() : ns.GetHashCode() * 37 + name.GetHashCode(); - } - - public override bool Equals(object obj) - { - TypeName? other = obj as TypeName?; - return other != null && other.Value == this; - } - - public override string ToString() - { - return ns == null ? name : ns + "." + name; - } - - bool IEquatable.Equals(TypeName other) - { - return this == other; - } - - internal bool Matches(string fullName) - { - if (ns == null) - { - return name == fullName; - } - if (ns.Length + 1 + name.Length == fullName.Length) - { - return fullName.StartsWith(ns, StringComparison.Ordinal) - && fullName[ns.Length] == '.' - && fullName.EndsWith(name, StringComparison.Ordinal); - } - return false; - } - - internal TypeName ToLowerInvariant() - { - return new TypeName(ns == null ? null : ns.ToLowerInvariant(), name.ToLowerInvariant()); - } - - internal static TypeName Split(string name) - { - int dot = name.LastIndexOf('.'); - if (dot == -1) - { - return new TypeName(null, name); - } - else - { - return new TypeName(name.Substring(0, dot), name.Substring(dot + 1)); - } - } - } - - struct TypeNameParser - { - private const string SpecialChars = "\\+,[]*&"; - private const short SZARRAY = -1; - private const short BYREF = -2; - private const short POINTER = -3; - private readonly string name; - private readonly string[] nested; - private readonly string assemblyName; - private readonly short[] modifiers; - private readonly TypeNameParser[] genericParameters; - - internal static string Escape(string name) - { - if (name == null) - { - return null; - } - StringBuilder sb = null; - for (int pos = 0; pos < name.Length; pos++) - { - char c = name[pos]; - switch (c) - { - case '\\': - case '+': - case ',': - case '[': - case ']': - case '*': - case '&': - if (sb == null) - { - sb = new StringBuilder(name, 0, pos, name.Length + 3); - } - sb.Append("\\").Append(c); - break; - default: - if (sb != null) - { - sb.Append(c); - } - break; - } - } - return sb != null ? sb.ToString() : name; - } - - internal static string Unescape(string name) - { - int pos = name.IndexOf('\\'); - if (pos == -1) - { - return name; - } - StringBuilder sb = new StringBuilder(name, 0, pos, name.Length - 1); - for (; pos < name.Length; pos++) - { - char c = name[pos]; - if (c == '\\') - { - c = name[++pos]; - } - sb.Append(c); - } - return sb.ToString(); - } - - internal static TypeNameParser Parse(string typeName, bool throwOnError) - { - if (throwOnError) - { - Parser parser = new Parser(typeName); - return new TypeNameParser(ref parser, true); - } - else - { - try - { - Parser parser = new Parser(typeName); - return new TypeNameParser(ref parser, true); - } - catch (ArgumentException) - { - return new TypeNameParser(); - } - } - } - - private TypeNameParser(ref Parser parser, bool withAssemblyName) - { - bool genericParameter = parser.pos != 0; - name = parser.NextNamePart(); - nested = null; - parser.ParseNested(ref nested); - genericParameters = null; - parser.ParseGenericParameters(ref genericParameters); - modifiers = null; - parser.ParseModifiers(ref modifiers); - assemblyName = null; - if (withAssemblyName) - { - parser.ParseAssemblyName(genericParameter, ref assemblyName); - } - } - - internal bool Error - { - get { return name == null; } - } - - internal string FirstNamePart - { - get { return name; } - } - - internal string AssemblyName - { - get { return assemblyName; } - } - - private struct Parser - { - private readonly string typeName; - internal int pos; - - internal Parser(string typeName) - { - this.typeName = typeName; - this.pos = 0; - } - - private void Check(bool condition) - { - if (!condition) - { - throw new ArgumentException("Invalid type name '" + typeName + "'"); - } - } - - private void Consume(char c) - { - Check(pos < typeName.Length && typeName[pos++] == c); - } - - private bool TryConsume(char c) - { - if (pos < typeName.Length && typeName[pos] == c) - { - pos++; - return true; - } - else - { - return false; - } - } - - internal string NextNamePart() - { - SkipWhiteSpace(); - int start = pos; - for (; pos < typeName.Length; pos++) - { - char c = typeName[pos]; - if (c == '\\') - { - pos++; - Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1); - } - else if (SpecialChars.IndexOf(c) != -1) - { - break; - } - } - Check(pos - start != 0); - if (start == 0 && pos == typeName.Length) - { - return typeName; - } - else - { - return typeName.Substring(start, pos - start); - } - } - - internal void ParseNested(ref string[] nested) - { - while (TryConsume('+')) - { - Add(ref nested, NextNamePart()); - } - } - - internal void ParseGenericParameters(ref TypeNameParser[] genericParameters) - { - int saved = pos; - if (TryConsume('[')) - { - SkipWhiteSpace(); - if (TryConsume(']') || TryConsume('*') || TryConsume(',')) - { - // it's not a generic parameter list, but an array instead - pos = saved; - return; - } - do - { - SkipWhiteSpace(); - if (TryConsume('[')) - { - Add(ref genericParameters, new TypeNameParser(ref this, true)); - Consume(']'); - } - else - { - Add(ref genericParameters, new TypeNameParser(ref this, false)); - } - } - while (TryConsume(',')); - Consume(']'); - SkipWhiteSpace(); - } - } - - internal void ParseModifiers(ref short[] modifiers) - { - while (pos < typeName.Length) - { - switch (typeName[pos]) - { - case '*': - pos++; - Add(ref modifiers, POINTER); - break; - case '&': - pos++; - Add(ref modifiers, BYREF); - break; - case '[': - pos++; - Add(ref modifiers, ParseArray()); - Consume(']'); - break; - default: - return; - } - SkipWhiteSpace(); - } - } - - internal void ParseAssemblyName(bool genericParameter, ref string assemblyName) - { - if (pos < typeName.Length) - { - if (typeName[pos] == ']' && genericParameter) - { - // ok - } - else - { - Consume(','); - SkipWhiteSpace(); - if (genericParameter) - { - int start = pos; - while (pos < typeName.Length) - { - char c = typeName[pos]; - if (c == '\\') - { - pos++; - // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash - Check(pos < typeName.Length && typeName[pos++] == ']'); - } - else if (c == ']') - { - break; - } - else - { - pos++; - } - } - Check(pos < typeName.Length && typeName[pos] == ']'); - assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]"); - } - else - { - // only when an assembly name is used in a generic type parameter, will it be escaped - assemblyName = typeName.Substring(pos); - } - Check(assemblyName.Length != 0); - } - } - else - { - Check(!genericParameter); - } - } - - private short ParseArray() - { - SkipWhiteSpace(); - Check(pos < typeName.Length); - char c = typeName[pos]; - if (c == ']') - { - return SZARRAY; - } - else if (c == '*') - { - pos++; - SkipWhiteSpace(); - return 1; - } - else - { - short rank = 1; - while (TryConsume(',')) - { - Check(rank < short.MaxValue); - rank++; - SkipWhiteSpace(); - } - return rank; - } - } - - private void SkipWhiteSpace() - { - while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos])) - { - pos++; - } - } - - private static void Add(ref T[] array, T elem) - { - if (array == null) - { - array = new T[] { elem }; - return; - } - Array.Resize(ref array, array.Length + 1); - array[array.Length - 1] = elem; - } - } - - internal Type GetType(Universe universe, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase) - { - Debug.Assert(!resolve || !ignoreCase); - TypeName name = TypeName.Split(this.name); - Type type; - if (assemblyName != null) - { - Assembly asm = universe.Load(assemblyName, context, throwOnError); - if (asm == null) - { - return null; - } - if (resolve) - { - type = asm.ResolveType(context, name); - } - else if (ignoreCase) - { - type = asm.FindTypeIgnoreCase(name.ToLowerInvariant()); - } - else - { - type = asm.FindType(name); - } - } - else if (context == null) - { - if (resolve) - { - type = universe.CoreLib.ResolveType(context, name); - } - else if (ignoreCase) - { - type = universe.CoreLib.FindTypeIgnoreCase(name.ToLowerInvariant()); - } - else - { - type = universe.CoreLib.FindType(name); - } - } - else - { - if (ignoreCase) - { - name = name.ToLowerInvariant(); - type = context.FindTypeIgnoreCase(name); - } - else - { - type = context.FindType(name); - } - if (type == null && context != universe.CoreLib.ManifestModule) - { - if (ignoreCase) - { - type = universe.CoreLib.FindTypeIgnoreCase(name); - } - else - { - type = universe.CoreLib.FindType(name); - } - } - if (type == null && resolve) - { - if (universe.CoreLib.__IsMissing && !context.__IsMissing) - { - type = universe.CoreLib.ResolveType(context, name); - } - else - { - type = context.Assembly.ResolveType(context, name); - } - } - } - return Expand(type, context, throwOnError, originalName, resolve, ignoreCase); - } - - internal Type Expand(Type type, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase) - { - Debug.Assert(!resolve || !ignoreCase); - if (type == null) - { - if (throwOnError) - { - throw new TypeLoadException(originalName); - } - return null; - } - if (nested != null) - { - Type outer; - foreach (string nest in nested) - { - outer = type; - TypeName name = TypeName.Split(TypeNameParser.Unescape(nest)); - type = ignoreCase - ? outer.FindNestedTypeIgnoreCase(name.ToLowerInvariant()) - : outer.FindNestedType(name); - if (type == null) - { - if (resolve) - { - type = outer.Module.universe.GetMissingTypeOrThrow(context, outer.Module, outer, name); - } - else if (throwOnError) - { - throw new TypeLoadException(originalName); - } - else - { - return null; - } - } - } - } - if (genericParameters != null) - { - Type[] typeArgs = new Type[genericParameters.Length]; - for (int i = 0; i < typeArgs.Length; i++) - { - typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName, resolve, ignoreCase); - if (typeArgs[i] == null) - { - return null; - } - } - type = type.MakeGenericType(typeArgs); - } - if (modifiers != null) - { - foreach (short modifier in modifiers) - { - switch (modifier) - { - case SZARRAY: - type = type.MakeArrayType(); - break; - case BYREF: - type = type.MakeByRefType(); - break; - case POINTER: - type = type.MakePointerType(); - break; - default: - type = type.MakeArrayType(modifier); - break; - } - } - } - return type; - } - } + + struct TypeNameParser + { + + const string SpecialChars = "\\+,[]*&"; + const short SZARRAY = -1; + const short BYREF = -2; + const short POINTER = -3; + + readonly string name; + readonly string[] nested; + readonly string assemblyName; + readonly short[] modifiers; + readonly TypeNameParser[] genericParameters; + + internal static string Escape(string name) + { + if (name == null) + return null; + + StringBuilder sb = null; + for (int pos = 0; pos < name.Length; pos++) + { + char c = name[pos]; + switch (c) + { + case '\\': + case '+': + case ',': + case '[': + case ']': + case '*': + case '&': + sb ??= new StringBuilder(name, 0, pos, name.Length + 3); + sb.Append("\\").Append(c); + break; + default: + if (sb != null) + { + sb.Append(c); + } + break; + } + } + + return sb != null ? sb.ToString() : name; + } + + internal static string Unescape(string name) + { + int pos = name.IndexOf('\\'); + if (pos == -1) + return name; + + var sb = new StringBuilder(name, 0, pos, name.Length - 1); + for (; pos < name.Length; pos++) + { + var c = name[pos]; + if (c == '\\') + c = name[++pos]; + + sb.Append(c); + } + + return sb.ToString(); + } + + internal static TypeNameParser Parse(string typeName, bool throwOnError) + { + if (throwOnError) + { + var parser = new Parser(typeName); + return new TypeNameParser(ref parser, true); + } + else + { + try + { + var parser = new Parser(typeName); + return new TypeNameParser(ref parser, true); + } + catch (ArgumentException) + { + return new TypeNameParser(); + } + } + } + + TypeNameParser(ref Parser parser, bool withAssemblyName) + { + bool genericParameter = parser.pos != 0; + name = parser.NextNamePart(); + nested = null; + parser.ParseNested(ref nested); + genericParameters = null; + parser.ParseGenericParameters(ref genericParameters); + modifiers = null; + parser.ParseModifiers(ref modifiers); + assemblyName = null; + if (withAssemblyName) + { + parser.ParseAssemblyName(genericParameter, ref assemblyName); + } + } + + internal bool Error => name == null; + + internal string FirstNamePart => name; + + internal string AssemblyName => assemblyName; + + private struct Parser + { + private readonly string typeName; + internal int pos; + + internal Parser(string typeName) + { + this.typeName = typeName; + this.pos = 0; + } + + private void Check(bool condition) + { + if (!condition) + { + throw new ArgumentException("Invalid type name '" + typeName + "'"); + } + } + + private void Consume(char c) + { + Check(pos < typeName.Length && typeName[pos++] == c); + } + + private bool TryConsume(char c) + { + if (pos < typeName.Length && typeName[pos] == c) + { + pos++; + return true; + } + else + { + return false; + } + } + + internal string NextNamePart() + { + SkipWhiteSpace(); + int start = pos; + for (; pos < typeName.Length; pos++) + { + char c = typeName[pos]; + if (c == '\\') + { + pos++; + Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1); + } + else if (SpecialChars.IndexOf(c) != -1) + { + break; + } + } + Check(pos - start != 0); + if (start == 0 && pos == typeName.Length) + { + return typeName; + } + else + { + return typeName.Substring(start, pos - start); + } + } + + internal void ParseNested(ref string[] nested) + { + while (TryConsume('+')) + { + Add(ref nested, NextNamePart()); + } + } + + internal void ParseGenericParameters(ref TypeNameParser[] genericParameters) + { + int saved = pos; + if (TryConsume('[')) + { + SkipWhiteSpace(); + if (TryConsume(']') || TryConsume('*') || TryConsume(',')) + { + // it's not a generic parameter list, but an array instead + pos = saved; + return; + } + do + { + SkipWhiteSpace(); + if (TryConsume('[')) + { + Add(ref genericParameters, new TypeNameParser(ref this, true)); + Consume(']'); + } + else + { + Add(ref genericParameters, new TypeNameParser(ref this, false)); + } + } + while (TryConsume(',')); + Consume(']'); + SkipWhiteSpace(); + } + } + + internal void ParseModifiers(ref short[] modifiers) + { + while (pos < typeName.Length) + { + switch (typeName[pos]) + { + case '*': + pos++; + Add(ref modifiers, POINTER); + break; + case '&': + pos++; + Add(ref modifiers, BYREF); + break; + case '[': + pos++; + Add(ref modifiers, ParseArray()); + Consume(']'); + break; + default: + return; + } + SkipWhiteSpace(); + } + } + + internal void ParseAssemblyName(bool genericParameter, ref string assemblyName) + { + if (pos < typeName.Length) + { + if (typeName[pos] == ']' && genericParameter) + { + // ok + } + else + { + Consume(','); + SkipWhiteSpace(); + if (genericParameter) + { + int start = pos; + while (pos < typeName.Length) + { + char c = typeName[pos]; + if (c == '\\') + { + pos++; + // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash + Check(pos < typeName.Length && typeName[pos++] == ']'); + } + else if (c == ']') + { + break; + } + else + { + pos++; + } + } + Check(pos < typeName.Length && typeName[pos] == ']'); + assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]"); + } + else + { + // only when an assembly name is used in a generic type parameter, will it be escaped + assemblyName = typeName.Substring(pos); + } + Check(assemblyName.Length != 0); + } + } + else + { + Check(!genericParameter); + } + } + + private short ParseArray() + { + SkipWhiteSpace(); + Check(pos < typeName.Length); + char c = typeName[pos]; + if (c == ']') + { + return SZARRAY; + } + else if (c == '*') + { + pos++; + SkipWhiteSpace(); + return 1; + } + else + { + short rank = 1; + while (TryConsume(',')) + { + Check(rank < short.MaxValue); + rank++; + SkipWhiteSpace(); + } + return rank; + } + } + + private void SkipWhiteSpace() + { + while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos])) + { + pos++; + } + } + + private static void Add(ref T[] array, T elem) + { + if (array == null) + { + array = new T[] { elem }; + return; + } + Array.Resize(ref array, array.Length + 1); + array[array.Length - 1] = elem; + } + } + + internal Type GetType(Universe universe, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase) + { + Debug.Assert(!resolve || !ignoreCase); + var name = TypeName.Split(this.name); + + Type type; + if (assemblyName != null) + { + Assembly asm = universe.Load(assemblyName, context, throwOnError); + if (asm == null) + { + return null; + } + if (resolve) + { + type = asm.ResolveType(context, name); + } + else if (ignoreCase) + { + type = asm.FindTypeIgnoreCase(name.ToLowerInvariant()); + } + else + { + type = asm.FindType(name); + } + } + else if (context == null) + { + if (resolve) + { + type = universe.CoreLib.ResolveType(context, name); + } + else if (ignoreCase) + { + type = universe.CoreLib.FindTypeIgnoreCase(name.ToLowerInvariant()); + } + else + { + type = universe.CoreLib.FindType(name); + } + } + else + { + if (ignoreCase) + { + name = name.ToLowerInvariant(); + type = context.FindTypeIgnoreCase(name); + } + else + { + type = context.FindType(name); + } + if (type == null && context != universe.CoreLib.ManifestModule) + { + if (ignoreCase) + { + type = universe.CoreLib.FindTypeIgnoreCase(name); + } + else + { + type = universe.CoreLib.FindType(name); + } + } + if (type == null && resolve) + { + if (universe.CoreLib.__IsMissing && !context.__IsMissing) + { + type = universe.CoreLib.ResolveType(context, name); + } + else + { + type = context.Assembly.ResolveType(context, name); + } + } + } + return Expand(type, context, throwOnError, originalName, resolve, ignoreCase); + } + + internal Type Expand(Type type, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase) + { + Debug.Assert(!resolve || !ignoreCase); + + if (type == null) + { + if (throwOnError) + throw new TypeLoadException(originalName); + + return null; + } + + if (nested != null) + { + Type outer; + foreach (string nest in nested) + { + outer = type; + var name = TypeName.Split(TypeNameParser.Unescape(nest)); + type = ignoreCase ? outer.FindNestedTypeIgnoreCase(name.ToLowerInvariant()) : outer.FindNestedType(name); + if (type == null) + { + if (resolve) + type = outer.Module.universe.GetMissingTypeOrThrow(context, outer.Module, outer, name); + else if (throwOnError) + throw new TypeLoadException(originalName); + else + return null; + } + } + } + + if (genericParameters != null) + { + var typeArgs = new Type[genericParameters.Length]; + for (int i = 0; i < typeArgs.Length; i++) + { + typeArgs[i] = genericParameters[i].GetType(type.Assembly.universe, context, throwOnError, originalName, resolve, ignoreCase); + if (typeArgs[i] == null) + return null; + } + + type = type.MakeGenericType(typeArgs); + } + + if (modifiers != null) + { + foreach (var modifier in modifiers) + { + switch (modifier) + { + case SZARRAY: + type = type.MakeArrayType(); + break; + case BYREF: + type = type.MakeByRefType(); + break; + case POINTER: + type = type.MakePointerType(); + break; + default: + type = type.MakeArrayType(modifier); + break; + } + } + } + + return type; + } + + } + } diff --git a/src/IKVM.Reflection/Universe.cs b/src/IKVM.Reflection/Universe.cs index 17aeb8e79b..93521a3259 100644 --- a/src/IKVM.Reflection/Universe.cs +++ b/src/IKVM.Reflection/Universe.cs @@ -33,114 +33,13 @@ Jeroen Frijters namespace IKVM.Reflection { - public sealed class ResolveEventArgs : EventArgs - { - - private readonly string name; - private readonly Assembly requestingAssembly; - - public ResolveEventArgs(string name) - : this(name, null) - { - } - - public ResolveEventArgs(string name, Assembly requestingAssembly) - { - this.name = name; - this.requestingAssembly = requestingAssembly; - } - - public string Name - { - get { return name; } - } - - public Assembly RequestingAssembly - { - get { return requestingAssembly; } - } - } - - public enum AssemblyComparisonResult - { - Unknown = 0, - EquivalentFullMatch = 1, - EquivalentWeakNamed = 2, - EquivalentFXUnified = 3, - EquivalentUnified = 4, - NonEquivalentVersion = 5, - NonEquivalent = 6, - EquivalentPartialMatch = 7, - EquivalentPartialWeakNamed = 8, - EquivalentPartialUnified = 9, - EquivalentPartialFXUnified = 10, - NonEquivalentPartialVersion = 11, - } - public delegate Assembly ResolveEventHandler(object sender, ResolveEventArgs args); public delegate void ResolvedMissingMemberHandler(Module requestingModule, MemberInfo member); - /* - * UniverseOptions: - * - * None - * Default behavior, most compatible with System.Reflection[.Emit] - * - * EnableFunctionPointers - * Normally function pointers in signatures are replaced by System.IntPtr - * (for compatibility with System.Reflection), when this option is enabled - * they are represented as first class types (Type.__IsFunctionPointer will - * return true for them). - * - * DisableFusion - * Don't use native Fusion API to resolve assembly names. - * - * DisablePseudoCustomAttributeRetrieval - * Set this option to disable the generaton of pseudo-custom attributes - * when querying custom attributes. - * - * DontProvideAutomaticDefaultConstructor - * Normally TypeBuilder, like System.Reflection.Emit, will provide a default - * constructor for types that meet the requirements. By enabling this - * option this behavior is disabled. - * - * MetadataOnly - * By default, when a module is read in, the stream is kept open to satisfy - * subsequent lazy loading. In MetadataOnly mode only the metadata is read in - * and after that the stream is closed immediately. Subsequent lazy loading - * attempts will fail with an InvalidOperationException. - * APIs that are not available is MetadataOnly mode are: - * - Module.ResolveString() - * - Module.GetSignerCertificate() - * - Module.GetManifestResourceStream() - * - Module.__ReadDataFromRVA() - * - MethodBase.GetMethodBody() - * - FieldInfo.__GetDataFromRVA() - * - * DeterministicOutput - * The generated output file will depend only on the input. In other words, - * the PE file header time stamp will be set to zero and the module version - * id will be based on a SHA1 of the contents, instead of a random guid. - * This option can not be used in combination with PDB file generation. - */ - - [Flags] - public enum UniverseOptions - { - None = 0, - EnableFunctionPointers = 1, - DisableFusion = 2, - DisablePseudoCustomAttributeRetrieval = 4, - DontProvideAutomaticDefaultConstructor = 8, - MetadataOnly = 16, - ResolveMissingMembers = 32, - DisableWindowsRuntimeProjection = 64, - DecodeVersionInfoAttributeBlobs = 128, - DeterministicOutput = 256, - DisableDefaultAssembliesLookup = 512, - } - + /// + /// Provides a view of the types available to the reflection infrastructure. + /// public sealed class Universe : IDisposable { @@ -148,83 +47,84 @@ public sealed class Universe : IDisposable public static readonly string CoreLibName = "netstandard"; +#elif NETFRAMEWORK -#elif NETFRAMEWORK || MONO - - public static readonly string CoreLibName = "mscorlib"; + public static readonly string CoreLibName = "mscorlib"; #endif +#if NETFRAMEWORK internal static readonly bool MonoRuntime = System.Type.GetType("Mono.Runtime") != null; -#if NET461 internal static readonly bool CoreRuntime = false; #else + internal static readonly bool MonoRuntime = false; internal static readonly bool CoreRuntime = true; #endif - private readonly Dictionary canonicalizedTypes = new Dictionary(); - private readonly List assemblies = new List(); - private readonly List dynamicAssemblies = new List(); - private readonly Dictionary assembliesByName = new Dictionary(); - private readonly Dictionary importedTypes = new Dictionary(); - private Dictionary missingTypes; - private bool resolveMissingMembers; - private readonly bool enableFunctionPointers; - private readonly bool useNativeFusion; - private readonly bool returnPseudoCustomAttributes; - private readonly bool automaticallyProvideDefaultConstructor; - private readonly UniverseOptions options; - private Type typeof_System_Object; - private Type typeof_System_ValueType; - private Type typeof_System_Enum; - private Type typeof_System_Void; - private Type typeof_System_Boolean; - private Type typeof_System_Char; - private Type typeof_System_SByte; - private Type typeof_System_Byte; - private Type typeof_System_Int16; - private Type typeof_System_UInt16; - private Type typeof_System_Int32; - private Type typeof_System_UInt32; - private Type typeof_System_Int64; - private Type typeof_System_UInt64; - private Type typeof_System_Single; - private Type typeof_System_Double; - private Type typeof_System_String; - private Type typeof_System_IntPtr; - private Type typeof_System_UIntPtr; - private Type typeof_System_TypedReference; - private Type typeof_System_Type; - private Type typeof_System_Array; - private Type typeof_System_DateTime; - private Type typeof_System_DBNull; - private Type typeof_System_Decimal; - private Type typeof_System_AttributeUsageAttribute; - private Type typeof_System_ContextBoundObject; - private Type typeof_System_MarshalByRefObject; - private Type typeof_System_Console; - private Type typeof_System_IO_TextWriter; - private Type typeof_System_Runtime_InteropServices_DllImportAttribute; - private Type typeof_System_Runtime_InteropServices_FieldOffsetAttribute; - private Type typeof_System_Runtime_InteropServices_MarshalAsAttribute; - private Type typeof_System_Runtime_InteropServices_UnmanagedType; - private Type typeof_System_Runtime_InteropServices_VarEnum; - private Type typeof_System_Runtime_InteropServices_PreserveSigAttribute; - private Type typeof_System_Runtime_InteropServices_CallingConvention; - private Type typeof_System_Runtime_InteropServices_CharSet; - private Type typeof_System_Runtime_CompilerServices_DecimalConstantAttribute; - private Type typeof_System_Reflection_AssemblyCopyrightAttribute; - private Type typeof_System_Reflection_AssemblyTrademarkAttribute; - private Type typeof_System_Reflection_AssemblyProductAttribute; - private Type typeof_System_Reflection_AssemblyCompanyAttribute; - private Type typeof_System_Reflection_AssemblyDescriptionAttribute; - private Type typeof_System_Reflection_AssemblyTitleAttribute; - private Type typeof_System_Reflection_AssemblyInformationalVersionAttribute; - private Type typeof_System_Reflection_AssemblyFileVersionAttribute; - private Type typeof_System_Security_Permissions_CodeAccessSecurityAttribute; - private Type typeof_System_Security_Permissions_PermissionSetAttribute; - private Type typeof_System_Security_Permissions_SecurityAction; - private List resolvers = new List(); - private Predicate missingTypeIsValueType; + + readonly Dictionary canonicalizedTypes = new Dictionary(); + readonly List assemblies = new List(); + readonly List dynamicAssemblies = new List(); + readonly Dictionary assembliesByName = new Dictionary(); + readonly Dictionary importedTypes = new Dictionary(); + Dictionary missingTypes; + bool resolveMissingMembers; + readonly bool enableFunctionPointers; + readonly bool useNativeFusion; + readonly bool returnPseudoCustomAttributes; + readonly bool automaticallyProvideDefaultConstructor; + readonly UniverseOptions options; + Type typeof_System_Object; + Type typeof_System_ValueType; + Type typeof_System_Enum; + Type typeof_System_Void; + Type typeof_System_Boolean; + Type typeof_System_Char; + Type typeof_System_SByte; + Type typeof_System_Byte; + Type typeof_System_Int16; + Type typeof_System_UInt16; + Type typeof_System_Int32; + Type typeof_System_UInt32; + Type typeof_System_Int64; + Type typeof_System_UInt64; + Type typeof_System_Single; + Type typeof_System_Double; + Type typeof_System_String; + Type typeof_System_IntPtr; + Type typeof_System_UIntPtr; + Type typeof_System_TypedReference; + Type typeof_System_Type; + Type typeof_System_Array; + Type typeof_System_DateTime; + Type typeof_System_DBNull; + Type typeof_System_Decimal; + Type typeof_System_AttributeUsageAttribute; + Type typeof_System_ContextBoundObject; + Type typeof_System_MarshalByRefObject; + Type typeof_System_Console; + Type typeof_System_IO_TextWriter; + Type typeof_System_Runtime_InteropServices_DllImportAttribute; + Type typeof_System_Runtime_InteropServices_FieldOffsetAttribute; + Type typeof_System_Runtime_InteropServices_MarshalAsAttribute; + Type typeof_System_Runtime_InteropServices_UnmanagedType; + Type typeof_System_Runtime_InteropServices_VarEnum; + Type typeof_System_Runtime_InteropServices_PreserveSigAttribute; + Type typeof_System_Runtime_InteropServices_CallingConvention; + Type typeof_System_Runtime_InteropServices_CharSet; + Type typeof_System_Runtime_CompilerServices_DecimalConstantAttribute; + Type typeof_System_Reflection_AssemblyCopyrightAttribute; + Type typeof_System_Reflection_AssemblyTrademarkAttribute; + Type typeof_System_Reflection_AssemblyProductAttribute; + Type typeof_System_Reflection_AssemblyCompanyAttribute; + Type typeof_System_Reflection_AssemblyDescriptionAttribute; + Type typeof_System_Reflection_AssemblyTitleAttribute; + Type typeof_System_Reflection_AssemblyInformationalVersionAttribute; + Type typeof_System_Reflection_AssemblyFileVersionAttribute; + Type typeof_System_Security_Permissions_CodeAccessSecurityAttribute; + Type typeof_System_Security_Permissions_PermissionSetAttribute; + Type typeof_System_Security_Permissions_SecurityAction; + List resolvers = new List(); + Predicate missingTypeIsValueType; public Universe() : this(UniverseOptions.None) @@ -241,13 +141,13 @@ public Universe(UniverseOptions options) resolveMissingMembers = (options & UniverseOptions.ResolveMissingMembers) != 0; } - private static bool GetUseNativeFusion() + static bool GetUseNativeFusion() { try { return Environment.OSVersion.Platform == PlatformID.Win32NT && !MonoRuntime && !CoreRuntime && Environment.GetEnvironmentVariable("IKVM_DISABLE_FUSION") == null; } - catch (System.Security.SecurityException) + catch (SecurityException) { return false; } @@ -276,7 +176,12 @@ Type ImportCoreLibType(string ns, string name) return CoreLib.FindType(new TypeName(ns, name)); } - private Type ResolvePrimitive(string name) + /// + /// Resolves the primitive type with teh specified name. + /// + /// + /// + Type ResolvePrimitive(string name) { // Primitive here means that these types have a special metadata encoding, which means that // there can be references to them without referring to them by name explicitly. @@ -284,272 +189,128 @@ private Type ResolvePrimitive(string name) return CoreLib.FindType(new TypeName("System", name)) ?? GetMissingType(null, CoreLib.ManifestModule, null, new TypeName("System", name)); } - internal Type System_Object - { - get { return typeof_System_Object ?? (typeof_System_Object = ResolvePrimitive("Object")); } - } + internal Type System_Object => typeof_System_Object ??= ResolvePrimitive("Object"); - internal Type System_ValueType - { - // System.ValueType is not a primitive, but generic type parameters can have a ValueType constraint - // (we also don't want to return null here) - get { return typeof_System_ValueType ?? (typeof_System_ValueType = ResolvePrimitive("ValueType")); } - } + internal Type System_ValueType => typeof_System_ValueType ??= ResolvePrimitive("ValueType"); - internal Type System_Enum - { - // System.Enum is not a primitive, but we don't want to return null - get { return typeof_System_Enum ?? (typeof_System_Enum = ResolvePrimitive("Enum")); } - } + internal Type System_Enum => typeof_System_Enum ??= ResolvePrimitive("Enum"); - internal Type System_Void - { - get { return typeof_System_Void ?? (typeof_System_Void = ResolvePrimitive("Void")); } - } + internal Type System_Void => typeof_System_Void ??= ResolvePrimitive("Void"); - internal Type System_Boolean - { - get { return typeof_System_Boolean ?? (typeof_System_Boolean = ResolvePrimitive("Boolean")); } - } + internal Type System_Boolean => typeof_System_Boolean ??= ResolvePrimitive("Boolean"); - internal Type System_Char - { - get { return typeof_System_Char ?? (typeof_System_Char = ResolvePrimitive("Char")); } - } + internal Type System_Char => typeof_System_Char ??= ResolvePrimitive("Char"); - internal Type System_SByte - { - get { return typeof_System_SByte ?? (typeof_System_SByte = ResolvePrimitive("SByte")); } - } + internal Type System_SByte => typeof_System_SByte ??= ResolvePrimitive("SByte"); - internal Type System_Byte - { - get { return typeof_System_Byte ?? (typeof_System_Byte = ResolvePrimitive("Byte")); } - } + internal Type System_Byte => typeof_System_Byte ??= ResolvePrimitive("Byte"); - internal Type System_Int16 - { - get { return typeof_System_Int16 ?? (typeof_System_Int16 = ResolvePrimitive("Int16")); } - } + internal Type System_Int16 => typeof_System_Int16 ??= ResolvePrimitive("Int16"); - internal Type System_UInt16 - { - get { return typeof_System_UInt16 ?? (typeof_System_UInt16 = ResolvePrimitive("UInt16")); } - } + internal Type System_UInt16 => typeof_System_UInt16 ??= ResolvePrimitive("UInt16"); - internal Type System_Int32 - { - get { return typeof_System_Int32 ?? (typeof_System_Int32 = ResolvePrimitive("Int32")); } - } + internal Type System_Int32 => typeof_System_Int32 ??= ResolvePrimitive("Int32"); - internal Type System_UInt32 - { - get { return typeof_System_UInt32 ?? (typeof_System_UInt32 = ResolvePrimitive("UInt32")); } - } + internal Type System_UInt32 => typeof_System_UInt32 ??= ResolvePrimitive("UInt32"); - internal Type System_Int64 - { - get { return typeof_System_Int64 ?? (typeof_System_Int64 = ResolvePrimitive("Int64")); } - } + internal Type System_Int64 => typeof_System_Int64 ??= ResolvePrimitive("Int64"); - internal Type System_UInt64 - { - get { return typeof_System_UInt64 ?? (typeof_System_UInt64 = ResolvePrimitive("UInt64")); } - } + internal Type System_UInt64 => typeof_System_UInt64 ??= ResolvePrimitive("UInt64"); - internal Type System_Single - { - get { return typeof_System_Single ?? (typeof_System_Single = ResolvePrimitive("Single")); } - } + internal Type System_Single => typeof_System_Single ??= ResolvePrimitive("Single"); - internal Type System_Double - { - get { return typeof_System_Double ?? (typeof_System_Double = ResolvePrimitive("Double")); } - } + internal Type System_Double => typeof_System_Double ??= ResolvePrimitive("Double"); - internal Type System_String - { - get { return typeof_System_String ?? (typeof_System_String = ResolvePrimitive("String")); } - } + internal Type System_String => typeof_System_String ??= ResolvePrimitive("String"); - internal Type System_IntPtr - { - get { return typeof_System_IntPtr ?? (typeof_System_IntPtr = ResolvePrimitive("IntPtr")); } - } + internal Type System_IntPtr => typeof_System_IntPtr ??= ResolvePrimitive("IntPtr"); - internal Type System_UIntPtr - { - get { return typeof_System_UIntPtr ?? (typeof_System_UIntPtr = ResolvePrimitive("UIntPtr")); } - } + internal Type System_UIntPtr => typeof_System_UIntPtr ??= ResolvePrimitive("UIntPtr"); - internal Type System_TypedReference - { - get { return typeof_System_TypedReference ?? (typeof_System_TypedReference = ResolvePrimitive("TypedReference")); } - } + internal Type System_TypedReference => typeof_System_TypedReference ??= ResolvePrimitive("TypedReference"); - internal Type System_Type - { - // System.Type is not a primitive, but it does have a special encoding in custom attributes - get { return typeof_System_Type ?? (typeof_System_Type = ResolvePrimitive("Type")); } - } + internal Type System_Type => typeof_System_Type ??= ResolvePrimitive("Type"); - internal Type System_Array - { - // System.Array is not a primitive, but it used as a base type for array types (that are primitives) - get { return typeof_System_Array ?? (typeof_System_Array = ResolvePrimitive("Array")); } - } + internal Type System_Array => typeof_System_Array ??= ResolvePrimitive("Array"); - internal Type System_DateTime - { - get { return typeof_System_DateTime ?? (typeof_System_DateTime = ImportCoreLibType("System", "DateTime")); } - } + internal Type System_DateTime => typeof_System_DateTime ?? (typeof_System_DateTime = ImportCoreLibType("System", "DateTime")); - internal Type System_DBNull - { - get { return typeof_System_DBNull ?? (typeof_System_DBNull = ImportCoreLibType("System", "DBNull")); } - } + internal Type System_DBNull => typeof_System_DBNull ?? (typeof_System_DBNull = ImportCoreLibType("System", "DBNull")); - internal Type System_Decimal - { - get { return typeof_System_Decimal ?? (typeof_System_Decimal = ImportCoreLibType("System", "Decimal")); } - } + internal Type System_Decimal => typeof_System_Decimal ?? (typeof_System_Decimal = ImportCoreLibType("System", "Decimal")); - internal Type System_AttributeUsageAttribute - { - get { return typeof_System_AttributeUsageAttribute ?? (typeof_System_AttributeUsageAttribute = ImportCoreLibType("System", "AttributeUsageAttribute")); } - } + internal Type System_AttributeUsageAttribute => typeof_System_AttributeUsageAttribute ??= ImportCoreLibType("System", "AttributeUsageAttribute"); - internal Type System_ContextBoundObject - { - get { return typeof_System_ContextBoundObject ?? (typeof_System_ContextBoundObject = ImportCoreLibType("System", "ContextBoundObject")); } - } + internal Type System_ContextBoundObject => typeof_System_ContextBoundObject ??= ImportCoreLibType("System", "ContextBoundObject"); - internal Type System_MarshalByRefObject - { - get { return typeof_System_MarshalByRefObject ?? (typeof_System_MarshalByRefObject = ImportCoreLibType("System", "MarshalByRefObject")); } - } + internal Type System_MarshalByRefObject => typeof_System_MarshalByRefObject ??= ImportCoreLibType("System", "MarshalByRefObject"); - internal Type System_Console - { - get { return typeof_System_Console ?? (typeof_System_Console = ImportCoreLibType("System", "Console")); } - } + internal Type System_Console => typeof_System_Console ??= ImportCoreLibType("System", "Console"); - internal Type System_IO_TextWriter - { - get { return typeof_System_IO_TextWriter ?? (typeof_System_IO_TextWriter = ImportCoreLibType("System.IO", "TextWriter")); } - } + internal Type System_IO_TextWriter => typeof_System_IO_TextWriter ??= ImportCoreLibType("System.IO", "TextWriter"); - internal Type System_Runtime_InteropServices_DllImportAttribute - { - get { return typeof_System_Runtime_InteropServices_DllImportAttribute ?? (typeof_System_Runtime_InteropServices_DllImportAttribute = ImportCoreLibType("System.Runtime.InteropServices", "DllImportAttribute")); } - } + internal Type System_Runtime_InteropServices_DllImportAttribute => typeof_System_Runtime_InteropServices_DllImportAttribute ??= ImportCoreLibType("System.Runtime.InteropServices", "DllImportAttribute"); - internal Type System_Runtime_InteropServices_FieldOffsetAttribute - { - get { return typeof_System_Runtime_InteropServices_FieldOffsetAttribute ?? (typeof_System_Runtime_InteropServices_FieldOffsetAttribute = ImportCoreLibType("System.Runtime.InteropServices", "FieldOffsetAttribute")); } - } + internal Type System_Runtime_InteropServices_FieldOffsetAttribute => typeof_System_Runtime_InteropServices_FieldOffsetAttribute ??= ImportCoreLibType("System.Runtime.InteropServices", "FieldOffsetAttribute"); - internal Type System_Runtime_InteropServices_MarshalAsAttribute - { - get { return typeof_System_Runtime_InteropServices_MarshalAsAttribute ?? (typeof_System_Runtime_InteropServices_MarshalAsAttribute = ImportCoreLibType("System.Runtime.InteropServices", "MarshalAsAttribute")); } - } + internal Type System_Runtime_InteropServices_MarshalAsAttribute => typeof_System_Runtime_InteropServices_MarshalAsAttribute ??= ImportCoreLibType("System.Runtime.InteropServices", "MarshalAsAttribute"); - internal Type System_Runtime_InteropServices_UnmanagedType - { - get { return typeof_System_Runtime_InteropServices_UnmanagedType ?? (typeof_System_Runtime_InteropServices_UnmanagedType = ImportCoreLibType("System.Runtime.InteropServices", "UnmanagedType")); } - } + internal Type System_Runtime_InteropServices_UnmanagedType => typeof_System_Runtime_InteropServices_UnmanagedType ??= ImportCoreLibType("System.Runtime.InteropServices", "UnmanagedType"); - internal Type System_Runtime_InteropServices_VarEnum - { - get { return typeof_System_Runtime_InteropServices_VarEnum ?? (typeof_System_Runtime_InteropServices_VarEnum = ImportCoreLibType("System.Runtime.InteropServices", "VarEnum")); } - } + internal Type System_Runtime_InteropServices_VarEnum => typeof_System_Runtime_InteropServices_VarEnum ??= ImportCoreLibType("System.Runtime.InteropServices", "VarEnum"); - internal Type System_Runtime_InteropServices_PreserveSigAttribute - { - get { return typeof_System_Runtime_InteropServices_PreserveSigAttribute ?? (typeof_System_Runtime_InteropServices_PreserveSigAttribute = ImportCoreLibType("System.Runtime.InteropServices", "PreserveSigAttribute")); } - } + internal Type System_Runtime_InteropServices_PreserveSigAttribute => typeof_System_Runtime_InteropServices_PreserveSigAttribute ??= ImportCoreLibType("System.Runtime.InteropServices", "PreserveSigAttribute"); - internal Type System_Runtime_InteropServices_CallingConvention - { - get { return typeof_System_Runtime_InteropServices_CallingConvention ?? (typeof_System_Runtime_InteropServices_CallingConvention = ImportCoreLibType("System.Runtime.InteropServices", "CallingConvention")); } - } + internal Type System_Runtime_InteropServices_CallingConvention => typeof_System_Runtime_InteropServices_CallingConvention ??= ImportCoreLibType("System.Runtime.InteropServices", "CallingConvention"); - internal Type System_Runtime_InteropServices_CharSet - { - get { return typeof_System_Runtime_InteropServices_CharSet ?? (typeof_System_Runtime_InteropServices_CharSet = ImportCoreLibType("System.Runtime.InteropServices", "CharSet")); } - } + internal Type System_Runtime_InteropServices_CharSet => typeof_System_Runtime_InteropServices_CharSet ??= ImportCoreLibType("System.Runtime.InteropServices", "CharSet"); - internal Type System_Runtime_CompilerServices_DecimalConstantAttribute - { - get { return typeof_System_Runtime_CompilerServices_DecimalConstantAttribute ?? (typeof_System_Runtime_CompilerServices_DecimalConstantAttribute = ImportCoreLibType("System.Runtime.CompilerServices", "DecimalConstantAttribute")); } - } + internal Type System_Runtime_CompilerServices_DecimalConstantAttribute => typeof_System_Runtime_CompilerServices_DecimalConstantAttribute ??= ImportCoreLibType("System.Runtime.CompilerServices", "DecimalConstantAttribute"); - internal Type System_Reflection_AssemblyCopyrightAttribute - { - get { return typeof_System_Reflection_AssemblyCopyrightAttribute ?? (typeof_System_Reflection_AssemblyCopyrightAttribute = ImportCoreLibType("System.Reflection", "AssemblyCopyrightAttribute")); } - } + internal Type System_Reflection_AssemblyCopyrightAttribute => typeof_System_Reflection_AssemblyCopyrightAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyCopyrightAttribute"); - internal Type System_Reflection_AssemblyTrademarkAttribute - { - get { return typeof_System_Reflection_AssemblyTrademarkAttribute ?? (typeof_System_Reflection_AssemblyTrademarkAttribute = ImportCoreLibType("System.Reflection", "AssemblyTrademarkAttribute")); } - } + internal Type System_Reflection_AssemblyTrademarkAttribute => typeof_System_Reflection_AssemblyTrademarkAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyTrademarkAttribute"); - internal Type System_Reflection_AssemblyProductAttribute - { - get { return typeof_System_Reflection_AssemblyProductAttribute ?? (typeof_System_Reflection_AssemblyProductAttribute = ImportCoreLibType("System.Reflection", "AssemblyProductAttribute")); } - } + internal Type System_Reflection_AssemblyProductAttribute => typeof_System_Reflection_AssemblyProductAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyProductAttribute"); - internal Type System_Reflection_AssemblyCompanyAttribute - { - get { return typeof_System_Reflection_AssemblyCompanyAttribute ?? (typeof_System_Reflection_AssemblyCompanyAttribute = ImportCoreLibType("System.Reflection", "AssemblyCompanyAttribute")); } - } + internal Type System_Reflection_AssemblyCompanyAttribute => typeof_System_Reflection_AssemblyCompanyAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyCompanyAttribute"); - internal Type System_Reflection_AssemblyDescriptionAttribute - { - get { return typeof_System_Reflection_AssemblyDescriptionAttribute ?? (typeof_System_Reflection_AssemblyDescriptionAttribute = ImportCoreLibType("System.Reflection", "AssemblyDescriptionAttribute")); } - } + internal Type System_Reflection_AssemblyDescriptionAttribute => typeof_System_Reflection_AssemblyDescriptionAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyDescriptionAttribute"); - internal Type System_Reflection_AssemblyTitleAttribute - { - get { return typeof_System_Reflection_AssemblyTitleAttribute ?? (typeof_System_Reflection_AssemblyTitleAttribute = ImportCoreLibType("System.Reflection", "AssemblyTitleAttribute")); } - } + internal Type System_Reflection_AssemblyTitleAttribute => typeof_System_Reflection_AssemblyTitleAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyTitleAttribute"); - internal Type System_Reflection_AssemblyInformationalVersionAttribute - { - get { return typeof_System_Reflection_AssemblyInformationalVersionAttribute ?? (typeof_System_Reflection_AssemblyInformationalVersionAttribute = ImportCoreLibType("System.Reflection", "AssemblyInformationalVersionAttribute")); } - } + internal Type System_Reflection_AssemblyInformationalVersionAttribute => typeof_System_Reflection_AssemblyInformationalVersionAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyInformationalVersionAttribute"); - internal Type System_Reflection_AssemblyFileVersionAttribute - { - get { return typeof_System_Reflection_AssemblyFileVersionAttribute ?? (typeof_System_Reflection_AssemblyFileVersionAttribute = ImportCoreLibType("System.Reflection", "AssemblyFileVersionAttribute")); } - } + internal Type System_Reflection_AssemblyFileVersionAttribute => typeof_System_Reflection_AssemblyFileVersionAttribute ??= ImportCoreLibType("System.Reflection", "AssemblyFileVersionAttribute"); - internal Type System_Security_Permissions_CodeAccessSecurityAttribute - { - get { return typeof_System_Security_Permissions_CodeAccessSecurityAttribute ?? (typeof_System_Security_Permissions_CodeAccessSecurityAttribute = ImportCoreLibType("System.Security.Permissions", "CodeAccessSecurityAttribute")); } - } + internal Type System_Security_Permissions_CodeAccessSecurityAttribute => typeof_System_Security_Permissions_CodeAccessSecurityAttribute ??= ImportCoreLibType("System.Security.Permissions", "CodeAccessSecurityAttribute"); - internal Type System_Security_Permissions_PermissionSetAttribute - { - get { return typeof_System_Security_Permissions_PermissionSetAttribute ?? (typeof_System_Security_Permissions_PermissionSetAttribute = ImportCoreLibType("System.Security.Permissions", "PermissionSetAttribute")); } - } + internal Type System_Security_Permissions_PermissionSetAttribute => typeof_System_Security_Permissions_PermissionSetAttribute ??= ImportCoreLibType("System.Security.Permissions", "PermissionSetAttribute"); - internal Type System_Security_Permissions_SecurityAction - { - get { return typeof_System_Security_Permissions_SecurityAction ?? (typeof_System_Security_Permissions_SecurityAction = ImportCoreLibType("System.Security.Permissions", "SecurityAction")); } - } + internal Type System_Security_Permissions_SecurityAction => typeof_System_Security_Permissions_SecurityAction ??= ImportCoreLibType("System.Security.Permissions", "SecurityAction"); + /// + /// Returns true if a CoreLib implementation has been loaded. + /// internal bool HasCoreLib { get { return GetLoadedAssembly(CoreLibName) != null; } } + /// + /// Invoked when an attempt to resolve an assembly occurs. + /// public event ResolveEventHandler AssemblyResolve { add { resolvers.Add(value); } remove { resolvers.Remove(value); } } + /// + /// Imports the specified into the environment. + /// + /// + /// public Type Import(System.Type type) { if (!importedTypes.TryGetValue(type, out var imported)) @@ -626,7 +387,12 @@ Type ImportImpl(System.Type type) return ResolveType(Import(TypeUtil.GetAssembly(type)), type.FullName); } - private Assembly Import(System.Reflection.Assembly asm) + /// + /// Imports the specified into the environment. + /// + /// + /// + Assembly Import(System.Reflection.Assembly asm) { return Load(asm.FullName); } @@ -634,24 +400,22 @@ private Assembly Import(System.Reflection.Assembly asm) public RawModule OpenRawModule(string path) { path = Path.GetFullPath(path); + FileStream fs = null; RawModule module; + try { fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); module = OpenRawModule(fs, path); - if (!MetadataOnly) - { + if (MetadataOnly == false) fs = null; - } } finally { - if (fs != null) - { - fs.Dispose(); - } + fs?.Dispose(); } + return module; } @@ -665,25 +429,25 @@ public RawModule OpenMappedRawModule(Stream stream, string location) return OpenRawModule(stream, location, true); } - private RawModule OpenRawModule(Stream stream, string location, bool mapped) + RawModule OpenRawModule(Stream stream, string location, bool mapped) { if (!stream.CanRead || !stream.CanSeek || stream.Position != 0) - { throw new ArgumentException("Stream must support read/seek and current position must be zero.", "stream"); - } + return new RawModule(new ModuleReader(null, this, stream, location, mapped)); } public Assembly LoadAssembly(RawModule module) { - string refname = module.GetAssemblyName().FullName; - Assembly asm = GetLoadedAssembly(refname); + var refname = module.GetAssemblyName().FullName; + var asm = GetLoadedAssembly(refname); if (asm == null) { - AssemblyReader asm1 = module.ToAssembly(); + var asm1 = module.ToAssembly(); assemblies.Add(asm1); asm = asm1; } + return asm; } @@ -691,29 +455,24 @@ public Assembly LoadFile(string path) { try { - using (RawModule module = OpenRawModule(path)) - { + using (var module = OpenRawModule(path)) return LoadAssembly(module); - } } - catch (IOException x) + catch (IOException e) { - throw new FileNotFoundException(x.Message, x); + throw new FileNotFoundException(e.Message, e); } - catch (UnauthorizedAccessException x) + catch (UnauthorizedAccessException e) { - throw new FileNotFoundException(x.Message, x); + throw new FileNotFoundException(e.Message, e); } } - private static string GetSimpleAssemblyName(string refname) + static string GetSimpleAssemblyName(string refname) { - int pos; - string name; - if (Fusion.ParseAssemblySimpleName(refname, out pos, out name) != ParseAssemblyResult.OK) - { + if (Fusion.ParseAssemblySimpleName(refname, out var pos, out var name) != ParseAssemblyResult.OK) throw new ArgumentException(); - } + return name; } @@ -790,45 +549,45 @@ internal Assembly Load(string refname, Module requestingModule, bool throwOnErro return null; } - private Assembly DefaultResolver(string refname, bool throwOnError) + Assembly DefaultResolver(string refname, bool throwOnError) { - Assembly asm = GetDynamicAssembly(refname); + var asm = GetDynamicAssembly(refname); if (asm != null) return asm; #if NETFRAMEWORK - string fileName; - if (throwOnError) - { - try - { - fileName = System.Reflection.Assembly.ReflectionOnlyLoad(refname).Location; - } - catch (System.BadImageFormatException x) - { - throw new BadImageFormatException(x.Message, x); - } - } - else - { - try - { - fileName = System.Reflection.Assembly.ReflectionOnlyLoad(refname).Location; - } - catch (System.BadImageFormatException x) - { - throw new BadImageFormatException(x.Message, x); - } - catch (FileNotFoundException) - { - // we intentionally only swallow the FileNotFoundException, if the file exists but isn't a valid assembly, - // we should throw an exception - return null; - } - } - - return LoadFile(fileName); + string fileName; + if (throwOnError) + { + try + { + fileName = System.Reflection.Assembly.ReflectionOnlyLoad(refname).Location; + } + catch (System.BadImageFormatException x) + { + throw new BadImageFormatException(x.Message, x); + } + } + else + { + try + { + fileName = System.Reflection.Assembly.ReflectionOnlyLoad(refname).Location; + } + catch (System.BadImageFormatException x) + { + throw new BadImageFormatException(x.Message, x); + } + catch (FileNotFoundException) + { + // we intentionally only swallow the FileNotFoundException, if the file exists but isn't a valid assembly, + // we should throw an exception + return null; + } + } + + return LoadFile(fileName); #else return null; #endif @@ -887,66 +646,53 @@ public Type ResolveType(Assembly context, string assemblyQualifiedTypeName) return parser.GetType(this, context == null ? null : context.ManifestModule, false, assemblyQualifiedTypeName, true, false); } + /// + /// Gets the builtin type with the specified name. The parameter must be "System". + /// + /// + /// + /// public Type GetBuiltInType(string ns, string name) { if (ns != "System") - { return null; - } - switch (name) - { - case "Boolean": - return System_Boolean; - case "Char": - return System_Char; - case "Object": - return System_Object; - case "String": - return System_String; - case "Single": - return System_Single; - case "Double": - return System_Double; - case "SByte": - return System_SByte; - case "Int16": - return System_Int16; - case "Int32": - return System_Int32; - case "Int64": - return System_Int64; - case "IntPtr": - return System_IntPtr; - case "UIntPtr": - return System_UIntPtr; - case "TypedReference": - return System_TypedReference; - case "Byte": - return System_Byte; - case "UInt16": - return System_UInt16; - case "UInt32": - return System_UInt32; - case "UInt64": - return System_UInt64; - case "Void": - return System_Void; - default: - return null; - } + + return name switch + { + "Boolean" => System_Boolean, + "Char" => System_Char, + "Object" => System_Object, + "String" => System_String, + "Single" => System_Single, + "Double" => System_Double, + "SByte" => System_SByte, + "Int16" => System_Int16, + "Int32" => System_Int32, + "Int64" => System_Int64, + "IntPtr" => System_IntPtr, + "UIntPtr" => System_UIntPtr, + "TypedReference" => System_TypedReference, + "Byte" => System_Byte, + "UInt16" => System_UInt16, + "UInt32" => System_UInt32, + "UInt64" => System_UInt64, + "Void" => System_Void, + _ => null, + }; } + /// + /// Gets the set of all loaded assemblies. + /// + /// public Assembly[] GetAssemblies() { - Assembly[] array = new Assembly[assemblies.Count + dynamicAssemblies.Count]; + var array = new Assembly[assemblies.Count + dynamicAssemblies.Count]; for (int i = 0; i < assemblies.Count; i++) - { array[i] = assemblies[i]; - } for (int i = 0, j = assemblies.Count; j < array.Length; i++, j++) - { array[j] = dynamicAssemblies[i]; - } + return array; } diff --git a/src/IKVM.Reflection/UniverseOptions.cs b/src/IKVM.Reflection/UniverseOptions.cs new file mode 100644 index 0000000000..04bf11637a --- /dev/null +++ b/src/IKVM.Reflection/UniverseOptions.cs @@ -0,0 +1,87 @@ +/* + Copyright (C) 2009-2013 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +namespace IKVM.Reflection +{ + /* + * UniverseOptions: + * + * None + * Default behavior, most compatible with System.Reflection[.Emit] + * + * EnableFunctionPointers + * Normally function pointers in signatures are replaced by System.IntPtr + * (for compatibility with System.Reflection), when this option is enabled + * they are represented as first class types (Type.__IsFunctionPointer will + * return true for them). + * + * DisableFusion + * Don't use native Fusion API to resolve assembly names. + * + * DisablePseudoCustomAttributeRetrieval + * Set this option to disable the generaton of pseudo-custom attributes + * when querying custom attributes. + * + * DontProvideAutomaticDefaultConstructor + * Normally TypeBuilder, like System.Reflection.Emit, will provide a default + * constructor for types that meet the requirements. By enabling this + * option this behavior is disabled. + * + * MetadataOnly + * By default, when a module is read in, the stream is kept open to satisfy + * subsequent lazy loading. In MetadataOnly mode only the metadata is read in + * and after that the stream is closed immediately. Subsequent lazy loading + * attempts will fail with an InvalidOperationException. + * APIs that are not available is MetadataOnly mode are: + * - Module.ResolveString() + * - Module.GetSignerCertificate() + * - Module.GetManifestResourceStream() + * - Module.__ReadDataFromRVA() + * - MethodBase.GetMethodBody() + * - FieldInfo.__GetDataFromRVA() + * + * DeterministicOutput + * The generated output file will depend only on the input. In other words, + * the PE file header time stamp will be set to zero and the module version + * id will be based on a SHA1 of the contents, instead of a random guid. + * This option can not be used in combination with PDB file generation. + */ + + [Flags] + public enum UniverseOptions + { + None = 0, + EnableFunctionPointers = 1, + DisableFusion = 2, + DisablePseudoCustomAttributeRetrieval = 4, + DontProvideAutomaticDefaultConstructor = 8, + MetadataOnly = 16, + ResolveMissingMembers = 32, + DisableWindowsRuntimeProjection = 64, + DecodeVersionInfoAttributeBlobs = 128, + DeterministicOutput = 256, + DisableDefaultAssembliesLookup = 512, + } +} diff --git a/src/IKVM.Reflection/Writer/ModuleWriter.cs b/src/IKVM.Reflection/Writer/ModuleWriter.cs index 2d15aeb307..2418d20279 100644 --- a/src/IKVM.Reflection/Writer/ModuleWriter.cs +++ b/src/IKVM.Reflection/Writer/ModuleWriter.cs @@ -22,114 +22,107 @@ Jeroen Frijters */ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Security.Cryptography; + using IKVM.Reflection.Emit; -using IKVM.Reflection.Impl; using IKVM.Reflection.Metadata; namespace IKVM.Reflection.Writer { - static class ModuleWriter - { - internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, - PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, - ResourceSection resources, int entryPointToken) - { - WriteModule(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, null); - } - - internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, - PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, - ResourceSection resources, int entryPointToken, Stream stream) - { - if (stream == null) - { - string fileName = moduleBuilder.FullyQualifiedName; - bool mono = System.Type.GetType("Mono.Runtime") != null; - if (mono) - { - try - { - // Mono mmaps the file, so unlink the previous version since it may be in use - File.Delete(fileName); - } - catch { } - } - using (FileStream fs = new FileStream(fileName, FileMode.Create)) - { - WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, fs); - } - // if we're running on Mono, mark the module as executable by using a Mono private API extension - if (mono) - { - File.SetAttributes(fileName, (FileAttributes)(unchecked((int)0x80000000))); - } - } - else - { - WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, stream); - } - } - - private static void WriteModuleImpl(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, - PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, - ResourceSection resources, int entryPointToken, Stream stream) - { - moduleBuilder.ApplyUnmanagedExports(imageFileMachine); - moduleBuilder.FixupMethodBodyTokens(); - - int moduleVersionIdIndex = moduleBuilder.Guids.Add(moduleBuilder.GetModuleVersionIdOrEmpty()); - moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleVersionIdIndex, 0, 0); - - if (moduleBuilder.UserStrings.IsEmpty) - { - // for compat with Ref.Emit, if there aren't any user strings, we add one - moduleBuilder.UserStrings.Add(" "); - } - - if (resources != null) - { - resources.Finish(); - } - - PEWriter writer = new PEWriter(stream); - writer.Headers.OptionalHeader.FileAlignment = (uint)moduleBuilder.__FileAlignment; - switch (imageFileMachine) - { - case ImageFileMachine.I386: - writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386; - writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE; - writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); - break; - case ImageFileMachine.ARM: - writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM; - writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; - writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); - writer.Headers.OptionalHeader.SectionAlignment = 0x1000; - break; - case ImageFileMachine.AMD64: - writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64; - writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; - writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; - writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; - writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); - writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; - writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; - break; - case ImageFileMachine.IA64: - writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64; - writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; - writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; - writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; - writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); - writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; - writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; - break; - case ImageFileMachine.ARM64: - writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM64; + + static class ModuleWriter + { + + internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken) + { + WriteModule(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, null); + } + + internal static void WriteModule(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken, Stream stream) + { + if (stream == null) + { + var fileName = moduleBuilder.FullyQualifiedName; + var mono = System.Type.GetType("Mono.Runtime") != null; + if (mono) + { + try + { + // Mono mmaps the file, so unlink the previous version since it may be in use + File.Delete(fileName); + } + catch + { + + } + } + + using (var fs = new FileStream(fileName, FileMode.Create)) + WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, fs); + + // if we're running on Mono, mark the module as executable by using a Mono private API extension + if (mono) + File.SetAttributes(fileName, (FileAttributes)(unchecked((int)0x80000000))); + } + else + { + WriteModuleImpl(keyPair, publicKey, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, resources, entryPointToken, stream); + } + } + + static void WriteModuleImpl(StrongNameKeyPair keyPair, byte[] publicKey, ModuleBuilder moduleBuilder, PEFileKinds fileKind, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine, ResourceSection resources, int entryPointToken, Stream stream) + { + moduleBuilder.ApplyUnmanagedExports(imageFileMachine); + moduleBuilder.FixupMethodBodyTokens(); + + int moduleVersionIdIndex = moduleBuilder.Guids.Add(moduleBuilder.GetModuleVersionIdOrEmpty()); + moduleBuilder.ModuleTable.Add(0, moduleBuilder.Strings.Add(moduleBuilder.moduleName), moduleVersionIdIndex, 0, 0); + + if (moduleBuilder.UserStrings.IsEmpty) + { + // for compat with Ref.Emit, if there aren't any user strings, we add one + moduleBuilder.UserStrings.Add(" "); + } + + if (resources != null) + resources.Finish(); + + var writer = new PEWriter(stream); + writer.Headers.OptionalHeader.FileAlignment = (uint)moduleBuilder.__FileAlignment; + switch (imageFileMachine) + { + case ImageFileMachine.I386: + writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386; + writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE; + writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); + break; + case ImageFileMachine.ARM: + writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM; + writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; + writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x100000); + writer.Headers.OptionalHeader.SectionAlignment = 0x1000; + break; + case ImageFileMachine.AMD64: + writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64; + writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; + writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; + writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; + writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); + writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; + writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; + break; + case ImageFileMachine.IA64: + writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64; + writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; + writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; + writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; + writer.Headers.OptionalHeader.SizeOfStackReserve = moduleBuilder.GetStackReserve(0x400000); + writer.Headers.OptionalHeader.SizeOfStackCommit = 0x4000; + writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; + break; + case ImageFileMachine.ARM64: + writer.Headers.FileHeader.Machine = IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM64; writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE; writer.Headers.FileHeader.SizeOfOptionalHeader = 0xF0; writer.Headers.OptionalHeader.Magic = IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC; @@ -138,413 +131,401 @@ private static void WriteModuleImpl(StrongNameKeyPair keyPair, byte[] publicKey, writer.Headers.OptionalHeader.SizeOfHeapCommit = 0x2000; break; default: - throw new ArgumentOutOfRangeException("imageFileMachine"); - } - if (fileKind == PEFileKinds.Dll) - { - writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_DLL; - } - - switch (fileKind) - { - case PEFileKinds.WindowApplication: - writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_GUI; - break; - default: - writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_CUI; - break; - } - writer.Headers.OptionalHeader.DllCharacteristics = (ushort)moduleBuilder.__DllCharacteristics; - - CliHeader cliHeader = new CliHeader(); - cliHeader.Cb = 0x48; - cliHeader.MajorRuntimeVersion = 2; - cliHeader.MinorRuntimeVersion = moduleBuilder.MDStreamVersion < 0x20000 ? (ushort)0 : (ushort)5; - if ((portableExecutableKind & PortableExecutableKinds.ILOnly) != 0) - { - cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_ILONLY; - } - if ((portableExecutableKind & PortableExecutableKinds.Required32Bit) != 0) - { - cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED; - } - if ((portableExecutableKind & PortableExecutableKinds.Preferred32Bit) != 0) - { - cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED; - } - if (keyPair != null) - { - cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_STRONGNAMESIGNED; - } - if (ModuleBuilder.IsPseudoToken(entryPointToken)) - { - entryPointToken = moduleBuilder.ResolvePseudoToken(entryPointToken); - } - cliHeader.EntryPointToken = (uint)entryPointToken; - - moduleBuilder.Strings.Freeze(); - moduleBuilder.UserStrings.Freeze(); - moduleBuilder.Guids.Freeze(); - moduleBuilder.Blobs.Freeze(); - MetadataWriter mw = new MetadataWriter(moduleBuilder, stream); - moduleBuilder.Tables.Freeze(mw); - TextSection code = new TextSection(writer, cliHeader, moduleBuilder, ComputeStrongNameSignatureLength(publicKey)); - - // Export Directory - if (code.ExportDirectoryLength != 0) - { - writer.Headers.OptionalHeader.DataDirectory[0].VirtualAddress = code.ExportDirectoryRVA; - writer.Headers.OptionalHeader.DataDirectory[0].Size = code.ExportDirectoryLength; - } - - // Import Directory - if (code.ImportDirectoryLength != 0) - { - writer.Headers.OptionalHeader.DataDirectory[1].VirtualAddress = code.ImportDirectoryRVA; - writer.Headers.OptionalHeader.DataDirectory[1].Size = code.ImportDirectoryLength; - } - - // Import Address Table Directory - if (code.ImportAddressTableLength != 0) - { - writer.Headers.OptionalHeader.DataDirectory[12].VirtualAddress = code.ImportAddressTableRVA; - writer.Headers.OptionalHeader.DataDirectory[12].Size = code.ImportAddressTableLength; - } - - // COM Descriptor Directory - writer.Headers.OptionalHeader.DataDirectory[14].VirtualAddress = code.ComDescriptorRVA; - writer.Headers.OptionalHeader.DataDirectory[14].Size = code.ComDescriptorLength; - - // Debug Directory - if (code.DebugDirectoryLength != 0) - { - writer.Headers.OptionalHeader.DataDirectory[6].VirtualAddress = code.DebugDirectoryRVA; - writer.Headers.OptionalHeader.DataDirectory[6].Size = code.DebugDirectoryLength; - } - - // Set the PE File timestamp - writer.Headers.FileHeader.TimeDateStamp = moduleBuilder.GetTimeDateStamp(); - - // we need to start by computing the number of sections, because code.PointerToRawData depends on that - writer.Headers.FileHeader.NumberOfSections = 2; - - if (moduleBuilder.initializedData.Length != 0) - { - // .sdata - writer.Headers.FileHeader.NumberOfSections++; - } - - if (resources != null) - { - // .rsrc - writer.Headers.FileHeader.NumberOfSections++; - } - - SectionHeader text = new SectionHeader(); - text.Name = ".text"; - text.VirtualAddress = code.BaseRVA; - text.VirtualSize = (uint)code.Length; - text.PointerToRawData = code.PointerToRawData; - text.SizeOfRawData = writer.ToFileAlignment((uint)code.Length); - text.Characteristics = SectionHeader.IMAGE_SCN_CNT_CODE | SectionHeader.IMAGE_SCN_MEM_EXECUTE | SectionHeader.IMAGE_SCN_MEM_READ; - - SectionHeader sdata = new SectionHeader(); - sdata.Name = ".sdata"; - sdata.VirtualAddress = text.VirtualAddress + writer.ToSectionAlignment(text.VirtualSize); - sdata.VirtualSize = (uint)moduleBuilder.initializedData.Length; - sdata.PointerToRawData = text.PointerToRawData + text.SizeOfRawData; - sdata.SizeOfRawData = writer.ToFileAlignment((uint)moduleBuilder.initializedData.Length); - sdata.Characteristics = SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_MEM_WRITE; - - SectionHeader rsrc = new SectionHeader(); - rsrc.Name = ".rsrc"; - rsrc.VirtualAddress = sdata.VirtualAddress + writer.ToSectionAlignment(sdata.VirtualSize); - rsrc.PointerToRawData = sdata.PointerToRawData + sdata.SizeOfRawData; - rsrc.VirtualSize = resources == null ? 0 : (uint)resources.Length; - rsrc.SizeOfRawData = writer.ToFileAlignment(rsrc.VirtualSize); - rsrc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA; - - if (rsrc.SizeOfRawData != 0) - { - // Resource Directory - writer.Headers.OptionalHeader.DataDirectory[2].VirtualAddress = rsrc.VirtualAddress; - writer.Headers.OptionalHeader.DataDirectory[2].Size = rsrc.VirtualSize; - } - - SectionHeader reloc = new SectionHeader(); - reloc.Name = ".reloc"; - reloc.VirtualAddress = rsrc.VirtualAddress + writer.ToSectionAlignment(rsrc.VirtualSize); - reloc.VirtualSize = code.PackRelocations(); - reloc.PointerToRawData = rsrc.PointerToRawData + rsrc.SizeOfRawData; - reloc.SizeOfRawData = writer.ToFileAlignment(reloc.VirtualSize); - reloc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_DISCARDABLE; - - if (reloc.SizeOfRawData != 0) - { - // Base Relocation Directory - writer.Headers.OptionalHeader.DataDirectory[5].VirtualAddress = reloc.VirtualAddress; - writer.Headers.OptionalHeader.DataDirectory[5].Size = reloc.VirtualSize; - } - - writer.Headers.OptionalHeader.SizeOfCode = text.SizeOfRawData; - writer.Headers.OptionalHeader.SizeOfInitializedData = sdata.SizeOfRawData + rsrc.SizeOfRawData + reloc.SizeOfRawData; - writer.Headers.OptionalHeader.SizeOfUninitializedData = 0; - writer.Headers.OptionalHeader.SizeOfImage = reloc.VirtualAddress + writer.ToSectionAlignment(reloc.VirtualSize); - writer.Headers.OptionalHeader.SizeOfHeaders = text.PointerToRawData; - writer.Headers.OptionalHeader.BaseOfCode = code.BaseRVA; - writer.Headers.OptionalHeader.BaseOfData = sdata.VirtualAddress; - writer.Headers.OptionalHeader.ImageBase = (ulong)moduleBuilder.__ImageBase; - - if (imageFileMachine == ImageFileMachine.IA64) - { - // apparently for IA64 AddressOfEntryPoint points to the address of the entry point - // (i.e. there is an additional layer of indirection), so we add the offset to the pointer - writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + 0x20; - } - else - { - writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + writer.Thumb; - } - - writer.WritePEHeaders(); - writer.WriteSectionHeader(text); - if (sdata.SizeOfRawData != 0) - { - writer.WriteSectionHeader(sdata); - } - if (rsrc.SizeOfRawData != 0) - { - writer.WriteSectionHeader(rsrc); - } - if (reloc.SizeOfRawData != 0) - { - writer.WriteSectionHeader(reloc); - } - - stream.Seek(text.PointerToRawData, SeekOrigin.Begin); - uint guidHeapOffset; - code.Write(mw, sdata.VirtualAddress, out guidHeapOffset); - - if (sdata.SizeOfRawData != 0) - { - stream.Seek(sdata.PointerToRawData, SeekOrigin.Begin); - mw.Write(moduleBuilder.initializedData); - } - - if (rsrc.SizeOfRawData != 0) - { - stream.Seek(rsrc.PointerToRawData, SeekOrigin.Begin); - resources.Write(mw, rsrc.VirtualAddress); - } - - if (reloc.SizeOfRawData != 0) - { - stream.Seek(reloc.PointerToRawData, SeekOrigin.Begin); - code.WriteRelocations(mw); - } - - // file alignment - stream.SetLength(reloc.PointerToRawData + reloc.SizeOfRawData); - - // if we don't have a guid, generate one based on the contents of the assembly - if (moduleBuilder.universe.Deterministic && moduleBuilder.GetModuleVersionIdOrEmpty() == Guid.Empty) - { - Guid guid = GenerateModuleVersionId(stream); - stream.Position = guidHeapOffset + (moduleVersionIdIndex - 1) * 16; - stream.Write(guid.ToByteArray(), 0, 16); - moduleBuilder.__SetModuleVersionId(guid); - } - - // do the strong naming - if (keyPair != null) - { - StrongName(stream, keyPair, writer.HeaderSize, text.PointerToRawData, code.StrongNameSignatureRVA - text.VirtualAddress + text.PointerToRawData, code.StrongNameSignatureLength); - } - - if (moduleBuilder.symbolWriter != null) - { - moduleBuilder.WriteSymbolTokenMap(); - moduleBuilder.symbolWriter.Close(); - } - } - - private static int ComputeStrongNameSignatureLength(byte[] publicKey) - { - if (publicKey == null) - { - return 0; - } - else if (publicKey.Length == 16) - { - // it must be the ECMA pseudo public key, we don't know the key size of the real key, but currently both Mono and Microsoft use a 1024 bit key size - return 128; - } - else - { - // for the supported strong naming algorithms, the signature size is the same as the key size - // (we have to subtract 32 for the header) - return publicKey.Length - 32; - } - } - - private static void StrongName(Stream stream, StrongNameKeyPair keyPair, uint headerLength, uint textSectionFileOffset, uint strongNameSignatureFileOffset, uint strongNameSignatureLength) - { - byte[] hash; - using (SHA1 sha1 = SHA1.Create()) - { - stream.Seek(0, SeekOrigin.Begin); - Stream skipStream = new SkipStream(stream, strongNameSignatureFileOffset, strongNameSignatureLength); - skipStream = new SkipStream(skipStream, headerLength, textSectionFileOffset - headerLength); - hash = sha1.ComputeHash(skipStream); - } - using (RSACryptoServiceProvider rsa = keyPair.CreateRSA()) - { - byte[] signature = rsa.SignHash(hash, "1.3.14.3.2.26"); - Array.Reverse(signature); - if (signature.Length != strongNameSignatureLength) - { - throw new InvalidOperationException("Signature length mismatch"); - } - stream.Seek(strongNameSignatureFileOffset, SeekOrigin.Begin); - stream.Write(signature, 0, signature.Length); - } - - // compute the PE checksum - stream.Seek(0, SeekOrigin.Begin); - int count = (int)stream.Length / 4; - BinaryReader br = new BinaryReader(stream); - long sum = 0; - for (int i = 0; i < count; i++) - { - sum += br.ReadUInt32(); - int carry = (int)(sum >> 32); - sum &= 0xFFFFFFFFU; - sum += carry; - } - while ((sum >> 16) != 0) - { - sum = (sum & 0xFFFF) + (sum >> 16); - } - sum += stream.Length; - - // write the PE checksum, note that it is always at offset 0xD8 in the file - ByteBuffer bb = new ByteBuffer(4); - bb.Write((int)sum); - stream.Seek(0xD8, SeekOrigin.Begin); - bb.WriteTo(stream); - } - - private static Guid GenerateModuleVersionId(Stream stream) - { - byte[] hash; - using (SHA1 sha1 = SHA1.Create()) - { - stream.Seek(0, SeekOrigin.Begin); - hash = sha1.ComputeHash(stream); - } - byte[] bytes = new byte[16]; - Buffer.BlockCopy(hash, 0, bytes, 0, bytes.Length); - // set GUID type to "version 4" (random) - bytes[7] &= 0x0F; - bytes[7] |= 0x40; - bytes[8] &= 0x3F; - bytes[8] |= 0x80; - return new Guid(bytes); - } - } - - sealed class SkipStream : Stream - { - private readonly Stream stream; - private long skipOffset; - private long skipLength; - - internal SkipStream(Stream stream, long skipOffset, long skipLength) - { - if (skipOffset < 0 || skipLength < 0) - { - throw new ArgumentOutOfRangeException(); - } - this.stream = stream; - this.skipOffset = skipOffset; - this.skipLength = skipLength; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - stream.Dispose(); - } - } - - public override bool CanRead - { - get { return stream.CanRead; } - } - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (skipLength != 0 && skipOffset < count) - { - if (skipOffset != 0) - { - count = (int)skipOffset; - } - else - { - // note that we loop forever if the skipped part lies beyond EOF - while (skipLength != 0) - { - // use the output buffer as scratch space - skipLength -= stream.Read(buffer, offset, (int)Math.Min(count, skipLength)); - } - } - } - int totalBytesRead = stream.Read(buffer, offset, count); - skipOffset -= totalBytesRead; - return totalBytesRead; - } - - public override long Length - { - get { throw new NotSupportedException(); } - } - - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } + throw new ArgumentOutOfRangeException("imageFileMachine"); + } + + if (fileKind == PEFileKinds.Dll) + writer.Headers.FileHeader.Characteristics |= IMAGE_FILE_HEADER.IMAGE_FILE_DLL; + + switch (fileKind) + { + case PEFileKinds.WindowApplication: + writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_GUI; + break; + default: + writer.Headers.OptionalHeader.Subsystem = IMAGE_OPTIONAL_HEADER.IMAGE_SUBSYSTEM_WINDOWS_CUI; + break; + } + writer.Headers.OptionalHeader.DllCharacteristics = (ushort)moduleBuilder.__DllCharacteristics; + + var cliHeader = new CliHeader(); + cliHeader.Cb = 0x48; + cliHeader.MajorRuntimeVersion = 2; + cliHeader.MinorRuntimeVersion = moduleBuilder.MDStreamVersion < 0x20000 ? (ushort)0 : (ushort)5; + + if ((portableExecutableKind & PortableExecutableKinds.ILOnly) != 0) + cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_ILONLY; + + if ((portableExecutableKind & PortableExecutableKinds.Required32Bit) != 0) + cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED; + + if ((portableExecutableKind & PortableExecutableKinds.Preferred32Bit) != 0) + cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED; + + if (keyPair != null) + cliHeader.Flags |= CliHeader.COMIMAGE_FLAGS_STRONGNAMESIGNED; + + if (ModuleBuilder.IsPseudoToken(entryPointToken)) + entryPointToken = moduleBuilder.ResolvePseudoToken(entryPointToken); + + cliHeader.EntryPointToken = (uint)entryPointToken; + + moduleBuilder.Strings.Freeze(); + moduleBuilder.UserStrings.Freeze(); + moduleBuilder.Guids.Freeze(); + moduleBuilder.Blobs.Freeze(); + var mw = new MetadataWriter(moduleBuilder, stream); + moduleBuilder.Tables.Freeze(mw); + var code = new TextSection(writer, cliHeader, moduleBuilder, ComputeStrongNameSignatureLength(publicKey)); + + // Export Directory + if (code.ExportDirectoryLength != 0) + { + writer.Headers.OptionalHeader.DataDirectory[0].VirtualAddress = code.ExportDirectoryRVA; + writer.Headers.OptionalHeader.DataDirectory[0].Size = code.ExportDirectoryLength; + } + + // Import Directory + if (code.ImportDirectoryLength != 0) + { + writer.Headers.OptionalHeader.DataDirectory[1].VirtualAddress = code.ImportDirectoryRVA; + writer.Headers.OptionalHeader.DataDirectory[1].Size = code.ImportDirectoryLength; + } + + // Import Address Table Directory + if (code.ImportAddressTableLength != 0) + { + writer.Headers.OptionalHeader.DataDirectory[12].VirtualAddress = code.ImportAddressTableRVA; + writer.Headers.OptionalHeader.DataDirectory[12].Size = code.ImportAddressTableLength; + } + + // COM Descriptor Directory + writer.Headers.OptionalHeader.DataDirectory[14].VirtualAddress = code.ComDescriptorRVA; + writer.Headers.OptionalHeader.DataDirectory[14].Size = code.ComDescriptorLength; + + // Debug Directory + if (code.DebugDirectoryLength != 0) + { + writer.Headers.OptionalHeader.DataDirectory[6].VirtualAddress = code.DebugDirectoryRVA; + writer.Headers.OptionalHeader.DataDirectory[6].Size = code.DebugDirectoryLength; + } + + // Set the PE File timestamp + writer.Headers.FileHeader.TimeDateStamp = moduleBuilder.GetTimeDateStamp(); + + // we need to start by computing the number of sections, because code.PointerToRawData depends on that + writer.Headers.FileHeader.NumberOfSections = 2; + + if (moduleBuilder.initializedData.Length != 0) + { + // .sdata + writer.Headers.FileHeader.NumberOfSections++; + } + + if (resources != null) + { + // .rsrc + writer.Headers.FileHeader.NumberOfSections++; + } + + var text = new SectionHeader(); + text.Name = ".text"; + text.VirtualAddress = code.BaseRVA; + text.VirtualSize = (uint)code.Length; + text.PointerToRawData = code.PointerToRawData; + text.SizeOfRawData = writer.ToFileAlignment((uint)code.Length); + text.Characteristics = SectionHeader.IMAGE_SCN_CNT_CODE | SectionHeader.IMAGE_SCN_MEM_EXECUTE | SectionHeader.IMAGE_SCN_MEM_READ; + + var sdata = new SectionHeader(); + sdata.Name = ".sdata"; + sdata.VirtualAddress = text.VirtualAddress + writer.ToSectionAlignment(text.VirtualSize); + sdata.VirtualSize = (uint)moduleBuilder.initializedData.Length; + sdata.PointerToRawData = text.PointerToRawData + text.SizeOfRawData; + sdata.SizeOfRawData = writer.ToFileAlignment((uint)moduleBuilder.initializedData.Length); + sdata.Characteristics = SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_MEM_WRITE; + + var rsrc = new SectionHeader(); + rsrc.Name = ".rsrc"; + rsrc.VirtualAddress = sdata.VirtualAddress + writer.ToSectionAlignment(sdata.VirtualSize); + rsrc.PointerToRawData = sdata.PointerToRawData + sdata.SizeOfRawData; + rsrc.VirtualSize = resources == null ? 0 : (uint)resources.Length; + rsrc.SizeOfRawData = writer.ToFileAlignment(rsrc.VirtualSize); + rsrc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA; + + if (rsrc.SizeOfRawData != 0) + { + // Resource Directory + writer.Headers.OptionalHeader.DataDirectory[2].VirtualAddress = rsrc.VirtualAddress; + writer.Headers.OptionalHeader.DataDirectory[2].Size = rsrc.VirtualSize; + } + + var reloc = new SectionHeader(); + reloc.Name = ".reloc"; + reloc.VirtualAddress = rsrc.VirtualAddress + writer.ToSectionAlignment(rsrc.VirtualSize); + reloc.VirtualSize = code.PackRelocations(); + reloc.PointerToRawData = rsrc.PointerToRawData + rsrc.SizeOfRawData; + reloc.SizeOfRawData = writer.ToFileAlignment(reloc.VirtualSize); + reloc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_DISCARDABLE; + + if (reloc.SizeOfRawData != 0) + { + // Base Relocation Directory + writer.Headers.OptionalHeader.DataDirectory[5].VirtualAddress = reloc.VirtualAddress; + writer.Headers.OptionalHeader.DataDirectory[5].Size = reloc.VirtualSize; + } + + writer.Headers.OptionalHeader.SizeOfCode = text.SizeOfRawData; + writer.Headers.OptionalHeader.SizeOfInitializedData = sdata.SizeOfRawData + rsrc.SizeOfRawData + reloc.SizeOfRawData; + writer.Headers.OptionalHeader.SizeOfUninitializedData = 0; + writer.Headers.OptionalHeader.SizeOfImage = reloc.VirtualAddress + writer.ToSectionAlignment(reloc.VirtualSize); + writer.Headers.OptionalHeader.SizeOfHeaders = text.PointerToRawData; + writer.Headers.OptionalHeader.BaseOfCode = code.BaseRVA; + writer.Headers.OptionalHeader.BaseOfData = sdata.VirtualAddress; + writer.Headers.OptionalHeader.ImageBase = (ulong)moduleBuilder.__ImageBase; + + if (imageFileMachine == ImageFileMachine.IA64) + { + // apparently for IA64 AddressOfEntryPoint points to the address of the entry point + // (i.e. there is an additional layer of indirection), so we add the offset to the pointer + writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + 0x20; + } + else + { + writer.Headers.OptionalHeader.AddressOfEntryPoint = code.StartupStubRVA + writer.Thumb; + } + + writer.WritePEHeaders(); + writer.WriteSectionHeader(text); + if (sdata.SizeOfRawData != 0) + writer.WriteSectionHeader(sdata); + if (rsrc.SizeOfRawData != 0) + writer.WriteSectionHeader(rsrc); + if (reloc.SizeOfRawData != 0) + writer.WriteSectionHeader(reloc); + + stream.Seek(text.PointerToRawData, SeekOrigin.Begin); + code.Write(mw, sdata.VirtualAddress, out var guidHeapOffset); + + if (sdata.SizeOfRawData != 0) + { + stream.Seek(sdata.PointerToRawData, SeekOrigin.Begin); + mw.Write(moduleBuilder.initializedData); + } + + if (rsrc.SizeOfRawData != 0) + { + stream.Seek(rsrc.PointerToRawData, SeekOrigin.Begin); + resources.Write(mw, rsrc.VirtualAddress); + } + + if (reloc.SizeOfRawData != 0) + { + stream.Seek(reloc.PointerToRawData, SeekOrigin.Begin); + code.WriteRelocations(mw); + } + + // file alignment + stream.SetLength(reloc.PointerToRawData + reloc.SizeOfRawData); + + // if we don't have a guid, generate one based on the contents of the assembly + if (moduleBuilder.universe.Deterministic && moduleBuilder.GetModuleVersionIdOrEmpty() == Guid.Empty) + { + Guid guid = GenerateModuleVersionId(stream); + stream.Position = guidHeapOffset + (moduleVersionIdIndex - 1) * 16; + stream.Write(guid.ToByteArray(), 0, 16); + moduleBuilder.__SetModuleVersionId(guid); + } + + // do the strong naming + if (keyPair != null) + { + StrongName(stream, keyPair, writer.HeaderSize, text.PointerToRawData, code.StrongNameSignatureRVA - text.VirtualAddress + text.PointerToRawData, code.StrongNameSignatureLength); + } + + if (moduleBuilder.symbolWriter != null) + { + moduleBuilder.WriteSymbolTokenMap(); + moduleBuilder.symbolWriter.Close(); + } + } + + private static int ComputeStrongNameSignatureLength(byte[] publicKey) + { + if (publicKey == null) + { + return 0; + } + else if (publicKey.Length == 16) + { + // it must be the ECMA pseudo public key, we don't know the key size of the real key, but currently both Mono and Microsoft use a 1024 bit key size + return 128; + } + else + { + // for the supported strong naming algorithms, the signature size is the same as the key size + // (we have to subtract 32 for the header) + return publicKey.Length - 32; + } + } + + private static void StrongName(Stream stream, StrongNameKeyPair keyPair, uint headerLength, uint textSectionFileOffset, uint strongNameSignatureFileOffset, uint strongNameSignatureLength) + { + byte[] hash; + using (SHA1 sha1 = SHA1.Create()) + { + stream.Seek(0, SeekOrigin.Begin); + Stream skipStream = new SkipStream(stream, strongNameSignatureFileOffset, strongNameSignatureLength); + skipStream = new SkipStream(skipStream, headerLength, textSectionFileOffset - headerLength); + hash = sha1.ComputeHash(skipStream); + } + using (RSACryptoServiceProvider rsa = keyPair.CreateRSA()) + { + byte[] signature = rsa.SignHash(hash, "1.3.14.3.2.26"); + Array.Reverse(signature); + if (signature.Length != strongNameSignatureLength) + { + throw new InvalidOperationException("Signature length mismatch"); + } + stream.Seek(strongNameSignatureFileOffset, SeekOrigin.Begin); + stream.Write(signature, 0, signature.Length); + } + + // compute the PE checksum + stream.Seek(0, SeekOrigin.Begin); + int count = (int)stream.Length / 4; + BinaryReader br = new BinaryReader(stream); + long sum = 0; + for (int i = 0; i < count; i++) + { + sum += br.ReadUInt32(); + int carry = (int)(sum >> 32); + sum &= 0xFFFFFFFFU; + sum += carry; + } + while ((sum >> 16) != 0) + { + sum = (sum & 0xFFFF) + (sum >> 16); + } + sum += stream.Length; + + // write the PE checksum, note that it is always at offset 0xD8 in the file + ByteBuffer bb = new ByteBuffer(4); + bb.Write((int)sum); + stream.Seek(0xD8, SeekOrigin.Begin); + bb.WriteTo(stream); + } + + private static Guid GenerateModuleVersionId(Stream stream) + { + byte[] hash; + using (SHA1 sha1 = SHA1.Create()) + { + stream.Seek(0, SeekOrigin.Begin); + hash = sha1.ComputeHash(stream); + } + byte[] bytes = new byte[16]; + Buffer.BlockCopy(hash, 0, bytes, 0, bytes.Length); + // set GUID type to "version 4" (random) + bytes[7] &= 0x0F; + bytes[7] |= 0x40; + bytes[8] &= 0x3F; + bytes[8] |= 0x80; + return new Guid(bytes); + } + } + + sealed class SkipStream : Stream + { + private readonly Stream stream; + private long skipOffset; + private long skipLength; + + internal SkipStream(Stream stream, long skipOffset, long skipLength) + { + if (skipOffset < 0 || skipLength < 0) + { + throw new ArgumentOutOfRangeException(); + } + this.stream = stream; + this.skipOffset = skipOffset; + this.skipLength = skipLength; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + stream.Dispose(); + } + } + + public override bool CanRead + { + get { return stream.CanRead; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (skipLength != 0 && skipOffset < count) + { + if (skipOffset != 0) + { + count = (int)skipOffset; + } + else + { + // note that we loop forever if the skipped part lies beyond EOF + while (skipLength != 0) + { + // use the output buffer as scratch space + skipLength -= stream.Read(buffer, offset, (int)Math.Min(count, skipLength)); + } + } + } + int totalBytesRead = stream.Read(buffer, offset, count); + skipOffset -= totalBytesRead; + return totalBytesRead; + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } } diff --git a/src/IKVM.Runtime-ref/IKVM.Runtime-ref.csproj b/src/IKVM.Runtime-ref/IKVM.Runtime-ref.csproj index 7eda3e1de4..a8fc6d3c04 100644 --- a/src/IKVM.Runtime-ref/IKVM.Runtime-ref.csproj +++ b/src/IKVM.Runtime-ref/IKVM.Runtime-ref.csproj @@ -1,12 +1,13 @@  - - + + IKVM.Runtime-ref net461;netcoreapp3.1 IKVM.Runtime - $(DefineConstants);EMITTERS;CLASSGC;FIRST_PASS + $(DefineConstants);EMITTERS;FIRST_PASS true + true @@ -14,6 +15,7 @@ + diff --git a/src/IKVM.Runtime/AccessStubConstructorMethodWrapper.cs b/src/IKVM.Runtime/AccessStubConstructorMethodWrapper.cs new file mode 100644 index 0000000000..a459f93fcc --- /dev/null +++ b/src/IKVM.Runtime/AccessStubConstructorMethodWrapper.cs @@ -0,0 +1,76 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace IKVM.Internal +{ + + sealed class AccessStubConstructorMethodWrapper : SmartMethodWrapper + { + + readonly ConstructorInfo stub; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + internal AccessStubConstructorMethodWrapper(TypeWrapper declaringType, string sig, ConstructorInfo core, ConstructorInfo stub, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, StringConstants.INIT, sig, core, PrimitiveTypeWrapper.VOID, parameterTypes, modifiers, flags) + { + this.stub = stub; + } + +#if EMITTERS + + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, stub); + } + + protected override void NewobjImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Newobj, stub); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/AccessStubMethodWrapper.cs b/src/IKVM.Runtime/AccessStubMethodWrapper.cs new file mode 100644 index 0000000000..6f2370b033 --- /dev/null +++ b/src/IKVM.Runtime/AccessStubMethodWrapper.cs @@ -0,0 +1,81 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace IKVM.Internal +{ + + sealed class AccessStubMethodWrapper : SmartMethodWrapper + { + + readonly MethodInfo stubVirtual; + readonly MethodInfo stubNonVirtual; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal AccessStubMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo core, MethodInfo stubVirtual, MethodInfo stubNonVirtual, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, core, returnType, parameterTypes, modifiers, flags) + { + this.stubVirtual = stubVirtual; + this.stubNonVirtual = stubNonVirtual; + } + +#if EMITTERS + + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, stubNonVirtual); + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Callvirt, stubVirtual); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Accessors/Accessor.cs b/src/IKVM.Runtime/Accessors/Accessor.cs new file mode 100644 index 0000000000..44fd8373d4 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Accessor.cs @@ -0,0 +1,123 @@ +using System; +using System.Threading; + +namespace IKVM.Runtime.Accessors +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides a way to access classes at runtime. + /// + internal abstract partial class Accessor + { + + readonly string typeName; + readonly AccessorTypeResolver resolver; + Type type; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public Accessor(AccessorTypeResolver resolver, string typeName) + { + this.resolver = resolver ?? throw new ArgumentNullException(nameof(resolver)); + this.typeName = typeName ?? throw new ArgumentNullException(nameof(typeName)); + } + + /// + /// Gets the type being accessed. + /// + public Type Type => AccessorUtil.LazyGet(ref type, () => Resolve(typeName)); + + /// + /// Resolves the given type name. + /// + /// + /// + protected Type Resolve(string typeName) => typeName != null ? resolver(typeName) : null; + + /// + /// Resolves the given type name. + /// + /// + /// + /// + protected Type Resolve(ref Type type, string typeName) => AccessorUtil.LazyGet(ref type, () => Resolve(typeName)); + + } + + /// + /// Provides a way to access classes at runtime. + /// + internal abstract partial class Accessor : Accessor + { + + /// + /// Initializes a new instance. + /// + /// + /// + public Accessor(AccessorTypeResolver resolver, string typeName) : + base(resolver, typeName) + { + + } + + /// + /// Initializes a field accessor. + /// + /// + /// + /// + protected FieldAccessor GetField(ref FieldAccessor accessor, string name) => FieldAccessor.LazyGet(ref accessor, Type, name); + + /// + /// Initializes a field accessor. + /// + /// + /// + /// + protected FieldAccessor GetField(ref FieldAccessor accessor, string name) => FieldAccessor.LazyGet(ref accessor, Type, name); + + /// + /// Initializes a property accessor. + /// + /// + /// + /// + protected PropertyAccessor GetProperty(ref PropertyAccessor accessor, string name) => PropertyAccessor.LazyGet(ref accessor, Type, name); + + /// + /// Initializes a property accessor. + /// + /// + /// + /// + protected PropertyAccessor GetProperty(ref PropertyAccessor accessor, string name) => PropertyAccessor.LazyGet(ref accessor, Type, name); + + /// + /// Initializes a field accessor. + /// + /// + /// + /// + protected MethodAccessor GetConstructor(ref MethodAccessor accessor, params Type[] parameterTypes) where TDelegate : Delegate => MethodAccessor.LazyGet(ref accessor, Type, ".ctor", typeof(void), parameterTypes); + + /// + /// Initializes a field accessor. + /// + /// + /// + /// + /// + protected MethodAccessor GetMethod(ref MethodAccessor accessor, string name, Type returnType, params Type[] parameterTypes) where TDelegate : Delegate => MethodAccessor.LazyGet(ref accessor, Type, name, returnType, parameterTypes); + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Accessors/AccessorCache.cs b/src/IKVM.Runtime/Accessors/AccessorCache.cs new file mode 100644 index 0000000000..6f537642fb --- /dev/null +++ b/src/IKVM.Runtime/Accessors/AccessorCache.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace IKVM.Runtime.Accessors +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to type accessors. + /// + class AccessorCache + { + + /// + /// Gets the set of accessors for a given assembly. + /// + /// + /// + /// + public static AccessorCache Get(ref AccessorCache cache, Assembly assembly) + { + return AccessorUtil.LazyGet(ref cache, () => new AccessorCache(assembly)); + } + + readonly Assembly assembly; + readonly ConcurrentDictionary cache = new ConcurrentDictionary(); + + /// + /// Initializes a new instance. + /// + /// + /// + public AccessorCache(Assembly assembly) + { + this.assembly = assembly ?? throw new ArgumentNullException(nameof(assembly)); + } + + /// + /// Gets the instance of the specified accessor if not already resolved. + /// + /// + /// + /// + public TAccessor Get(ref TAccessor accessor) + where TAccessor : Accessor + { + return AccessorUtil.LazyGet(ref accessor, () => (TAccessor)cache.GetOrAdd(typeof(TAccessor), Make)); + } + + /// + /// Creates a new accessor instance. + /// + /// + /// + Accessor Make(Type accessorType) + { + return (Accessor)Activator.CreateInstance(accessorType, (AccessorTypeResolver)(t => assembly.GetType(t))); + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/AccessorTypeResolver.cs b/src/IKVM.Runtime/Accessors/AccessorTypeResolver.cs new file mode 100644 index 0000000000..40c5a4a5cf --- /dev/null +++ b/src/IKVM.Runtime/Accessors/AccessorTypeResolver.cs @@ -0,0 +1,8 @@ +using System; + +namespace IKVM.Runtime.Accessors +{ + + internal delegate Type AccessorTypeResolver(string name); + +} diff --git a/src/IKVM.Runtime/Accessors/AccessorUtil.cs b/src/IKVM.Runtime/Accessors/AccessorUtil.cs new file mode 100644 index 0000000000..3949609dc0 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/AccessorUtil.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; + +namespace IKVM.Runtime.Accessors +{ + + static class AccessorUtil + { + + /// + /// Gets the value at the given location or initializes it if null. + /// + /// + /// + /// + public static T LazyGet(ref T location, Func create) + where T : class + { + if (create is null) + throw new ArgumentNullException(nameof(create)); + + if (location == null) + Interlocked.CompareExchange(ref location, create(), null); + + return location; + } + + } + +} diff --git a/src/IKVM.Runtime/Accessors/FieldAccessor.cs b/src/IKVM.Runtime/Accessors/FieldAccessor.cs new file mode 100644 index 0000000000..846734ef9d --- /dev/null +++ b/src/IKVM.Runtime/Accessors/FieldAccessor.cs @@ -0,0 +1,397 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; + +using IKVM.Internal; + +namespace IKVM.Runtime.Accessors +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Base class for accessors of class fields. + /// + internal abstract class FieldAccessor + { + + readonly Type type; + readonly string name; + FieldInfo field; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + protected FieldAccessor(Type type, string name) + { + this.type = type ?? throw new ArgumentNullException(nameof(type)); + this.name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + /// Gets the type which contains the field being accessed. + /// + protected Type Type => type; + + /// + /// Gets the name of the field being accessed. + /// + protected string Name => name; + + /// + /// Gets the field being accessed. + /// + protected FieldInfo Field => AccessorUtil.LazyGet(ref field, () => type.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) ?? throw new InvalidOperationException(); + + } + + /// + /// Provides fast access to a field of a given type. + /// + /// + internal sealed class FieldAccessor : FieldAccessor + { + + /// + /// Gets a for the given field on the given type. + /// + /// + /// + /// + /// + public static FieldAccessor LazyGet(ref FieldAccessor location, Type type, string name) + { + return AccessorUtil.LazyGet(ref location, () => new FieldAccessor(type, name)); + } + + Func getter; + Action setter; + + /// + /// Initializes a new instance. + /// + /// + /// + public FieldAccessor(Type type, string name) : + base(type, name) + { + + } + + /// + /// Gets the getter for the field. + /// + Func Getter => AccessorUtil.LazyGet(ref getter, MakeGetter); + + /// + /// Gets the setter for the field. + /// + Action Setter => AccessorUtil.LazyGet(ref setter, MakeSetter); + + /// + /// Creates a new getter. + /// + /// + /// + Func MakeGetter() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Type.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(TField), Array.Empty()); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldsfld, Field); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Func)dm.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Creates a new setter. + /// + /// + /// + Action MakeSetter() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Type.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(void), new[] { typeof(TField) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Stsfld, Field); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Action)dm.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// Gets the value of the field. + /// + /// + public TField GetValue() => Getter(); + + /// + /// Sets the value of the field. + /// + /// + public void SetValue(TField value) => Setter(value); + + } + + /// + /// Provides fast access to a field of a given type on the given object type. + /// + /// + /// + internal sealed class FieldAccessor : FieldAccessor + { + + delegate TField GetterDelegate(TObject self); + delegate void SetterDelegate(TObject self, TField value); + delegate TField ExchangeDelegate(TObject self, TField value); + delegate TField CompareExchangeDelegate(TObject self, TField value, TField comparand); + + static readonly MethodInfo ExchangeOfInt32 = typeof(Interlocked).GetMethod(nameof(Interlocked.Exchange), new[] { typeof(int).MakeByRefType(), typeof(int) }); + static readonly MethodInfo ExchangeOfInt64 = typeof(Interlocked).GetMethod(nameof(Interlocked.Exchange), new[] { typeof(long).MakeByRefType(), typeof(long) }); + static readonly MethodInfo ExchangeOfSingle = typeof(Interlocked).GetMethod(nameof(Interlocked.Exchange), new[] { typeof(float).MakeByRefType(), typeof(float) }); + static readonly MethodInfo ExchangeOfDouble = typeof(Interlocked).GetMethod(nameof(Interlocked.Exchange), new[] { typeof(double).MakeByRefType(), typeof(double) }); + static readonly MethodInfo ExchangeOfT = typeof(Interlocked).GetMethods().First(i => i.Name == nameof(Interlocked.Exchange) && i.GetGenericArguments().Length == 1); + + static readonly MethodInfo CompareExchangeOfInt32 = typeof(Interlocked).GetMethod(nameof(Interlocked.CompareExchange), new[] { typeof(int).MakeByRefType(), typeof(int), typeof(int) }); + static readonly MethodInfo CompareExchangeOfInt64 = typeof(Interlocked).GetMethod(nameof(Interlocked.CompareExchange), new[] { typeof(long).MakeByRefType(), typeof(long), typeof(long) }); + static readonly MethodInfo CompareExchangeOfSingle = typeof(Interlocked).GetMethod(nameof(Interlocked.CompareExchange), new[] { typeof(float).MakeByRefType(), typeof(float), typeof(float) }); + static readonly MethodInfo CompareExchangeOfDouble = typeof(Interlocked).GetMethod(nameof(Interlocked.CompareExchange), new[] { typeof(double).MakeByRefType(), typeof(double), typeof(double) }); + static readonly MethodInfo CompareExchangeOfT = typeof(Interlocked).GetMethods().First(i => i.Name == nameof(Interlocked.CompareExchange) && i.GetGenericArguments().Length == 1); + + /// + /// Gets a for the given field on the given type. + /// + /// + /// + /// + /// + public static FieldAccessor LazyGet(ref FieldAccessor location, Type type, string name) + { + return AccessorUtil.LazyGet(ref location, () => new FieldAccessor(type, name)); + } + + GetterDelegate getter; + SetterDelegate setter; + ExchangeDelegate exchange; + CompareExchangeDelegate compareExchange; + + /// + /// Initializes a new instance. + /// + /// + /// + public FieldAccessor(Type type, string name) : + base(type, name) + { + + } + + /// + /// Gets the getter for the field. + /// + GetterDelegate Getter => AccessorUtil.LazyGet(ref getter, MakeGetter); + + /// + /// Gets the setter for the field. + /// + SetterDelegate Setter => AccessorUtil.LazyGet(ref setter, MakeSetter); + + /// + /// Gets the exchange operation for the field. + /// + ExchangeDelegate Exchange => AccessorUtil.LazyGet(ref exchange, MakeExchange); + + /// + /// Gets the compare and exchange operation for the field. + /// + CompareExchangeDelegate CompareExchange => AccessorUtil.LazyGet(ref compareExchange, MakeCompareExchange); + + /// + /// Creates a new getter. + /// + /// + GetterDelegate MakeGetter() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Field.DeclaringType.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(TField), new[] { typeof(TObject) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(OpCodes.Ldfld, Field); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (GetterDelegate)dm.CreateDelegate(typeof(GetterDelegate)); +#endif + } + + /// + /// Creates a new setter. + /// + /// + SetterDelegate MakeSetter() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Field.DeclaringType.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(void), new[] { typeof(TObject), typeof(TField) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, Field); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (SetterDelegate)dm.CreateDelegate(typeof(SetterDelegate)); +#endif + } + + /// + /// Creates a new compare and exchange delegate. + /// + /// + ExchangeDelegate MakeExchange() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Field.DeclaringType.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(TField), new[] { typeof(TObject), typeof(TField) }); + var il = CodeEmitter.Create(dm); + + il.EmitLdarg(0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(Field.IsStatic ? OpCodes.Ldsflda: OpCodes.Ldflda, Field); + il.EmitLdarg(1); + + switch (typeof(TField)) + { + case Type t when t == typeof(int): + il.Emit(OpCodes.Call, ExchangeOfInt32); + break; + case Type t when t == typeof(long): + il.Emit(OpCodes.Call, ExchangeOfInt64); + break; + case Type t when t == typeof(float): + il.Emit(OpCodes.Call, ExchangeOfSingle); + break; + case Type t when t == typeof(double): + il.Emit(OpCodes.Call, ExchangeOfDouble); + break; + case Type t when t.IsValueType == false: + il.Emit(OpCodes.Call, ExchangeOfT.MakeGenericMethod(Field.FieldType)); + break; + default: + throw new InternalException("No Interlocked.Exchange implementation for type."); + } + + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (ExchangeDelegate)dm.CreateDelegate(typeof(ExchangeDelegate)); +#endif + } + + /// + /// Creates a new compare and exchange delegate. + /// + /// + CompareExchangeDelegate MakeCompareExchange() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Field.DeclaringType.Name.Replace(".", "_")}__{Field.Name}", Type, false, typeof(TField), new[] { typeof(TObject), typeof(TField), typeof(TField) }); + var il = CodeEmitter.Create(dm); + + il.EmitLdarg(0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(Field.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, Field); + il.EmitLdarg(1); + il.EmitLdarg(2); + + switch (typeof(TField)) + { + case Type t when t == typeof(int): + il.Emit(OpCodes.Call, CompareExchangeOfInt32); + break; + case Type t when t == typeof(long): + il.Emit(OpCodes.Call, CompareExchangeOfInt64); + break; + case Type t when t == typeof(float): + il.Emit(OpCodes.Call, CompareExchangeOfSingle); + break; + case Type t when t == typeof(double): + il.Emit(OpCodes.Call, CompareExchangeOfDouble); + break; + case Type t when t.IsValueType == false: + il.Emit(OpCodes.Call, CompareExchangeOfT.MakeGenericMethod(Field.FieldType)); + break; + default: + throw new InternalException("No Interlocked.CompareExchange implementation for type."); + } + + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (CompareExchangeDelegate)dm.CreateDelegate(typeof(CompareExchangeDelegate)); +#endif + } + + /// + /// Gets the value of the field. + /// + /// + /// + public TField GetValue(TObject self) => Getter(self); + + /// + /// Sets the value of the field. + /// + /// + /// + public void SetValue(TObject self, TField value) => Setter(self, value); + + /// + /// Exchanges the value of the field. + /// + /// + /// + /// + public TField ExchangeValue(TObject self, TField value) => Exchange(self, value); + + /// + /// Compares and exchanges the value of the field. + /// + /// + /// + /// + /// + public TField CompareExchangeValue(TObject self, TField value, TField comparand) => CompareExchange(self, value, comparand); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Ikvm/EnumeratorIteratorAccessor.cs b/src/IKVM.Runtime/Accessors/Ikvm/EnumeratorIteratorAccessor.cs new file mode 100644 index 0000000000..da22e258ba --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Ikvm/EnumeratorIteratorAccessor.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; + +namespace IKVM.Runtime.Accessors.Ikvm.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + class EnumeratorIteratorAccessor : Accessor + { + + MethodAccessor> init; + + /// + /// Initializes a new instance. + /// + /// + public EnumeratorIteratorAccessor(AccessorTypeResolver resolver) : + base(resolver, "ikvm.util.EnumeratorIterator") + { + + } + + /// + /// Gets the value of the 'enumerator' field. + /// + /// + /// + public object Init(IEnumerator enumerator) => GetConstructor(ref init, typeof(IEnumerator)).Invoker(enumerator); + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Accessors/Ikvm/Internal/CallerIDAccessor.cs b/src/IKVM.Runtime/Accessors/Ikvm/Internal/CallerIDAccessor.cs new file mode 100644 index 0000000000..48895b85d9 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Ikvm/Internal/CallerIDAccessor.cs @@ -0,0 +1,36 @@ +using System; + +namespace IKVM.Runtime.Accessors.Ikvm.Internal +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'ikvm.internal.CallerID' type. + /// + internal sealed class CallerIDAccessor : Accessor + { + + MethodAccessor> create; + + /// + /// Initializes a new instance. + /// + /// + public CallerIDAccessor(AccessorTypeResolver resolver) : + base(resolver, "ikvm.internal.CallerID") + { + + } + + /// + /// Invokes the 'create' method. + /// + /// + public object InvokeCreate(RuntimeTypeHandle typeHandle) => GetMethod(ref create, "create", Resolve("ikvm.internal.CallerID"), typeof(RuntimeTypeHandle)).Invoker(typeHandle); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/BufferedInputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/BufferedInputStreamAccessor.cs new file mode 100644 index 0000000000..a982d066f5 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/BufferedInputStreamAccessor.cs @@ -0,0 +1,36 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.BufferedInputStream' type. + /// + internal sealed class BufferedInputStreamAccessor : Accessor + { + + MethodAccessor> init; + + /// + /// Initializes a new instance. + /// + /// + public BufferedInputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.BufferedInputStream") + { + + } + + /// + /// Constructs a new instance. + /// + /// + public object Init(object self) => GetConstructor(ref init, Resolve("java.io.InputStream")).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/BufferedOutputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/BufferedOutputStreamAccessor.cs new file mode 100644 index 0000000000..b79d61649c --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/BufferedOutputStreamAccessor.cs @@ -0,0 +1,36 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.BufferedOutputStream' type. + /// + internal sealed class BufferedOutputStreamAccessor : Accessor + { + + MethodAccessor> init; + + /// + /// Initializes a new instance. + /// + /// + public BufferedOutputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.BufferedOutputStream") + { + + } + + /// + /// Constructs a new instance. + /// + /// + public object Init(object self) => GetConstructor(ref init, Resolve("java.io.OutputStream")).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/FileAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/FileAccessor.cs new file mode 100644 index 0000000000..93d1560715 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/FileAccessor.cs @@ -0,0 +1,45 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.File' type. + /// + internal sealed class FileAccessor : Accessor + { + + MethodAccessor> init; + MethodAccessor> getPath; + + /// + /// Initializes a new instance. + /// + /// + public FileAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.File") + { + + } + + /// + /// Creates a new instance of the object. + /// + /// + /// + public object Init(string path) => GetConstructor(ref init, typeof(string)).Invoker(path); + + /// + /// Invokes the 'getPath' method. + /// + /// + /// + public string InvokeGetPath(object self) => GetMethod(ref getPath, nameof(getPath), typeof(string)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/FileDescriptorAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/FileDescriptorAccessor.cs new file mode 100644 index 0000000000..97cf216eb5 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/FileDescriptorAccessor.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.FileDescriptor' type. + /// + internal sealed class FileDescriptorAccessor : Accessor + { + + Type javaIoFileDescriptor; + + FieldAccessor @in; + FieldAccessor @out; + FieldAccessor @err; + MethodAccessor> fromStream; + MethodAccessor> fromSocket; + + MethodAccessor> init; + PropertyAccessor fd; + FieldAccessor handle; + FieldAccessor task; + FieldAccessor semaphore; + FieldAccessor stream; + FieldAccessor socket; + MethodAccessor> sync; + + /// + /// Initializes a new instance. + /// + /// + public FileDescriptorAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.FileDescriptor") + { + + } + + Type JavaIoFileDescriptor => Resolve(ref javaIoFileDescriptor, "java.io.FileDescriptor"); + + /// + /// Invokes the constructor. + /// + /// + public object Init() => GetConstructor(ref init).Invoker(); + + /// + /// Gets the value for the 'in' field. + /// + public object GetIn() => GetField(ref @in, nameof(@in)).GetValue(); + + /// + /// Gets the value for the 'out' field. + /// + public object GetOut() => GetField(ref @out, nameof(@out)).GetValue(); + + /// + /// Gets the value for the 'err' field. + /// + public object GetErr() => GetField(ref @err, nameof(@err)).GetValue(); + + /// + /// Invokes the 'fromStream' static method. + /// + /// + /// + public object FromStream(Stream stream) => GetMethod(ref fromStream, nameof(fromStream), JavaIoFileDescriptor, typeof(Stream)).Invoker(stream); + + /// + /// Invokes the 'fromSocket' static method. + /// + /// + /// + public object FromSocket(Socket socket) => GetMethod(ref fromSocket, nameof(fromSocket), JavaIoFileDescriptor, typeof(Socket)).Invoker(socket); + + /// + /// Gets the value for the 'fd' property. + /// + public int GetFd(object self) => GetProperty(ref fd, nameof(fd)).GetValue(self); + + /// + /// Gets the value for the 'handle' property. + /// + public long GetHandle(object self) => GetField(ref handle, nameof(handle)).GetValue(self); + + /// + /// Gets the value of the 'task' field. + /// + /// + /// + public Task GetTask(object self) => GetField(ref task, nameof(task)).GetValue(self); + + /// + /// Sets the value of the 'task' field. + /// + /// + /// + /// + public void SetTask(object self, Task value) => GetField(ref task, nameof(task)).SetValue(self, value); + + /// + /// Gets the value of the 'semaphore' field. + /// + /// + /// + /// + public SemaphoreSlim GetSemaphore(object self) => GetField(ref semaphore, nameof(semaphore)).GetValue(self); + + /// + /// Compares and exchanges the value of the 'semaphore' field. + /// + /// + /// + /// + /// + public SemaphoreSlim CompareExchangeSemaphore(object self, SemaphoreSlim value, SemaphoreSlim comparand) => GetField(ref semaphore, nameof(semaphore)).CompareExchangeValue(self, value, comparand); + + /// + /// Gets the value for the 'stream' property. + /// + public Stream GetStream(object self) => GetField(ref stream, nameof(stream)).GetValue(self); + + /// + /// Sets the value for the 'stream' property. + /// + public void SetStream(object self, Stream value) => GetField(ref stream, nameof(stream)).SetValue(self, value); + + /// + /// Gets the value for the 'socket' property. + /// + public Socket GetSocket(object self) => GetField(ref socket, nameof(socket)).GetValue(self); + + /// + /// Sets the value for the 'socket' property. + /// + public void SetSocket(object self, Socket value) => GetField(ref socket, nameof(socket)).SetValue(self, value); + + /// + /// Invokes the 'sync' method. + /// + /// + public void InvokeSync(object self) => GetMethod(ref sync, nameof(sync), typeof(void)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/FileInputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/FileInputStreamAccessor.cs new file mode 100644 index 0000000000..9e768905ee --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/FileInputStreamAccessor.cs @@ -0,0 +1,59 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.FileInputStreamAccessor' type. + /// + internal sealed class FileInputStreamAccessor : Accessor + { + + MethodAccessor> init1; + MethodAccessor> init2; + FieldAccessor fd; + + /// + /// Initializes a new instance. + /// + /// + public FileInputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.FileInputStream") + { + + } + + /// + /// Initializes a new instance of the object. + /// + /// + public object Init1(object fd) => GetConstructor(ref init1, Resolve("java.io.File")).Invoker(fd); + + /// + /// Initializes a new instance of the object. + /// + /// + public object Init2(object fd) => GetConstructor(ref init2, Resolve("java.io.FileDescriptor")).Invoker(fd); + + /// + /// Gets the value of the 'fd' field. + /// + /// + /// + public object GetFd(object self) => GetField(ref fd, nameof(fd)).GetValue(self); + + /// + /// Sets the value of the 'fd' field. + /// + /// + /// + /// + public void SetFd(object self, object value) => GetField(ref fd, nameof(fd)).SetValue(self, value); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/FileOutputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/FileOutputStreamAccessor.cs new file mode 100644 index 0000000000..9705699be9 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/FileOutputStreamAccessor.cs @@ -0,0 +1,59 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.FileOutputStream' type. + /// + internal sealed class FileOutputStreamAccessor : Accessor + { + + MethodAccessor> init1; + MethodAccessor> init2; + FieldAccessor fd; + + /// + /// Initializes a new instance. + /// + /// + public FileOutputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.FileOutputStream") + { + + } + + /// + /// Initializes a new instance of the object. + /// + /// + public object Init1(object fd) => GetConstructor(ref init1, Resolve("java.io.File")).Invoker(fd); + + /// + /// Initializes a new instance of the object. + /// + /// + public object Init2(object fd) => GetConstructor(ref init2, Resolve("java.io.FileDescriptor")).Invoker(fd); + + /// + /// Gets the value of the 'fd' field. + /// + /// + /// + public object GetFd(object self) => GetField(ref fd, nameof(fd)).GetValue(self); + + /// + /// Sets the value of the 'fd' field. + /// + /// + /// + /// + public void SetFd(object self, object value) => GetField(ref fd, nameof(fd)).SetValue(self, value); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/InputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/InputStreamAccessor.cs new file mode 100644 index 0000000000..68320519a5 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/InputStreamAccessor.cs @@ -0,0 +1,36 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.InputStream' type. + /// + internal sealed class InputStreamAccessor : Accessor + { + + MethodAccessor> close; + + /// + /// Initializes a new instance. + /// + /// + public InputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.InputStream") + { + + } + + /// + /// Invokes the 'close' method. + /// + /// + public void InvokeClose(object self) => GetMethod(ref close, nameof(close), typeof(void)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/OutputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/OutputStreamAccessor.cs new file mode 100644 index 0000000000..b8f599d382 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/OutputStreamAccessor.cs @@ -0,0 +1,36 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.OutputStream' type. + /// + internal sealed class OutputStreamAccessor : Accessor + { + + MethodAccessor> close; + + /// + /// Initializes a new instance. + /// + /// + public OutputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.OutputStream") + { + + } + + /// + /// Invokes the 'close' method. + /// + /// + public void InvokeClose(object self) => GetMethod(ref close, nameof(close), typeof(void)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/PrintStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/PrintStreamAccessor.cs new file mode 100644 index 0000000000..1a5ed48666 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/PrintStreamAccessor.cs @@ -0,0 +1,27 @@ +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.PrintStream' type. + /// + internal sealed class PrintStreamAccessor : Accessor + { + + + /// + /// Initializes a new instance. + /// + /// + public PrintStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.PrintStream") + { + + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Io/RandomAccessFileAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Io/RandomAccessFileAccessor.cs new file mode 100644 index 0000000000..c318ecd728 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Io/RandomAccessFileAccessor.cs @@ -0,0 +1,43 @@ +namespace IKVM.Runtime.Accessors.Java.Io +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.RandomAccessFile' type. + /// + internal sealed class RandomAccessFileAccessor : Accessor + { + + FieldAccessor fd; + + /// + /// Initializes a new instance. + /// + /// + public RandomAccessFileAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.io.RandomAccessFile") + { + + } + + /// + /// Gets the value of the 'fd' field. + /// + /// + /// + public object GetFd(object self) => GetField(ref fd, nameof(fd)).GetValue(self); + + /// + /// Sets the value of the 'fd' field. + /// + /// + /// + /// + public void SetFd(object self, object value) => GetField(ref fd, nameof(fd)).SetValue(self, value); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ClassLoaderAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ClassLoaderAccessor.cs new file mode 100644 index 0000000000..2145d35242 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ClassLoaderAccessor.cs @@ -0,0 +1,77 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ClassLoader' type. + /// + internal sealed class ClassLoaderAccessor : Accessor + { + + Type javaLangClass; + Type javaSecurityProtectionDomain; + + FieldAccessor scl; + + FieldAccessor parent; + MethodAccessor> checkPackageAccess; + MethodAccessor> loadClassInternal; + MethodAccessor> checkName; + + /// + /// Initializes a new instance. + /// + /// + public ClassLoaderAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ClassLoader") + { + + } + + Type JavaLangClass => Resolve(ref javaLangClass, "java.lang.Class"); + + Type JavaSecurityProtectionDomain => Resolve(ref javaSecurityProtectionDomain, "java.security.ProtectionDomain"); + + /// + /// Gets the value for the 'scl' field. + /// + public object GetScl() => GetField(ref scl, nameof(scl)).GetValue(); + + /// + /// Sets the value for the 'scl' field. + /// + public void SetScl(object value) => GetField(ref scl, nameof(scl)).SetValue(value); + + /// + /// Gets the value for the 'parent' field. + /// + public object GetParent(object self) => GetField(ref parent, nameof(parent)).GetValue(self); + + /// + /// Sets the value for the 'parent' field. + /// + public void SetParent(object self, object value) => GetField(ref parent, nameof(parent)).SetValue(self, value); + + /// + /// Invokes the 'checkPackageAccess' method. + /// + public void InvokeCheckPackageAccess(object self, object cls, object pd) => GetMethod(ref checkPackageAccess, nameof(checkPackageAccess), typeof(void), JavaLangClass, JavaSecurityProtectionDomain).Invoker(self, cls, pd); + + /// + /// Invokes the 'checkName' method. + /// + public bool InvokeCheckName(object self, string name) => GetMethod(ref checkName, nameof(checkName), typeof(bool), typeof(string)).Invoker(self, name); + + /// + /// Invokes the 'loadClassInternal' method. + /// + public object InvokeLoadClassInternal(object self, string name) => GetMethod(ref loadClassInternal, nameof(loadClassInternal), JavaLangClass, typeof(string)).Invoker(self, name); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ObjectAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ObjectAccessor.cs new file mode 100644 index 0000000000..3c4385c4cf --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ObjectAccessor.cs @@ -0,0 +1,33 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.Object' type. + /// + internal sealed class ObjectAccessor : Accessor + { + + FieldAccessor __init; + + /// + /// Initializes a new instance. + /// + /// + public ObjectAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.Object") + { + + } + + /// + /// Gets the value for the '__' field. + /// + public bool GetInit(object self) => GetField(ref __init, "__").GetValue(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderAccessor.cs new file mode 100644 index 0000000000..292aaab00f --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderAccessor.cs @@ -0,0 +1,26 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ProcessBuilder' type. + /// + internal sealed class ProcessBuilderAccessor : Accessor + { + + /// + /// Initializes a new instance. + /// + /// + public ProcessBuilderAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessBuilder") + { + + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullInputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullInputStreamAccessor.cs new file mode 100644 index 0000000000..47daeb23d4 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullInputStreamAccessor.cs @@ -0,0 +1,34 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ProcessBuilder+NullInputStream' type. + /// + internal sealed class ProcessBuilderNullInputStreamAccessor : Accessor + { + + FieldAccessor INSTANCE; + + /// + /// Initializes a new instance. + /// + /// + public ProcessBuilderNullInputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessBuilder+NullInputStream") + { + + } + + /// + /// Gets the value of the INSTANCE field. + /// + /// + public object GetInstance() => GetField(ref INSTANCE, nameof(INSTANCE)).GetValue(); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullOutputStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullOutputStreamAccessor.cs new file mode 100644 index 0000000000..eb23783c93 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderNullOutputStreamAccessor.cs @@ -0,0 +1,34 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ProcessBuilder+NullOutputStream' type. + /// + internal sealed class ProcessBuilderNullOutputStreamAccessor : Accessor + { + + FieldAccessor INSTANCE; + + /// + /// Initializes a new instance. + /// + /// + public ProcessBuilderNullOutputStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessBuilder+NullOutputStream") + { + + } + + /// + /// Gets the value of the INSTANCE field. + /// + /// + public object GetInstance() => GetField(ref INSTANCE, nameof(INSTANCE)).GetValue(); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectAccessor.cs new file mode 100644 index 0000000000..699999366a --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectAccessor.cs @@ -0,0 +1,60 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ProcessBuilderRedirect' type. + /// + internal sealed class ProcessBuilderRedirectAccessor : Accessor + { + + FieldAccessor PIPE; + FieldAccessor INHERIT; + + MethodAccessor> file; + MethodAccessor> append; + + /// + /// Initializes a new instance. + /// + /// + public ProcessBuilderRedirectAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessBuilder+Redirect") + { + + } + + /// + /// Gets the value of the PIPE field. + /// + /// + public object GetPipe() => GetField(ref PIPE, nameof(PIPE)).GetValue(); + + /// + /// Gets the value of the INHERIT field. + /// + /// + public object GetInherit() => GetField(ref INHERIT, nameof(INHERIT)).GetValue(); + + /// + /// Invokes the 'file' method. + /// + /// + /// + public object InvokeFile(object self) => GetMethod(ref file, nameof(file), Resolve("java.io.File")).Invoker(self); + + /// + /// Invokes the 'append' method. + /// + /// + /// + public bool InvokeAppend(object self) => GetMethod(ref append, nameof(append), typeof(bool)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectTypeAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectTypeAccessor.cs new file mode 100644 index 0000000000..286ac76f47 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessBuilderRedirectTypeAccessor.cs @@ -0,0 +1,62 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ProcessBuilderRedirectType' type. + /// + internal sealed class ProcessBuilderRedirectTypeAccessor : Accessor + { + + FieldAccessor pipe; + FieldAccessor inherit; + FieldAccessor read; + FieldAccessor write; + FieldAccessor append; + + /// + /// Initializes a new instance. + /// + /// + public ProcessBuilderRedirectTypeAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessBuilder+Redirect+Type") + { + + } + + /// + /// Gets the value of the PIPE field. + /// + /// + public object Pipe() => GetField(ref pipe, "PIPE"); + + /// + /// Gets the value of the INHERIT field. + /// + /// + public object Inherit() => GetField(ref inherit, "INHERIT"); + + /// + /// Gets the value of the READ field. + /// + /// + public object Read() => GetField(ref read, "READ"); + + /// + /// Gets the value of the WRITE field. + /// + /// + public object Write() => GetField(ref write, "WRITE"); + + /// + /// Gets the value of the APPEND field. + /// + /// + public object Append() => GetField(ref append, "APPEND"); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ProcessImplAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessImplAccessor.cs new file mode 100644 index 0000000000..df6f9aa7ea --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ProcessImplAccessor.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.io.ProcessImpl' type. + /// + internal sealed class ProcessImplAccessor : Accessor + { + + MethodAccessor> init; + FieldAccessor process; + + /// + /// Initializes a new instance. + /// + /// + public ProcessImplAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ProcessImpl") + { + + } + + public object Init(Process process, object outputStream, object inputStream, object errorStream) => GetConstructor(ref init, typeof(Process), Resolve("java.io.OutputStream"), Resolve("java.io.InputStream"), Resolve("java.io.InputStream")).Invoker(process, outputStream, inputStream, errorStream); + + public Process GetProcess(object self) => GetField(ref process, nameof(process)).GetValue(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/SecurityManagerAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/SecurityManagerAccessor.cs new file mode 100644 index 0000000000..852c82eee9 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/SecurityManagerAccessor.cs @@ -0,0 +1,59 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.SecurityManager' type. + /// + internal sealed class SecurityManagerAccessor : Accessor + { + + MethodAccessor> checkRead; + MethodAccessor> checkWrite; + MethodAccessor> checkDelete; + MethodAccessor> checkExec; + MethodAccessor> checkAccept; + + /// + /// Initializes a new instance. + /// + /// + public SecurityManagerAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.SecurityManager") + { + + } + + /// + /// Invokes the 'checkRead' method. + /// + public void InvokeCheckRead(object self, string path) => GetMethod(ref checkRead, nameof(checkRead), typeof(void), typeof(string)).Invoker(self, path); + + /// + /// Invokes the 'checkWrite' method. + /// + public void InvokeCheckWrite(object self, string path) => GetMethod(ref checkWrite, nameof(checkWrite), typeof(void), typeof(string)).Invoker(self, path); + + /// + /// Invokes the 'checkDelete' method. + /// + public void InvokeCheckDelete(object self, string path) => GetMethod(ref checkDelete, nameof(checkDelete), typeof(void), typeof(string)).Invoker(self, path); + + /// + /// Invokes the 'checkExec' method. + /// + public void InvokeCheckExec(object self, string path) => GetMethod(ref checkExec, nameof(checkExec), typeof(void), typeof(string)).Invoker(self, path); + + /// + /// Invokes the 'checkAccept' method. + /// + public void InvokeCheckAccept(object self, string host, int port) => GetMethod(ref checkAccept, nameof(checkAccept), typeof(void), typeof(string), typeof(int)).Invoker(self, host, port); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/SystemAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/SystemAccessor.cs new file mode 100644 index 0000000000..c5de31a99c --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/SystemAccessor.cs @@ -0,0 +1,88 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.System' type. + /// + internal sealed class SystemAccessor : Accessor + { + + Type javaLangSecurityManager; + + FieldAccessor _in; + FieldAccessor _out; + FieldAccessor _err; + MethodAccessor initializeSystemClass; + MethodAccessor> getProperty; + MethodAccessor> setProperty; + MethodAccessor> getSecurityManager; + + /// + /// Initializes a new instance. + /// + /// + public SystemAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.System") + { + + } + + Type JavaLangSecurityManager => Resolve(ref javaLangSecurityManager, "java.lang.SecurityManager"); + + /// + /// Sets the value of the 'in' field. + /// + /// + /// + public void SetIn(object value) => GetField(ref _in, nameof(_in)).SetValue(value); + + /// + /// Sets the value of the 'out' field. + /// + /// + /// + public void SetOut(object value) => GetField(ref _out, nameof(_out)).SetValue(value); + + /// + /// Sets the value of the 'err' field. + /// + /// + /// + public void SetErr(object value) => GetField(ref _err, nameof(_err)).SetValue(value); + + /// + /// Sets the value of the 'initializeSystemClass' field. + /// + /// + public void InvokeInitializeSystemClass() => GetMethod(ref initializeSystemClass, nameof(initializeSystemClass), typeof(void)).Invoker(); + + /// + /// Sets the value of the 'getProperty' field. + /// + /// + /// + public string InvokeGetProperty(string key) => GetMethod(ref getProperty, nameof(getProperty), typeof(string), typeof(string)).Invoker(key); + + /// + /// Sets the value of the 'setProperty' field. + /// + /// + /// + /// + public string InvokeSetProperty(string key, string value) => GetMethod(ref setProperty, nameof(setProperty), typeof(string), typeof(string), typeof(string)).Invoker(key, value); + + /// + /// Invokes the 'getSecurityManager' method. + /// + /// + public object InvokeGetSecurityManager() => GetMethod(ref getSecurityManager, nameof(getSecurityManager), JavaLangSecurityManager).Invoker(); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ThreadAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ThreadAccessor.cs new file mode 100644 index 0000000000..2d6f2882e8 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ThreadAccessor.cs @@ -0,0 +1,92 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.Thread' type. + /// + internal sealed class ThreadAccessor : Accessor + { + + Type javaLangThread; + Type javaLangThreadGroup; + + FieldAccessor current; + MethodAccessor> currentThread; + MethodAccessor> interrupted; + + MethodAccessor> init; + MethodAccessor> isDaemon; + MethodAccessor> isInterrupted; + MethodAccessor> die; + MethodAccessor> getThreadGroup; + + /// + /// Initializes a new instance. + /// + /// + public ThreadAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.Thread") + { + + } + + Type JavaLangThread => Resolve(ref javaLangThread, "java.lang.Thread"); + + Type JavaLangThreadGroup => Resolve(ref javaLangThreadGroup, "java.lang.ThreadGroup"); + + /// + /// Gets the value of the 'current' field. + /// + /// + public object GetCurrent() => GetField(ref current, nameof(current)).GetValue(); + + /// + /// Invokes the 'currentThread' method. + /// + public object InvokeCurrentThread() => GetMethod(ref currentThread, nameof(currentThread), JavaLangThread).Invoker(); + + /// + /// Invokes the 'interrupted' method. + /// + /// + public bool InvokeIsInterrupted(object self) => GetMethod(ref isInterrupted, nameof(isInterrupted), typeof(bool)).Invoker(self); + + /// + /// Invokes the constructor. + /// + /// + /// + public object Init(object threadGroup) => GetConstructor(ref init, JavaLangThreadGroup).Invoker(threadGroup); + + /// + /// Invokes the 'isDaemon' method. + /// + /// + /// + public bool InvokeIsDaemon(object self) => GetMethod(ref isDaemon, nameof(isDaemon), typeof(bool)).Invoker(self); + + /// + /// Invokes the 'die' method. + /// + public void InvokeDie(object self) => GetMethod(ref die, nameof(die), typeof(void)).Invoker(self); + + /// + /// Invokes the 'getThreadGroup' method. + /// + public object InvokeGetThreadGroup(object self) => GetMethod(ref getThreadGroup, nameof(getThreadGroup), JavaLangThreadGroup).Invoker(self); + + /// + /// Invokes the 'interrupted' method. + /// + /// + public bool InvokeInterrupted() => GetMethod(ref interrupted, nameof(interrupted), typeof(bool)).Invoker(); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Lang/ThreadGroupAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Lang/ThreadGroupAccessor.cs new file mode 100644 index 0000000000..1c2590011f --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Lang/ThreadGroupAccessor.cs @@ -0,0 +1,67 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.lang.ThreadGroup' type. + /// + internal sealed class ThreadGroupAccessor : Accessor + { + + Type javaLangVoid; + Type javaLangThreadGroup; + Type javaLangThread; + Type javaLangThrowable; + + MethodAccessor> init1; + MethodAccessor> init2; + MethodAccessor> init3; + MethodAccessor> uncaughtException; + + /// + /// Initializes a new instance. + /// + /// + public ThreadGroupAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.lang.ThreadGroup") + { + + } + + Type JavaLangVoid => Resolve(ref javaLangVoid, "java.lang.Void"); + + Type JavaLangThread => Resolve(ref javaLangThread, "java.lang.Thread"); + + Type JavaLangThreadGroup => Resolve(ref javaLangThreadGroup, "java.lang.ThreadGroup"); + + /// + /// Invokes the constructor. + /// + /// + public object Init(object unused, object parent, string name) => GetConstructor(ref init1, JavaLangVoid, JavaLangThreadGroup, typeof(string)).Invoker(unused, parent, name); + + /// + /// Invokes the constructor. + /// + /// + public object Init(object parent, string name) => GetConstructor(ref init2, JavaLangThreadGroup, typeof(string)).Invoker(parent, name); + + /// + /// Invokes the constructor. + /// + /// + public object Init() => GetConstructor(ref init3).Invoker(); + + /// + /// Invokes the 'uncaughtException' method. + /// + public void InvokeUncaughtException(object self, object t, object e) => GetMethod(ref uncaughtException, nameof(uncaughtException), typeof(void), JavaLangThread, typeof(Exception)).Invoker(self, t, e); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Nio/BufferAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Nio/BufferAccessor.cs new file mode 100644 index 0000000000..fb0aecc191 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Nio/BufferAccessor.cs @@ -0,0 +1,51 @@ +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.nio.Buffer' type. + /// + internal sealed class BufferAccessor : Accessor + { + + FieldAccessor address; + FieldAccessor capacity; + FieldAccessor position; + FieldAccessor limit; + + /// + /// Initializes a new instance. + /// + /// + public BufferAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.nio.Buffer") + { + + } + + /// + /// Gets the value of the 'address' field. + /// + public long GetAddress(object self) => GetField(ref address, nameof(address)).GetValue(self); + + /// + /// Gets the value of the 'address' field. + /// + public int GetCapacity(object self) => GetField(ref capacity, nameof(capacity)).GetValue(self); + + /// + /// Gets the value of the 'address' field. + /// + public int GetPosition(object self) => GetField(ref position, nameof(position)).GetValue(self); + + /// + /// Gets the value of the 'address' field. + /// + public int GetLimit(object self) => GetField(ref limit, nameof(limit)).GetValue(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Nio/DirectoryStreamFilterAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Nio/DirectoryStreamFilterAccessor.cs new file mode 100644 index 0000000000..b2e446efe8 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Nio/DirectoryStreamFilterAccessor.cs @@ -0,0 +1,35 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Nio.File +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.nio.Buffer' type. + /// + internal sealed class DirectoryStreamFilterAccessor : Accessor + { + + MethodAccessor> accept; + + /// + /// Initializes a new instance. + /// + /// + public DirectoryStreamFilterAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.nio.file.DirectoryStream+Filter") + { + + } + + /// + /// Invokes the 'accept' method. + /// + public bool InvokeAccept(object self, object entry) => GetMethod(ref accept, nameof(accept), typeof(bool), typeof(object)).Invoker(self, entry); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Nio/File/Attribute/FileTimeAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Nio/File/Attribute/FileTimeAccessor.cs new file mode 100644 index 0000000000..41aefc3eb8 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Nio/File/Attribute/FileTimeAccessor.cs @@ -0,0 +1,47 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.nio.file.attribute.FileTime' type. + /// + internal sealed class FileTimeAccessor : Accessor + { + + Type javaNioFileAttributeFileTime; + + MethodAccessor> fromMillis; + MethodAccessor> toMillis; + + /// + /// Initializes a new instance. + /// + /// + public FileTimeAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.nio.file.attribute.FileTime") + { + + } + + Type JavaNioFileAttributeFileTime => Resolve(ref javaNioFileAttributeFileTime, "java.nio.file.attribute.FileTime"); + + /// + /// Invokes the 'fromMillis' method. + /// + public object InvokeFromMillis(long value) => GetMethod(ref fromMillis, nameof(fromMillis), JavaNioFileAttributeFileTime, typeof(long)).Invoker(value); + + /// + /// Invokes the 'toMillis' method. + /// + /// + /// + public long InvokeToMillis(object self) => GetMethod(ref toMillis, nameof(toMillis), typeof(long)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Security/AccessControllerAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Security/AccessControllerAccessor.cs new file mode 100644 index 0000000000..12637ec7f3 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Security/AccessControllerAccessor.cs @@ -0,0 +1,63 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Lang +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.security.AccessController' type. + /// + internal sealed class AccessControllerAccessor : Accessor + { + + Type ikvmInternalCallerID; + Type javaSecurityPrivilegedAction; + Type javaSecurityAccessControlContext; + + MethodAccessor> doPrivileged; + MethodAccessor> doPrivileged2; + MethodAccessor> doPrivileged3; + + /// + /// Initializes a new instance. + /// + /// + public AccessControllerAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.security.AccessController") + { + + } + + Type IkvmInternalCallerID => Resolve(ref ikvmInternalCallerID, "ikvm.internal.CallerID"); + + Type JavaSecurityPrivilegedAction => Resolve(ref javaSecurityPrivilegedAction, "java.security.PrivilegedAction"); + + Type JavaSecurityAccessControlContext => Resolve(ref javaSecurityAccessControlContext, "java.security.AccessControlContext"); + + /// + /// Invokes the 'doPrivileged' method. + /// + /// + /// + public object InvokeDoPrivileged(object action) => GetMethod(ref doPrivileged, nameof(doPrivileged), typeof(object), JavaSecurityPrivilegedAction).Invoker(action); + + /// + /// Invokes the 'doPrivileged' method. + /// + /// + /// + public object InvokeDoPrivileged(object action, object accessControlContext) => GetMethod(ref doPrivileged2, "doPrivileged", typeof(object), JavaSecurityPrivilegedAction, JavaSecurityAccessControlContext).Invoker(action, accessControlContext); + + /// + /// Invokes the 'doPrivileged' method. + /// + /// + /// + public object InvokeDoPrivileged(object action, object accessControlContext, object callerID) => GetMethod(ref doPrivileged3, "doPrivileged", typeof(object), JavaSecurityPrivilegedAction, JavaSecurityAccessControlContext, IkvmInternalCallerID).Invoker(action, accessControlContext, callerID); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Util/IteratorAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Util/IteratorAccessor.cs new file mode 100644 index 0000000000..0febb644a0 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Util/IteratorAccessor.cs @@ -0,0 +1,45 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.util.Iterator' type. + /// + internal sealed class IteratorAccessor : Accessor + { + + MethodAccessor> hasNext; + MethodAccessor> next; + + /// + /// Initializes a new instance. + /// + /// + public IteratorAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.util.Iterator") + { + + } + + /// + /// Invokes the 'hasNext' method. + /// + /// + /// + public bool InvokeHasNext(object self) => GetMethod(ref hasNext, nameof(hasNext), typeof(bool)).Invoker(self); + + /// + /// Invokes the 'next' method. + /// + /// + /// + public object InvokeNext(object self) => GetMethod(ref next, nameof(next), typeof(object)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Util/MapAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Util/MapAccessor.cs new file mode 100644 index 0000000000..7bed8ae5ee --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Util/MapAccessor.cs @@ -0,0 +1,37 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.util.Map' type. + /// + internal sealed class MapAccessor : Accessor + { + + MethodAccessor> entrySet; + + /// + /// Initializes a new instance. + /// + /// + public MapAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.util.Map") + { + + } + + /// + /// Invokes the 'entrySet' method. + /// + /// + /// + public object InvokeEntrySet(object self) => GetMethod(ref entrySet, nameof(entrySet), Resolve("java.util.Set")).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Util/MapEntryAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Util/MapEntryAccessor.cs new file mode 100644 index 0000000000..05fa11cf1c --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Util/MapEntryAccessor.cs @@ -0,0 +1,44 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.util.Map$Entry' type. + /// + internal sealed class MapEntryAccessor : Accessor + { + + MethodAccessor> getKey; + MethodAccessor> getValue; + + /// Initializes a new instance. + /// + /// + public MapEntryAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.util.Map+Entry") + { + + } + + /// + /// Invokes the 'getKey' method. + /// + /// + /// + public object InvokeGetKey(object self) => GetMethod(ref getKey, nameof(getKey), typeof(object)).Invoker(self); + + /// + /// Invokes the 'getValue' method. + /// + /// + /// + public object InvokeGetValue(object self) => GetMethod(ref getValue, nameof(getValue), typeof(object)).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Util/PropertiesAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Util/PropertiesAccessor.cs new file mode 100644 index 0000000000..7e4bfcbe98 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Util/PropertiesAccessor.cs @@ -0,0 +1,48 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.util.Properties' type. + /// + internal sealed class PropertiesAccessor : Accessor + { + + MethodAccessor> getProperty; + MethodAccessor> setProperty; + + /// + /// Initializes a new instance. + /// + /// + public PropertiesAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.util.Properties") + { + + } + + /// + /// Sets the value of the 'getProperty' field. + /// + /// + /// + /// + public object InvokeGetProperty(object self, string key) => GetMethod(ref getProperty, nameof(getProperty), typeof(string), typeof(string)).Invoker(self, key); + + /// + /// Sets the value of the 'setProperty' field. + /// + /// + /// + /// + /// + public object InvokeSetProperty(object self, string key, string value) => GetMethod(ref setProperty, nameof(setProperty), typeof(object), typeof(string), typeof(string)).Invoker(self, key, value); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Java/Util/SetAccessor.cs b/src/IKVM.Runtime/Accessors/Java/Util/SetAccessor.cs new file mode 100644 index 0000000000..4dae2495dc --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Java/Util/SetAccessor.cs @@ -0,0 +1,37 @@ +using System; + +namespace IKVM.Runtime.Accessors.Java.Util +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'java.util.Set' type. + /// + internal sealed class SetAccessor : Accessor + { + + MethodAccessor> iterator; + + /// + /// Initializes a new instance. + /// + /// + public SetAccessor(AccessorTypeResolver resolver) : + base(resolver, "java.util.Set") + { + + } + + /// + /// Invokes the 'iterator' method. + /// + /// + /// + public object InvokeIterator(object self) => GetMethod(ref iterator, nameof(iterator), Resolve("java.util.Iterator")).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/MethodAccessor.cs b/src/IKVM.Runtime/Accessors/MethodAccessor.cs new file mode 100644 index 0000000000..938437443e --- /dev/null +++ b/src/IKVM.Runtime/Accessors/MethodAccessor.cs @@ -0,0 +1,228 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +using IKVM.Internal; + +namespace IKVM.Runtime.Accessors +{ + + /// + /// Base class for accessors of class methods. + /// + internal abstract partial class MethodAccessor + { + + readonly Type type; + readonly string name; + readonly Type returnType; + readonly Type[] parameterTypes; + MethodBase method; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + protected MethodAccessor(Type type, string name, Type returnType, Type[] parameterTypes) + { + + this.type = type ?? throw new ArgumentNullException(nameof(type)); + this.name = name ?? throw new ArgumentNullException(nameof(name)); + this.returnType = returnType ?? throw new ArgumentNullException(nameof(returnType)); + this.parameterTypes = parameterTypes ?? throw new ArgumentNullException(nameof(parameterTypes)); + } + + /// + /// Gets the type which contains the method being accessed. + /// + protected Type Type => type; + + /// + /// Gets the name of the method being accessed. + /// + protected string Name => name; + + /// + /// Gets the return type of the method being accessed. + /// + protected Type ReturnType => returnType; + + /// + /// Gets the paremeter types of the method being accessed. + /// + protected Type[] ParameterTypes => parameterTypes; + + /// + /// Gets the method being accessed. + /// + protected MethodBase Method => AccessorUtil.LazyGet(ref method, FindMethod) ?? throw new InternalException($"Could not locate method {Name}."); + + /// + /// Finds the appropriate method. + /// + /// + MethodBase FindMethod() => type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance) + .OfType() + .Where(i => i.Name == Name) + .Where(i => i.IsConstructor && ReturnType == typeof(void) || i is MethodInfo m && m.ReturnType == ReturnType) + .Where(i => i.GetParameters().Select(i => i.ParameterType).SequenceEqual(ParameterTypes)) + .FirstOrDefault(); + + } + + /// + /// Base class for accessors of class methods. + /// + internal sealed class MethodAccessor : MethodAccessor + where TDelegate : Delegate + { + + public static MethodAccessor LazyGet(ref MethodAccessor location, Type type, string name, Type returnType, params Type[] parameterTypes) + { + return AccessorUtil.LazyGet(ref location, () => new MethodAccessor(type, name, returnType, parameterTypes)); + } + + TDelegate invoker; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + public MethodAccessor(Type type, string name, Type returnType, Type[] parameters) : + base(type, name, returnType, parameters) + { + + } + + /// + /// Gets the setter for the field. + /// + public TDelegate Invoker => AccessorUtil.LazyGet(ref invoker, MakeInvoker); + + /// + /// Creates a new invoker. + /// + /// + TDelegate MakeInvoker() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + // validate return type + var delegateReturnType = GetDelegateReturnType(typeof(TDelegate)); + if (Method.IsConstructor) + { + if (delegateReturnType == typeof(void)) + throw new InternalException("Delegate has no return type for constructor."); + } + else if (Method is MethodInfo mi) + { + if (delegateReturnType == typeof(void) && mi.ReturnType != typeof(void)) + throw new InternalException("Delegate has no return type for method with return type."); + if (delegateReturnType != typeof(void) && mi.ReturnType == typeof(void)) + throw new InternalException("Delegate has return type for method without return type."); + } + + var parameters = Method.GetParameters(); + + // validate parameter counts + var delegateParameterTypes = GetDelegateParameterTypes(typeof(TDelegate)); + if ((Method.IsConstructor || Method.IsStatic) && delegateParameterTypes.Length != parameters.Length) + throw new InternalException("Delegate has wrong number of parameters for constructor or static method."); + else if (Method.IsConstructor == false && Method.IsStatic == false && delegateParameterTypes.Length != parameters.Length + 1) + throw new InternalException("Delegate has wrong number of parameters for instance method."); + + // generate new dynamic method + var dm = DynamicMethodUtil.Create($"____{Type.Name.Replace(".", "_")}__{Method.Name}", Type, false, delegateReturnType, delegateParameterTypes); + var il = CodeEmitter.Create(dm); + + // advance through each argument + var n = 0; + + // load first argument, which is the instance if non-static + if (Method.IsStatic == false && Method.IsConstructor == false) + { + il.EmitLdarg(n++); + if (delegateParameterTypes[0] != Type) + il.EmitCastclass(Type); + } + + // emit conversion code for the remainder of the arguments + for (var i = 0; i < parameters.Length; i++) + { + var delegateParameterType = delegateParameterTypes[n]; + il.EmitLdarg(n++); + if (parameters[i].ParameterType != delegateParameterType) + il.EmitCastclass(parameters[i].ParameterType); + } + + if (Method.IsConstructor) + il.Emit(OpCodes.Newobj, Method); + else if (Method.IsStatic || Method.IsVirtual == false) + il.Emit(OpCodes.Call, Method); + else + il.Emit(OpCodes.Callvirt, Method); + + // convert to delegate value + if (delegateReturnType != typeof(void)) + if (Method.IsConstructor && delegateReturnType != Type || Method.IsConstructor == false && delegateReturnType != ((MethodInfo)Method).ReturnType) + il.EmitCastclass(delegateReturnType); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (TDelegate)dm.CreateDelegate(typeof(TDelegate)); +#endif + } + + /// + /// Gets the parameter types for a delegate. + /// + /// + /// + /// + Type[] GetDelegateParameterTypes(Type d) + { + if (d.BaseType != typeof(MulticastDelegate)) + throw new ArgumentException("Not a delegate.", nameof(d)); + + var invoke = d.GetMethod("Invoke"); + if (invoke == null) + throw new ArgumentException("Not a delegate.", nameof(d)); + + var parameters = invoke.GetParameters(); + var typeParameters = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + typeParameters[i] = parameters[i].ParameterType; + + return typeParameters; + } + + /// + /// Gets the return type for a delegate. + /// + /// + /// + /// + Type GetDelegateReturnType(Type d) + { + if (d.BaseType != typeof(MulticastDelegate)) + throw new ArgumentException("Not a delegate.", nameof(d)); + + var invoke = d.GetMethod("Invoke"); + if (invoke == null) + throw new ArgumentException("Not a delegate.", nameof(d)); + + return invoke.ReturnType; + } + + } + +} diff --git a/src/IKVM.Runtime/Accessors/PropertyAccessor.cs b/src/IKVM.Runtime/Accessors/PropertyAccessor.cs new file mode 100644 index 0000000000..5cc554b8bb --- /dev/null +++ b/src/IKVM.Runtime/Accessors/PropertyAccessor.cs @@ -0,0 +1,265 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +using IKVM.Internal; + +namespace IKVM.Runtime.Accessors +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Base class for accessors of class properties. + /// + internal abstract class PropertyAccessor + { + + readonly Type type; + readonly string name; + PropertyInfo property; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + protected PropertyAccessor(Type type, string name) + { + this.type = type ?? throw new ArgumentNullException(nameof(type)); + this.name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + /// Gets the type which contains the property being accessed. + /// + protected Type Type => type; + + /// + /// Gets the name of the property being accessed. + /// + protected string Name => name; + + /// + /// Gets the property being accessed. + /// + protected PropertyInfo Property => AccessorUtil.LazyGet(ref property, () => type.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) ?? throw new InvalidOperationException(); + + } + + /// + /// Provides fast access to a property of a given type. + /// + /// + internal sealed class PropertyAccessor : PropertyAccessor + { + + /// + /// Gets a for the given property on the given type. + /// + /// + /// + /// + /// + /// + public static PropertyAccessor LazyGet(ref PropertyAccessor location, Type type, string name) + { + return AccessorUtil.LazyGet(ref location, () => new PropertyAccessor(type, name)); + } + + Func getter; + Action setter; + + /// + /// Initializes a new instance. + /// + /// + /// + public PropertyAccessor(Type type, string name) : + base(type, name) + { + + } + + /// + /// Gets the getter for the property. + /// + Func Getter => AccessorUtil.LazyGet(ref getter, MakeGetter); + + /// + /// Gets the setter for the property. + /// + Action Setter => AccessorUtil.LazyGet(ref setter, MakeSetter); + + /// + /// Creates a new getter. + /// + /// + /// + Func MakeGetter() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Type.Name.Replace(".", "_")}__{Property.Name}", Type, false, typeof(TProperty), Array.Empty()); + var il = CodeEmitter.Create(dm); + + il.Emit(Property.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, Property.GetMethod); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Func)dm.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Creates a new setter. + /// + /// + /// + Action MakeSetter() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var dm = DynamicMethodUtil.Create($"____{Type.Name.Replace(".", "_")}__{Property.Name}", Type, false, typeof(void), new[] { typeof(TProperty) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(Property.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, Property.SetMethod); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Action)dm.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// Gets the value of the property. + /// + /// + public TProperty GetValue() => Getter(); + + /// + /// Sets the value of the property. + /// + /// + public void SetValue(TProperty value) => Setter(value); + + } + + /// + /// Provides fast access to a property of a given type on the given object type. + /// + /// + /// + internal sealed class PropertyAccessor : PropertyAccessor + { + + /// + /// Gets a for the given property on the given type. + /// + /// + /// + /// + /// + public static PropertyAccessor LazyGet(ref PropertyAccessor location, Type type, string name) + { + return AccessorUtil.LazyGet(ref location, () => new PropertyAccessor(type, name)); + } + + Func getter; + Action setter; + + /// + /// Initializes a new instance. + /// + /// + /// + public PropertyAccessor(Type type, string name) : + base(type, name) + { + + } + + /// + /// Gets the getter for the property. + /// + Func Getter => AccessorUtil.LazyGet(ref getter, MakeGetter); + + /// + /// Gets the setter for the property. + /// + Action Setter => AccessorUtil.LazyGet(ref setter, MakeSetter); + + /// + /// Creates a new getter. + /// + /// + Func MakeGetter() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + if (Property.CanRead == false) + throw new InternalException($"Property {Property.Name} cannot be read."); + + var dm = DynamicMethodUtil.Create($"____{Property.DeclaringType.Name.Replace(".", "_")}__{Property.Name}", Type, false, typeof(TProperty), new[] { typeof(TObject) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(Property.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, Property.GetMethod); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Func)dm.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Creates a new setter. + /// + /// + Action MakeSetter() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + if (Property.CanWrite == false) + throw new InternalException($"Property {Property.Name} cannot be written."); + + var dm = DynamicMethodUtil.Create($"____{Property.DeclaringType.Name.Replace(".", "_")}__{Property.Name}", Type, false, typeof(void), new[] { typeof(TObject), typeof(TProperty) }); + var il = CodeEmitter.Create(dm); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, Type); + il.Emit(OpCodes.Ldarg_1); + il.Emit(Property.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, Property.SetMethod); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + return (Action)dm.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// Gets the value of the property. + /// + /// + /// + public TProperty GetValue(TObject self) => Getter(self); + + /// + /// Sets the value of the property. + /// + /// + /// + public void SetValue(TObject self, TProperty value) => Setter(self, value); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DatagramChannelImplAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DatagramChannelImplAccessor.cs new file mode 100644 index 0000000000..bdf94bbb73 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DatagramChannelImplAccessor.cs @@ -0,0 +1,51 @@ +using System; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.ch.DatagramChannelImpl' type. + /// + internal sealed class DatagramChannelImplAccessor : Accessor + { + + Type javaNetSocketAddress; + + FieldAccessor sender; + MethodAccessor> remoteAddress; + + /// + /// Initializes a new instance. + /// + /// + public DatagramChannelImplAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.ch.DatagramChannelImpl") + { + + } + + Type JavaNetSocketAddress => Resolve(ref javaNetSocketAddress, "java.net.SocketAddress"); + + /// + /// Gets the value for the 'sender' field. + /// + public object GetSender(object self) => GetField(ref sender, nameof(sender)).GetValue(self); + + /// + /// Sets the value for the 'sender' field. + /// + public void SetSender(object self, object value) => GetField(ref sender, nameof(sender)).SetValue(self, value); + + /// + /// Invokes the 'remoteAddress' function. + /// + /// + public object InvokeRemoteAddress(object self) => GetMethod(ref remoteAddress, nameof(remoteAddress), JavaNetSocketAddress).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousServerSocketChannelImplAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousServerSocketChannelImplAccessor.cs new file mode 100644 index 0000000000..3b84d680e3 --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousServerSocketChannelImplAccessor.cs @@ -0,0 +1,42 @@ +using System; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl' type. + /// + internal sealed class DotNetAsynchronousServerSocketChannelImplAccessor : Accessor + { + + Type sunNioChAsynchronousChannelGroupImpl; + + MethodAccessor> begin; + MethodAccessor> end; + MethodAccessor> group; + + /// + /// Initializes a new instance. + /// + /// + public DotNetAsynchronousServerSocketChannelImplAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl") + { + + } + + Type SunNioChAsynchronousChannelGroupImpl => Resolve(ref sunNioChAsynchronousChannelGroupImpl, "sun.nio.ch.AsynchronousChannelGroupImpl"); + + public void InvokeBegin(object self) => GetMethod(ref begin, nameof(begin), typeof(void)).Invoker(self); + + public void InvokeEnd(object self) => GetMethod(ref end, nameof(end), typeof(void)).Invoker(self); + + public object InvokeGroup(object self) => GetMethod(ref group, nameof(group), SunNioChAsynchronousChannelGroupImpl).Invoker(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousSocketChannelImplAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousSocketChannelImplAccessor.cs new file mode 100644 index 0000000000..c085c16e1e --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/DotNetAsynchronousSocketChannelImplAccessor.cs @@ -0,0 +1,71 @@ +using System; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.ch.DotNetAsynchronousSocketChannelImpl' type. + /// + internal sealed class DotNetAsynchronousSocketChannelImplAccessor : Accessor + { + + Type javaIoFileDescriptor; + Type javaNetSocketAddress; + Type sunNioChDotNetAsynchronousChannelGroup; + + MethodAccessor> init1; + MethodAccessor> init2; + MethodAccessor> init3; + FieldAccessor fd; + MethodAccessor> begin; + MethodAccessor> end; + MethodAccessor> enableReading; + MethodAccessor> enableWriting; + MethodAccessor> enableReading2; + MethodAccessor> enableWriting2; + + /// + /// Initializes a new instance. + /// + /// + public DotNetAsynchronousSocketChannelImplAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.ch.DotNetAsynchronousSocketChannelImpl") + { + + } + + Type JavaIoFileDescriptor => Resolve(ref javaIoFileDescriptor, "java.io.FileDescriptor"); + + Type SunNioChDotNetAsynchronousChannelGroup => Resolve(ref sunNioChDotNetAsynchronousChannelGroup, "sun.nio.ch.DotNetAsynchronousChannelGroup"); + + Type JavaNetInetSocketAddress => Resolve(ref javaNetSocketAddress, "java.net.InetSocketAddress"); + + public object Init(object group) => GetConstructor(ref init1, SunNioChDotNetAsynchronousChannelGroup).Invoker(group); + + public object Init(object group, bool failIfGroupShutdown) => GetConstructor(ref init2, SunNioChDotNetAsynchronousChannelGroup, typeof(bool)).Invoker(group, failIfGroupShutdown); + + public object Init(object group, object fd, object remote) => GetConstructor(ref init3, SunNioChDotNetAsynchronousChannelGroup, JavaIoFileDescriptor, JavaNetInetSocketAddress).Invoker(group, fd, remote); + + public object GetFd(object self) => GetField(ref fd, nameof(fd)).GetValue(self); + + public void SetFd(object self, object value) => GetField(ref fd, nameof(fd)).SetValue(self, value); + + public void InvokeBegin(object self) => GetMethod(ref begin, nameof(begin), typeof(void)).Invoker(self); + + public void InvokeEnd(object self) => GetMethod(ref end, nameof(end), typeof(void)).Invoker(self); + + public void InvokeEnableReading(object self) => GetMethod(ref enableReading, "enableReading", typeof(void)).Invoker(self); + + public void InvokeEnableWriting(object self) => GetMethod(ref enableWriting, "enableWriting", typeof(void)).Invoker(self); + + public void InvokeEnableReading(object self, bool killed) => GetMethod(ref enableReading2, "enableReading", typeof(void), typeof(bool)).Invoker(self, killed); + + public void InvokeEnableWriting(object self, bool killed) => GetMethod(ref enableWriting2, "enableWriting", typeof(void), typeof(bool)).Invoker(self, killed); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/FileChannelImplAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/FileChannelImplAccessor.cs new file mode 100644 index 0000000000..2b8d72c19e --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Ch/FileChannelImplAccessor.cs @@ -0,0 +1,38 @@ +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.ch.FileChannelImpl' type. + /// + internal sealed class FileChannelImplAccessor : Accessor + { + + FieldAccessor fd; + + /// + /// Initializes a new instance. + /// + /// + public FileChannelImplAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.ch.FileChannelImpl") + { + + } + + /// + /// Gets the value for the 'fd' field. + /// + public object GetFd(object self) => GetField(ref fd, nameof(fd)).GetValue(self); + + /// + /// Sets the value for the 'fd' field. + /// + public void SetFd(object self, object value) => GetField(ref fd, nameof(fd)).SetValue(self, value); + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetBasicFileAttributeViewAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetBasicFileAttributeViewAccessor.cs new file mode 100644 index 0000000000..da072da1df --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetBasicFileAttributeViewAccessor.cs @@ -0,0 +1,33 @@ +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.fs.DotNetBasicFileAttributeView' type. + /// + internal sealed class DotNetBasicFileAttributeViewAccessor : Accessor + { + + FieldAccessor path; + + /// + /// Initializes a new instance. + /// + /// + public DotNetBasicFileAttributeViewAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.fs.DotNetBasicFileAttributeView") + { + + } + + /// + /// Initializes a new instance. + /// + public string GetPath(object self) => GetField(ref path, nameof(path)).GetValue(self); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDirectoryStreamAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDirectoryStreamAccessor.cs new file mode 100644 index 0000000000..eb0b62720f --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDirectoryStreamAccessor.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Fs +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + internal sealed class DotNetDirectoryStreamAccessor : Accessor + { + + Type sunNioFsDotNetPath; + Type javaNioFileDirectoryStreamFilter; + + MethodAccessor, object, object>> init; + FieldAccessor path; + FieldAccessor> files; + FieldAccessor filter; + + /// + /// Initializes a new instance. + /// + /// + public DotNetDirectoryStreamAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.fs.DotNetDirectoryStream") + { + + } + + Type SunNioFsDotNetPath => Resolve(ref sunNioFsDotNetPath, "sun.nio.fs.DotNetPath"); + + + Type JavaNioFileDirectoryStreamFilter => Resolve(ref javaNioFileDirectoryStreamFilter, "java.nio.file.DirectoryStream+Filter"); + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + public object Init(object path, IEnumerable files, object filter) => GetConstructor(ref init, SunNioFsDotNetPath, typeof(System.Collections.IEnumerable), JavaNioFileDirectoryStreamFilter).Invoker(path, files, filter); + + /// + /// Gets the value of the 'path' field. + /// + /// + /// + public object GetPath(object self) => GetField(ref path, nameof(path)).GetValue(self); + + /// + /// Gets the value of the 'files' field. + /// + /// + /// + public IEnumerable GetFiles(object self) => GetField(ref files, nameof(files)).GetValue(self); + + /// + /// Gets the value of the 'filter' field. + /// + /// + /// + public object GetFilter(object self) => GetField(ref filter, nameof(filter)).GetValue(self); + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDosFileAttributesAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDosFileAttributesAccessor.cs new file mode 100644 index 0000000000..b992c92a7c --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetDosFileAttributesAccessor.cs @@ -0,0 +1,39 @@ +using System; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Ch +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + /// + /// Provides runtime access to the 'sun.nio.fs.DotNetDosFileAttributes' type. + /// + internal sealed class DotNetDosFileAttributesAccessor : Accessor + { + + Type javaNioFileAttributeFileTime; + + MethodAccessor> init; + + /// + /// Initializes a new instance. + /// + /// + public DotNetDosFileAttributesAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.fs.DotNetDosFileAttributes") + { + + } + + Type JavaNioFileAttributeFileTime => Resolve(ref javaNioFileAttributeFileTime, "java.nio.file.attribute.FileTime"); + + /// + /// Initializes a new instance. + /// + public object Init(object creationTime, object lastAccessTime, object lastModifiedTime, object fileKey, bool isDirectory, bool isOther, bool isRegularFile, bool isSymbolicLink, long size, bool isReadOnly, bool isHidden, bool isArchive, bool isSystem) => GetConstructor(ref init, JavaNioFileAttributeFileTime, JavaNioFileAttributeFileTime, JavaNioFileAttributeFileTime, typeof(object), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(long), typeof(bool), typeof(bool), typeof(bool), typeof(bool)).Invoker(creationTime, lastAccessTime, lastModifiedTime, fileKey, isDirectory, isOther, isRegularFile, isSymbolicLink, size, isReadOnly, isHidden, isArchive, isSystem); + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetPathAccessor.cs b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetPathAccessor.cs new file mode 100644 index 0000000000..19e38d448d --- /dev/null +++ b/src/IKVM.Runtime/Accessors/Sun/Nio/Fs/DotNetPathAccessor.cs @@ -0,0 +1,51 @@ +using System; + +namespace IKVM.Runtime.Accessors.Sun.Nio.Fs +{ + +#if FIRST_PASS == false && EXPORTER == false && IMPORTER == false + + internal sealed class DotNetPathAccessor : Accessor + { + + MethodAccessor> init; + FieldAccessor fs; + FieldAccessor path; + + /// + /// Initializes a new instance. + /// + /// + public DotNetPathAccessor(AccessorTypeResolver resolver) : + base(resolver, "sun.nio.fs.DotNetPath") + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public object Init(object fs, string path) => GetConstructor(ref init, Resolve("sun.nio.fs.DotNetFileSystem"), typeof(string)).Invoker(fs, path); + + /// + /// Gets the value of the 'fs' field. + /// + /// + /// + public object GetFs(object self) => GetField(ref fs, nameof(fs)).GetValue(self); + + /// + /// Gets the value of the 'path' field. + /// + /// + /// + public string GetPath(object self) => GetField(ref path, nameof(path)).GetValue(self); + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Annotation.cs b/src/IKVM.Runtime/Annotation.cs new file mode 100644 index 0000000000..b95a73ffef --- /dev/null +++ b/src/IKVM.Runtime/Annotation.cs @@ -0,0 +1,330 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Attributes; +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + abstract class Annotation + { + +#if IMPORTER + + internal static Annotation LoadAssemblyCustomAttribute(ClassLoaderWrapper loader, object[] def) + { + if (def.Length == 0) + throw new ArgumentException("LoadAssemblyCustomAttribute did not receive any definitions."); + if (object.Equals(def[0], AnnotationDefaultAttribute.TAG_ANNOTATION) == false) + throw new InternalException("LoadAssemblyCustomAttribute did not receive AnnotationDefaultAttribute.TAG_ANNOTATION."); + + string annotationClass = (string)def[1]; + if (ClassFile.IsValidFieldSig(annotationClass)) + { + try + { + return loader.RetTypeWrapperFromSig(annotationClass.Replace('/', '.'), LoadMode.LoadOrThrow).Annotation; + } + catch (RetargetableJavaException) + { + + } + } + return null; + } + +#endif + +#if !EXPORTER + // NOTE this method returns null if the type could not be found + // or if the type is not a Custom Attribute and we're not in the static compiler + internal static Annotation Load(TypeWrapper owner, object[] def) + { + Debug.Assert(def[0].Equals(AnnotationDefaultAttribute.TAG_ANNOTATION)); + string annotationClass = (string)def[1]; +#if !IMPORTER + if (!annotationClass.EndsWith("$Annotation;") + && !annotationClass.EndsWith("$Annotation$__ReturnValue;") + && !annotationClass.EndsWith("$Annotation$__Multiple;")) + { + // we don't want to try to load an annotation in dynamic mode, + // unless it is a .NET custom attribute (which can affect runtime behavior) + return null; + } +#endif + if (ClassFile.IsValidFieldSig(annotationClass)) + { + TypeWrapper tw = owner.GetClassLoader().RetTypeWrapperFromSig(annotationClass.Replace('/', '.'), LoadMode.Link); + // Java allows inaccessible annotations to be used, so when the annotation isn't visible + // we fall back to using the DynamicAnnotationAttribute. + if (!tw.IsUnloadable && tw.IsAccessibleFrom(owner)) + { + return tw.Annotation; + } + } + Tracer.Warning(Tracer.Compiler, "Unable to load annotation class {0}", annotationClass); +#if IMPORTER + return new CompiledTypeWrapper.CompiledAnnotation(StaticCompiler.GetRuntimeType("IKVM.Attributes.DynamicAnnotationAttribute")); +#else + return null; +#endif + } +#endif + + private static object LookupEnumValue(Type enumType, string value) + { + FieldInfo field = enumType.GetField(value, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + if (field != null) + { + return field.GetRawConstantValue(); + } + // both __unspecified and missing values end up here + return EnumHelper.GetPrimitiveValue(EnumHelper.GetUnderlyingType(enumType), 0); + } + + protected static object ConvertValue(ClassLoaderWrapper loader, Type targetType, object obj) + { + if (targetType.IsEnum) + { + // TODO check the obj descriptor matches the type we expect + if (((object[])obj)[0].Equals(AnnotationDefaultAttribute.TAG_ARRAY)) + { + object[] arr = (object[])obj; + object value = null; + for (int i = 1; i < arr.Length; i++) + { + // TODO check the obj descriptor matches the type we expect + string s = ((object[])arr[i])[2].ToString(); + object newval = LookupEnumValue(targetType, s); + if (value == null) + { + value = newval; + } + else + { + value = EnumHelper.OrBoxedIntegrals(value, newval); + } + } + return value; + } + else + { + string s = ((object[])obj)[2].ToString(); + if (s == "__unspecified") + { + // TODO we should probably return null and handle that + } + return LookupEnumValue(targetType, s); + } + } + else if (targetType == Types.Type) + { + // TODO check the obj descriptor matches the type we expect + return loader.FieldTypeWrapperFromSig(((string)((object[])obj)[1]).Replace('/', '.'), LoadMode.LoadOrThrow).TypeAsTBD; + } + else if (targetType.IsArray) + { + // TODO check the obj descriptor matches the type we expect + object[] arr = (object[])obj; + Type elementType = targetType.GetElementType(); + object[] targetArray = new object[arr.Length - 1]; + for (int i = 1; i < arr.Length; i++) + { + targetArray[i - 1] = ConvertValue(loader, elementType, arr[i]); + } + return targetArray; + } + else + { + return obj; + } + } + + internal static bool HasRetentionPolicyRuntime(object[] annotations) + { + if (annotations != null) + { + foreach (object[] def in annotations) + { + if (def[1].Equals("Ljava/lang/annotation/Retention;")) + { + for (int i = 2; i < def.Length; i += 2) + { + if (def[i].Equals("value")) + { + object[] val = def[i + 1] as object[]; + if (val != null + && val.Length == 3 + && val[0].Equals(AnnotationDefaultAttribute.TAG_ENUM) + && val[1].Equals("Ljava/lang/annotation/RetentionPolicy;") + && val[2].Equals("RUNTIME")) + { + return true; + } + } + } + } + } + } + return false; + } + + internal static bool HasObsoleteAttribute(object[] annotations) + { + if (annotations != null) + { + foreach (object[] def in annotations) + { + if (def[1].Equals("Lcli/System/ObsoleteAttribute$Annotation;")) + { + return true; + } + } + } + return false; + } + + protected static object QualifyClassNames(ClassLoaderWrapper loader, object annotation) + { + bool copy = false; + object[] def = (object[])annotation; + for (int i = 3; i < def.Length; i += 2) + { + object[] val = def[i] as object[]; + if (val != null) + { + object[] newval = ValueQualifyClassNames(loader, val); + if (newval != val) + { + if (!copy) + { + copy = true; + object[] newdef = new object[def.Length]; + Array.Copy(def, newdef, def.Length); + def = newdef; + } + def[i] = newval; + } + } + } + return def; + } + + private static object[] ValueQualifyClassNames(ClassLoaderWrapper loader, object[] val) + { + if (val[0].Equals(AnnotationDefaultAttribute.TAG_ANNOTATION)) + { + return (object[])QualifyClassNames(loader, val); + } + else if (val[0].Equals(AnnotationDefaultAttribute.TAG_CLASS)) + { + string sig = (string)val[1]; + if (sig.StartsWith("L")) + { + TypeWrapper tw = loader.LoadClassByDottedNameFast(sig.Substring(1, sig.Length - 2).Replace('/', '.')); + if (tw != null) + { + return new object[] { AnnotationDefaultAttribute.TAG_CLASS, "L" + tw.TypeAsBaseType.AssemblyQualifiedName.Replace('.', '/') + ";" }; + } + } + return val; + } + else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ENUM)) + { + string sig = (string)val[1]; + TypeWrapper tw = loader.LoadClassByDottedNameFast(sig.Substring(1, sig.Length - 2).Replace('/', '.')); + if (tw != null) + { + return new object[] { AnnotationDefaultAttribute.TAG_ENUM, "L" + tw.TypeAsBaseType.AssemblyQualifiedName.Replace('.', '/') + ";", val[2] }; + } + return val; + } + else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ARRAY)) + { + bool copy = false; + for (int i = 1; i < val.Length; i++) + { + object[] nval = val[i] as object[]; + if (nval != null) + { + object newnval = ValueQualifyClassNames(loader, nval); + if (newnval != nval) + { + if (!copy) + { + copy = true; + object[] newval = new object[val.Length]; + Array.Copy(val, newval, val.Length); + val = newval; + } + val[i] = newnval; + } + } + } + return val; + } + else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ERROR)) + { + return val; + } + else + { + throw new InvalidOperationException(); + } + } + + internal abstract void Apply(ClassLoaderWrapper loader, TypeBuilder tb, object annotation); + internal abstract void Apply(ClassLoaderWrapper loader, MethodBuilder mb, object annotation); + internal abstract void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation); + internal abstract void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation); + internal abstract void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation); + internal abstract void Apply(ClassLoaderWrapper loader, PropertyBuilder pb, object annotation); + + internal virtual void ApplyReturnValue(ClassLoaderWrapper loader, MethodBuilder mb, ref ParameterBuilder pb, object annotation) + { + + } + + internal abstract bool IsCustomAttribute { get; } + + } + +} diff --git a/src/IKVM.Runtime/AnonymousTypeWrapper.cs b/src/IKVM.Runtime/AnonymousTypeWrapper.cs new file mode 100644 index 0000000000..64026ff51c --- /dev/null +++ b/src/IKVM.Runtime/AnonymousTypeWrapper.cs @@ -0,0 +1,161 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; + +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + +#if !IMPORTER && !EXPORTER + + /// + /// Represents an intrinsified anonymous class. Currently only used by LambdaMetafactory. + /// + sealed class AnonymousTypeWrapper : TypeWrapper + { + + readonly Type type; + + /// + /// Initializes a new instance. + /// + /// + internal AnonymousTypeWrapper(Type type) : + base(TypeFlags.Anonymous, Modifiers.Final | Modifiers.Synthetic, GetName(type)) + { + this.type = type; + } + + internal static bool IsAnonymous(Type type) + { + return type.IsSpecialName + && type.Name.StartsWith(NestedTypeName.IntrinsifiedAnonymousClass, StringComparison.Ordinal) + && AttributeHelper.IsJavaModule(type.Module); + } + + private static string GetName(Type type) + { + return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).Name + + type.Name.Replace(NestedTypeName.IntrinsifiedAnonymousClass, "$$Lambda$"); + } + + internal override ClassLoaderWrapper GetClassLoader() + { + return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).GetClassLoader(); + } + + internal override Type TypeAsTBD + { + get { return type; } + } + + internal override TypeWrapper BaseTypeWrapper + { + get { return CoreClasses.java.lang.Object.Wrapper; } + } + + internal override TypeWrapper[] Interfaces + { + get + { + TypeWrapper[] interfaces = GetImplementedInterfacesAsTypeWrappers(type); + if (type.IsSerializable) + { + // we have to remove the System.Runtime.Serialization.ISerializable interface + List list = new List(interfaces); + list.RemoveAll(Serialization.IsISerializable); + return list.ToArray(); + } + return interfaces; + } + } + + protected override void LazyPublishMembers() + { + List methods = new List(); + foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + if (mi.IsSpecialName) + { + // we use special name to hide default methods + } + else if (mi.IsPublic) + { + TypeWrapper returnType; + TypeWrapper[] parameterTypes; + string signature; + GetSig(mi, out returnType, out parameterTypes, out signature); + methods.Add(new TypicalMethodWrapper(this, mi.Name, signature, mi, returnType, parameterTypes, Modifiers.Public, MemberFlags.None)); + } + else if (mi.Name == "writeReplace") + { + methods.Add(new TypicalMethodWrapper(this, "writeReplace", "()Ljava.lang.Object;", mi, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, + Modifiers.Private | Modifiers.Final, MemberFlags.None)); + } + } + SetMethods(methods.ToArray()); + List fields = new List(); + foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + TypeWrapper fieldType = CompiledTypeWrapper.GetFieldTypeWrapper(fi); + fields.Add(new SimpleFieldWrapper(this, fieldType, fi, fi.Name, fieldType.SigName, new ExModifiers(Modifiers.Private | Modifiers.Final, false))); + } + SetFields(fields.ToArray()); + } + + private void GetSig(MethodInfo mi, out TypeWrapper returnType, out TypeWrapper[] parameterTypes, out string signature) + { + returnType = CompiledTypeWrapper.GetParameterTypeWrapper(mi.ReturnParameter); + ParameterInfo[] parameters = mi.GetParameters(); + parameterTypes = new TypeWrapper[parameters.Length]; + System.Text.StringBuilder sb = new System.Text.StringBuilder("("); + for (int i = 0; i < parameters.Length; i++) + { + parameterTypes[i] = CompiledTypeWrapper.GetParameterTypeWrapper(parameters[i]); + sb.Append(parameterTypes[i].SigName); + } + sb.Append(')'); + sb.Append(returnType.SigName); + signature = sb.ToString(); + } + } + +#endif + +} diff --git a/src/IKVM.Runtime/ArrayTypeWrapper.cs b/src/IKVM.Runtime/ArrayTypeWrapper.cs new file mode 100644 index 0000000000..7b5a7032d0 --- /dev/null +++ b/src/IKVM.Runtime/ArrayTypeWrapper.cs @@ -0,0 +1,212 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + sealed class ArrayTypeWrapper : TypeWrapper + { + + static volatile TypeWrapper[] interfaces; + static volatile MethodInfo clone; + readonly TypeWrapper ultimateElementTypeWrapper; + Type arrayType; + bool finished; + + /// + /// Initializes a new instance. + /// + /// + /// + internal ArrayTypeWrapper(TypeWrapper ultimateElementTypeWrapper, string name) : + base(ultimateElementTypeWrapper.IsInternal ? TypeFlags.InternalAccess : TypeFlags.None, Modifiers.Final | Modifiers.Abstract | (ultimateElementTypeWrapper.Modifiers & Modifiers.Public), name) + { + Debug.Assert(!ultimateElementTypeWrapper.IsArray); + this.ultimateElementTypeWrapper = ultimateElementTypeWrapper; + } + + internal override TypeWrapper BaseTypeWrapper + { + get { return CoreClasses.java.lang.Object.Wrapper; } + } + + internal override ClassLoaderWrapper GetClassLoader() + { + return ultimateElementTypeWrapper.GetClassLoader(); + } + + internal static MethodInfo CloneMethod + { + get + { + if (clone == null) + clone = Types.Array.GetMethod("Clone", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); + + return clone; + } + } + + protected override void LazyPublishMembers() + { + var mw = new SimpleCallMethodWrapper(this, "clone", "()Ljava.lang.Object;", CloneMethod, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, Modifiers.Public, MemberFlags.HideFromReflection, SimpleOpCode.Callvirt, SimpleOpCode.Callvirt); + mw.Link(); + SetMethods(new MethodWrapper[] { mw }); + SetFields(FieldWrapper.EmptyArray); + } + + internal override Modifiers ReflectiveModifiers + { + get + { + return Modifiers.Final | Modifiers.Abstract | (ultimateElementTypeWrapper.ReflectiveModifiers & Modifiers.AccessMask); + } + } + + internal override string SigName + { + get + { + // for arrays the signature name is the same as the normal name + return Name; + } + } + + internal override TypeWrapper[] Interfaces + { + get + { + if (interfaces == null) + { + TypeWrapper[] tw = new TypeWrapper[2]; + tw[0] = CoreClasses.java.lang.Cloneable.Wrapper; + tw[1] = CoreClasses.java.io.Serializable.Wrapper; + interfaces = tw; + } + return interfaces; + } + } + + internal override Type TypeAsTBD + { + get + { + while (arrayType == null) + { + bool prevFinished = finished; + Type type = MakeArrayType(ultimateElementTypeWrapper.TypeAsArrayType, this.ArrayRank); + if (prevFinished) + { + // We were already finished prior to the call to MakeArrayType, so we can safely + // set arrayType to the finished type. + // Note that this takes advantage of the fact that once we've been finished, + // we can never become unfinished. + arrayType = type; + } + else + { + lock (this) + { + // To prevent a race with Finish, we can only set arrayType in this case + // (inside the locked region) if we've not already finished. If we have + // finished, we need to rerun MakeArrayType on the now finished element type. + // Note that there is a benign race left, because it is possible that another + // thread finishes right after we've set arrayType and exited the locked + // region. This is not problem, because TypeAsTBD is only guaranteed to + // return a finished type *after* Finish has been called. + if (!finished) + { + arrayType = type; + } + } + } + } + + return arrayType; + } + } + + internal override void Finish() + { + if (!finished) + { + ultimateElementTypeWrapper.Finish(); + lock (this) + { + // Now that we've finished the element type, we must clear arrayType, + // because it may still refer to a TypeBuilder. Note that we have to + // do this atomically with setting "finished", to prevent a race + // with TypeAsTBD. + finished = true; + arrayType = null; + } + } + } + + internal override bool IsFastClassLiteralSafe + { + // here we have to deal with the somewhat strange fact that in Java you cannot represent primitive type class literals, + // but you can represent arrays of primitive types as a class literal + get { return ultimateElementTypeWrapper.IsFastClassLiteralSafe || ultimateElementTypeWrapper.IsPrimitive; } + } + + internal override TypeWrapper GetUltimateElementTypeWrapper() + { + return ultimateElementTypeWrapper; + } + + internal static Type MakeArrayType(Type type, int dims) + { + // NOTE this is not just an optimization, but it is also required to + // make sure that ReflectionOnly types stay ReflectionOnly types + // (in particular instantiations of generic types from mscorlib that + // have ReflectionOnly type parameters). + for (int i = 0; i < dims; i++) + { + type = type.MakeArrayType(); + } + return type; + } + } + +#if !IMPORTER && !EXPORTER + +#endif + +} diff --git a/src/IKVM.Runtime/ArrayUtil.cs b/src/IKVM.Runtime/ArrayUtil.cs new file mode 100644 index 0000000000..546436720b --- /dev/null +++ b/src/IKVM.Runtime/ArrayUtil.cs @@ -0,0 +1,57 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +namespace IKVM.Internal +{ + + static class ArrayUtil + { + internal static T[] Concat(X obj, T[] arr) + where X : T + { + T[] narr = new T[arr.Length + 1]; + narr[0] = obj; + Array.Copy(arr, 0, narr, 1, arr.Length); + return narr; + } + + internal static T[] Concat(T[] arr, X obj) + where X : T + { + Array.Resize(ref arr, arr.Length + 1); + arr[arr.Length - 1] = obj; + return arr; + } + + internal static T[] DropFirst(T[] arr) + { + T[] narr = new T[arr.Length - 1]; + Array.Copy(arr, 1, narr, 0, narr.Length); + return narr; + } + + } + +} diff --git a/src/IKVM.Runtime/AssemblyClassLoader.cs b/src/IKVM.Runtime/AssemblyClassLoader.cs index c7c4461b55..fbd581ac16 100644 --- a/src/IKVM.Runtime/AssemblyClassLoader.cs +++ b/src/IKVM.Runtime/AssemblyClassLoader.cs @@ -26,16 +26,14 @@ Jeroen Frijters using System.Collections.Generic; using System.Diagnostics; using System.Threading; - -using FormatterServices = System.Runtime.Serialization.FormatterServices; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using IKVM.Attributes; -using IKVM.Runtime.Syntax; - -using System.Runtime.CompilerServices; using IKVM.Runtime; +using IKVM.Runtime.Syntax; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using Type = IKVM.Reflection.Type; @@ -45,10 +43,14 @@ Jeroen Frijters using System.Reflection; #endif +#if IMPORTER +using IKVM.Tools.Importer; +#endif + namespace IKVM.Internal { - class AssemblyClassLoader : ClassLoaderWrapper + internal class AssemblyClassLoader : ClassLoaderWrapper { /// @@ -57,7 +59,7 @@ class AssemblyClassLoader : ClassLoaderWrapper /// static readonly ConditionalWeakTable assemblyClassLoaders = new(); -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS static Dictionary customClassLoaderRedirects; #endif @@ -102,17 +104,17 @@ static AssemblyClassLoader Create(Assembly assembly) return FromAssembly(mainAssembly); } -#if STATIC_COMPILER +#if IMPORTER - if (JVM.CoreAssembly == null && CompilerClassLoader.IsCoreAssembly(assembly)) - { - JVM.CoreAssembly = assembly; - ClassLoaderWrapper.LoadRemappedTypes(); - } + if (JVM.BaseAssembly == null && CompilerClassLoader.IsCoreAssembly(assembly)) + { + JVM.BaseAssembly = assembly; + ClassLoaderWrapper.LoadRemappedTypes(); + } #endif - if (assembly == JVM.CoreAssembly) + if (assembly == JVM.BaseAssembly) { // This cast is necessary for ikvmc and a no-op for the runtime. // Note that the cast cannot fail, because ikvmc will only return a non AssemblyClassLoader @@ -127,7 +129,7 @@ static AssemblyClassLoader Create(Assembly assembly) AssemblyLoader assemblyLoader; string[] references; AssemblyClassLoader[] delegates; -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS JavaClassLoaderConstructionInProgress jclcip; java.security.ProtectionDomain protectionDomain; byte hasCustomClassLoader; /* 0 = unknown, 1 = yes, 2 = no */ @@ -151,7 +153,7 @@ sealed class AssemblyLoader bool hasDotNetModule; AssemblyName[] internalsVisibleTo; string[] jarList; -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS sun.misc.URLClassPath urlClassPath; #endif @@ -475,7 +477,7 @@ internal bool InternalsVisibleTo(AssemblyName otherName) return false; } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS internal java.util.Enumeration FindResources(string name) { @@ -519,36 +521,36 @@ internal AssemblyClassLoader(Assembly assembly, string[] fixedReferences) : this.references = fixedReferences; } -#if STATIC_COMPILER - - internal static void PreloadExportedAssemblies(Assembly assembly) - { - if (assembly.GetManifestResourceInfo("ikvm.exports") != null) - { - using (Stream stream = assembly.GetManifestResourceStream("ikvm.exports")) - { - BinaryReader rdr = new BinaryReader(stream); - int assemblyCount = rdr.ReadInt32(); - for (int i = 0; i < assemblyCount; i++) - { - string assemblyName = rdr.ReadString(); - int typeCount = rdr.ReadInt32(); - if (typeCount != 0) - { - for (int j = 0; j < typeCount; j++) - { - rdr.ReadInt32(); - } - try - { - StaticCompiler.LoadFile(assembly.Location + "/../" + new AssemblyName(assemblyName).Name + ".dll"); - } - catch { } - } - } - } - } - } +#if IMPORTER + + internal static void PreloadExportedAssemblies(Assembly assembly) + { + if (assembly.GetManifestResourceInfo("ikvm.exports") != null) + { + using (Stream stream = assembly.GetManifestResourceStream("ikvm.exports")) + { + BinaryReader rdr = new BinaryReader(stream); + int assemblyCount = rdr.ReadInt32(); + for (int i = 0; i < assemblyCount; i++) + { + string assemblyName = rdr.ReadString(); + int typeCount = rdr.ReadInt32(); + if (typeCount != 0) + { + for (int j = 0; j < typeCount; j++) + { + rdr.ReadInt32(); + } + try + { + StaticCompiler.LoadFile(assembly.Location + "/../" + new AssemblyName(assemblyName).Name + ".dll"); + } + catch { } + } + } + } + } + } #endif @@ -636,8 +638,8 @@ Assembly LoadAssemblyOrClearName(ref string name, bool exported) try { -#if STATIC_COMPILER || STUB_GENERATOR - return StaticCompiler.Load(name); +#if IMPORTER || EXPORTER + return StaticCompiler.Load(name); #else return Assembly.Load(name); #endif @@ -751,11 +753,18 @@ AssemblyLoader GetLoaderForExportedAssembly(Assembly assembly) return loader; } + /// + /// Gets the type wrapper for the given type located in this assembly. + /// + /// + /// + /// internal virtual TypeWrapper GetWrapperFromAssemblyType(Type type) { - //Tracer.Info(Tracer.Runtime, "GetWrapperFromAssemblyType: {0}", type.FullName); - Debug.Assert(!type.Name.EndsWith("[]"), "!type.IsArray", type.FullName); - Debug.Assert(AssemblyClassLoader.FromAssembly(type.Assembly) == this); + if (type.Name.EndsWith("[]")) + throw new InternalException(); + if (AssemblyClassLoader.FromAssembly(type.Assembly) != this) + throw new InternalException(); var wrapper = GetLoader(type.Assembly).CreateWrapperForAssemblyType(type); if (wrapper != null) @@ -771,19 +780,20 @@ internal virtual TypeWrapper GetWrapperFromAssemblyType(Type type) wrapper = RegisterInitiatingLoader(wrapper); } + // this really shouldn't happen, it means that we have two different types in our assembly that both + // have the same Java name if (wrapper.TypeAsTBD != type && (!wrapper.IsRemapped || wrapper.TypeAsBaseType != type)) { - // this really shouldn't happen, it means that we have two different types in our assembly that both - // have the same Java name -#if STATIC_COMPILER - throw new FatalCompilerErrorException(Message.AssemblyContainsDuplicateClassNames, type.FullName, wrapper.TypeAsTBD.FullName, wrapper.Name, type.Assembly.FullName); +#if IMPORTER + throw new FatalCompilerErrorException(Message.AssemblyContainsDuplicateClassNames, type.FullName, wrapper.TypeAsTBD.FullName, wrapper.Name, type.Assembly.FullName); #else - string msg = $"\nType \"{type.FullName}\" and \"{wrapper.TypeAsTBD.FullName}\" both map to the same name \"{wrapper.Name}\".\n"; - JVM.CriticalFailure(msg, null); + throw new InternalException($"\nType \"{type.FullName}\" and \"{wrapper.TypeAsTBD.FullName}\" both map to the same name \"{wrapper.Name}\"."); #endif } + return wrapper; } + return null; } @@ -793,7 +803,7 @@ protected override TypeWrapper LoadClassImpl(string name, LoadMode mode) if (tw != null) return tw; -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS while (hasCustomClassLoader != 2) { @@ -843,7 +853,7 @@ TypeWrapper LoadBootstrapIfNonJavaAssembly(string name) TypeWrapper LoadDynamic(string name) { -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS var classFile = name.Replace('.', '/') + ".class"; foreach (var res in GetBootstrapClassLoader().FindDelegateResources(classFile)) return res.Loader.DefineDynamic(name, res.URL); @@ -855,7 +865,7 @@ TypeWrapper LoadDynamic(string name) return null; } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS TypeWrapper DefineDynamic(string name, java.net.URL url) { @@ -910,7 +920,7 @@ TypeWrapper FindReferenced(string name) return null; } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER static java.net.URL MakeResourceURL(Assembly asm, string name) { @@ -1072,9 +1082,9 @@ protected IEnumerable FindDelegateResources(string name) yield return url; } -#endif // !STATIC_COMPILER +#endif // !IMPORTER -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER private sealed class JavaClassLoaderConstructionInProgress { @@ -1136,19 +1146,14 @@ private java.lang.ClassLoader WaitInitializeJavaClassLoader(Type customClassLoad internal override java.lang.ClassLoader GetJavaClassLoader() { - if (javaClassLoader == null) - { - return WaitInitializeJavaClassLoader(GetCustomClassLoaderType()); - } - return javaClassLoader; + return javaClassLoader ?? WaitInitializeJavaClassLoader(GetCustomClassLoaderType()); } internal virtual java.security.ProtectionDomain GetProtectionDomain() { if (protectionDomain == null) - { Interlocked.CompareExchange(ref protectionDomain, new java.security.ProtectionDomain(assemblyLoader.Assembly), null); - } + return protectionDomain; } #endif @@ -1162,11 +1167,11 @@ protected override TypeWrapper FindLoadedClassLazy(string name) internal override bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper friend) { - ClassLoaderWrapper other = friend.GetClassLoader(); + var other = friend.GetClassLoader(); if (this == other) { -#if STATIC_COMPILER || STUB_GENERATOR - return true; +#if IMPORTER || EXPORTER + return true; #else // we're OK if the type being accessed (wrapper) is a dynamic type // or if the dynamic assembly has internal access @@ -1175,13 +1180,13 @@ internal override bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper f #endif } AssemblyName otherName; -#if STATIC_COMPILER - CompilerClassLoader ccl = other as CompilerClassLoader; - if (ccl == null) - { - return false; - } - otherName = ccl.GetAssemblyName(); +#if IMPORTER + CompilerClassLoader ccl = other as CompilerClassLoader; + if (ccl == null) + { + return false; + } + otherName = ccl.GetAssemblyName(); #else AssemblyClassLoader acl = other as AssemblyClassLoader; if (acl == null) @@ -1202,7 +1207,7 @@ internal void AddDelegate(AssemblyClassLoader acl) } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal List> GetPackageInfo() { @@ -1219,7 +1224,7 @@ internal List> GetPackageInfo() #endif -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER Type GetCustomClassLoaderType() { @@ -1396,7 +1401,7 @@ sealed class BootstrapClassLoader : AssemblyClassLoader /// Initializes a new instance. /// internal BootstrapClassLoader() : - base(JVM.CoreAssembly, new string[] { typeof(object).Assembly.FullName, typeof(Uri).Assembly.FullName }) + base(JVM.BaseAssembly, new string[] { typeof(object).Assembly.FullName, typeof(Uri).Assembly.FullName }) { } @@ -1429,7 +1434,7 @@ protected override void CheckProhibitedPackage(string className) } -#if !FIRST_PASS && !STATIC_COMPILER && !STUB_GENERATOR +#if !FIRST_PASS && !IMPORTER && !EXPORTER internal override java.lang.ClassLoader GetJavaClassLoader() { diff --git a/src/IKVM.Runtime/AttributeHelper.cs b/src/IKVM.Runtime/AttributeHelper.cs new file mode 100644 index 0000000000..bfe72ba68f --- /dev/null +++ b/src/IKVM.Runtime/AttributeHelper.cs @@ -0,0 +1,1235 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; + +using IKVM.Attributes; +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; + +using IKVM.ByteCode.Reading; +using IKVM.ByteCode.Parsing; + +using System.Linq; +using System.IO; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + static class AttributeHelper + { + +#if IMPORTER + + static CustomAttributeBuilder ghostInterfaceAttribute; + static CustomAttributeBuilder deprecatedAttribute; + static CustomAttributeBuilder editorBrowsableNever; + static ConstructorInfo implementsAttribute; + static ConstructorInfo throwsAttribute; + static ConstructorInfo sourceFileAttribute; + static ConstructorInfo lineNumberTableAttribute1; + static ConstructorInfo lineNumberTableAttribute2; + static ConstructorInfo enclosingMethodAttribute; + static ConstructorInfo signatureAttribute; + static ConstructorInfo methodParametersAttribute; + static ConstructorInfo runtimeVisibleTypeAnnotationsAttribute; + static ConstructorInfo constantPoolAttribute; + static CustomAttributeBuilder paramArrayAttribute; + static ConstructorInfo nonNestedInnerClassAttribute; + static ConstructorInfo nonNestedOuterClassAttribute; + static readonly Type typeofModifiers = JVM.LoadType(typeof(Modifiers)); + static readonly Type typeofSourceFileAttribute = JVM.LoadType(typeof(SourceFileAttribute)); + static readonly Type typeofLineNumberTableAttribute = JVM.LoadType(typeof(LineNumberTableAttribute)); +#endif // IMPORTER + static readonly Type typeofRemappedClassAttribute = JVM.LoadType(typeof(RemappedClassAttribute)); + static readonly Type typeofRemappedTypeAttribute = JVM.LoadType(typeof(RemappedTypeAttribute)); + static readonly Type typeofModifiersAttribute = JVM.LoadType(typeof(ModifiersAttribute)); + static readonly Type typeofRemappedInterfaceMethodAttribute = JVM.LoadType(typeof(RemappedInterfaceMethodAttribute)); + static readonly Type typeofNameSigAttribute = JVM.LoadType(typeof(NameSigAttribute)); + static readonly Type typeofJavaModuleAttribute = JVM.LoadType(typeof(JavaModuleAttribute)); + static readonly Type typeofSignatureAttribute = JVM.LoadType(typeof(SignatureAttribute)); + static readonly Type typeofInnerClassAttribute = JVM.LoadType(typeof(InnerClassAttribute)); + static readonly Type typeofImplementsAttribute = JVM.LoadType(typeof(ImplementsAttribute)); + static readonly Type typeofGhostInterfaceAttribute = JVM.LoadType(typeof(GhostInterfaceAttribute)); + static readonly Type typeofExceptionIsUnsafeForMappingAttribute = JVM.LoadType(typeof(ExceptionIsUnsafeForMappingAttribute)); + static readonly Type typeofThrowsAttribute = JVM.LoadType(typeof(ThrowsAttribute)); + static readonly Type typeofHideFromJavaAttribute = JVM.LoadType(typeof(HideFromJavaAttribute)); + static readonly Type typeofHideFromJavaFlags = JVM.LoadType(typeof(HideFromJavaFlags)); + static readonly Type typeofNoPackagePrefixAttribute = JVM.LoadType(typeof(NoPackagePrefixAttribute)); + static readonly Type typeofAnnotationAttributeAttribute = JVM.LoadType(typeof(AnnotationAttributeAttribute)); + static readonly Type typeofNonNestedInnerClassAttribute = JVM.LoadType(typeof(NonNestedInnerClassAttribute)); + static readonly Type typeofNonNestedOuterClassAttribute = JVM.LoadType(typeof(NonNestedOuterClassAttribute)); + static readonly Type typeofEnclosingMethodAttribute = JVM.LoadType(typeof(EnclosingMethodAttribute)); + static readonly Type typeofMethodParametersAttribute = JVM.LoadType(typeof(MethodParametersAttribute)); + static readonly Type typeofRuntimeVisibleTypeAnnotationsAttribute = JVM.LoadType(typeof(RuntimeVisibleTypeAnnotationsAttribute)); + static readonly Type typeofConstantPoolAttribute = JVM.LoadType(typeof(ConstantPoolAttribute)); + static readonly CustomAttributeBuilder hideFromJavaAttribute = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(Type.EmptyTypes), new object[0]); + static readonly CustomAttributeBuilder hideFromReflection = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(new Type[] { typeofHideFromJavaFlags }), new object[] { HideFromJavaFlags.Reflection | HideFromJavaFlags.StackTrace | HideFromJavaFlags.StackWalk }); + + // we don't want beforefieldinit + static AttributeHelper() + { + + } + +#if IMPORTER + + private static object ParseValue(ClassLoaderWrapper loader, TypeWrapper tw, string val) + { + if (tw == CoreClasses.java.lang.String.Wrapper) + { + return val; + } + else if (tw.IsUnloadable) + { + throw new FatalCompilerErrorException(Message.MapFileTypeNotFound, tw.Name); + } + else if (tw.TypeAsTBD.IsEnum) + { + return EnumHelper.Parse(tw.TypeAsTBD, val); + } + else if (tw.TypeAsTBD == Types.Type) + { + TypeWrapper valtw = loader.LoadClassByDottedNameFast(val); + if (valtw != null) + { + return valtw.TypeAsBaseType; + } + return StaticCompiler.Universe.GetType(val, true); + } + else if (tw == PrimitiveTypeWrapper.BOOLEAN) + { + return bool.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.BYTE) + { + return (byte)sbyte.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.CHAR) + { + return char.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.SHORT) + { + return short.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.INT) + { + return int.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.FLOAT) + { + return float.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.LONG) + { + return long.Parse(val); + } + else if (tw == PrimitiveTypeWrapper.DOUBLE) + { + return double.Parse(val); + } + else + { + throw new NotImplementedException(); + } + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, TypeBuilder tb, IKVM.Tools.Importer.MapXml.Attribute attr) + { + tb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, FieldBuilder fb, IKVM.Tools.Importer.MapXml.Attribute attr) + { + fb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, ParameterBuilder pb, IKVM.Tools.Importer.MapXml.Attribute attr) + { + pb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, MethodBuilder mb, IKVM.Tools.Importer.MapXml.Attribute attr) + { + mb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, PropertyBuilder pb, IKVM.Tools.Importer.MapXml.Attribute attr) + { + pb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + internal static void SetCustomAttribute(ClassLoaderWrapper loader, AssemblyBuilder ab, IKVM.Tools.Importer.MapXml.Attribute attr) + { + ab.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + } + + private static void GetAttributeArgsAndTypes(ClassLoaderWrapper loader, IKVM.Tools.Importer.MapXml.Attribute attr, out Type[] argTypes, out object[] args) + { + // TODO add error handling + TypeWrapper[] twargs = loader.ArgTypeWrapperListFromSig(attr.Sig, LoadMode.Link); + argTypes = new Type[twargs.Length]; + args = new object[argTypes.Length]; + for (int i = 0; i < twargs.Length; i++) + { + argTypes[i] = twargs[i].TypeAsSignatureType; + TypeWrapper tw = twargs[i]; + if (tw == CoreClasses.java.lang.Object.Wrapper) + { + tw = loader.FieldTypeWrapperFromSig(attr.Params[i].Sig, LoadMode.Link); + } + if (tw.IsArray) + { + Array arr = Array.CreateInstance(Type.__GetSystemType(Type.GetTypeCode(tw.ElementTypeWrapper.TypeAsArrayType)), attr.Params[i].Elements.Length); + for (int j = 0; j < arr.Length; j++) + { + arr.SetValue(ParseValue(loader, tw.ElementTypeWrapper, attr.Params[i].Elements[j].Value), j); + } + args[i] = arr; + } + else + { + args[i] = ParseValue(loader, tw, attr.Params[i].Value); + } + } + } + + static CustomAttributeBuilder CreateCustomAttribute(ClassLoaderWrapper loader, IKVM.Tools.Importer.MapXml.Attribute attr) + { + // TODO add error handling + Type[] argTypes; + object[] args; + GetAttributeArgsAndTypes(loader, attr, out argTypes, out args); + if (attr.Type != null) + { + Type t = StaticCompiler.GetTypeForMapXml(loader, attr.Type); + ConstructorInfo ci = t.GetConstructor(argTypes); + if (ci == null) + { + throw new InvalidOperationException(string.Format("Constructor missing: {0}::{1}", attr.Type, attr.Sig)); + } + PropertyInfo[] namedProperties; + object[] propertyValues; + if (attr.Properties != null) + { + namedProperties = new PropertyInfo[attr.Properties.Length]; + propertyValues = new object[attr.Properties.Length]; + for (int i = 0; i < namedProperties.Length; i++) + { + namedProperties[i] = t.GetProperty(attr.Properties[i].Name); + propertyValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Properties[i].Sig, LoadMode.Link), attr.Properties[i].Value); + } + } + else + { + namedProperties = new PropertyInfo[0]; + propertyValues = new object[0]; + } + FieldInfo[] namedFields; + object[] fieldValues; + if (attr.Fields != null) + { + namedFields = new FieldInfo[attr.Fields.Length]; + fieldValues = new object[attr.Fields.Length]; + for (int i = 0; i < namedFields.Length; i++) + { + namedFields[i] = t.GetField(attr.Fields[i].Name); + fieldValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Fields[i].Sig, LoadMode.Link), attr.Fields[i].Value); + } + } + else + { + namedFields = new FieldInfo[0]; + fieldValues = new object[0]; + } + return new CustomAttributeBuilder(ci, args, namedProperties, propertyValues, namedFields, fieldValues); + } + else + { + if (attr.Properties != null) + { + throw new NotImplementedException("Setting property values on Java attributes is not implemented"); + } + TypeWrapper t = loader.LoadClassByDottedName(attr.Class); + FieldInfo[] namedFields; + object[] fieldValues; + if (attr.Fields != null) + { + namedFields = new FieldInfo[attr.Fields.Length]; + fieldValues = new object[attr.Fields.Length]; + for (int i = 0; i < namedFields.Length; i++) + { + FieldWrapper fw = t.GetFieldWrapper(attr.Fields[i].Name, attr.Fields[i].Sig); + fw.Link(); + namedFields[i] = fw.GetField(); + fieldValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Fields[i].Sig, LoadMode.Link), attr.Fields[i].Value); + } + } + else + { + namedFields = new FieldInfo[0]; + fieldValues = new object[0]; + } + MethodWrapper mw = t.GetMethodWrapper("", attr.Sig, false); + if (mw == null) + { + throw new InvalidOperationException(string.Format("Constructor missing: {0}::{1}", attr.Class, attr.Sig)); + } + mw.Link(); + ConstructorInfo ci = (mw.GetMethod() as ConstructorInfo) ?? ((MethodInfo)mw.GetMethod()).__AsConstructorInfo(); + return new CustomAttributeBuilder(ci, args, namedFields, fieldValues); + } + } + + private static CustomAttributeBuilder GetEditorBrowsableNever() + { + if (editorBrowsableNever == null) + { + // to avoid having to load (and find) System.dll, we construct a symbolic CustomAttributeBuilder + AssemblyName name = Types.Object.Assembly.GetName(); +#if NETFRAMEWORK + name.Name = "System"; +#endif + Universe u = StaticCompiler.Universe; + Type typeofEditorBrowsableAttribute = u.ResolveType(Types.Object.Assembly, "System.ComponentModel.EditorBrowsableAttribute, " + name.FullName); + Type typeofEditorBrowsableState = u.ResolveType(Types.Object.Assembly, "System.ComponentModel.EditorBrowsableState, " + name.FullName); + u.MissingTypeIsValueType += delegate (Type type) { return type == typeofEditorBrowsableState; }; + ConstructorInfo ctor = (ConstructorInfo)typeofEditorBrowsableAttribute.__CreateMissingMethod(ConstructorInfo.ConstructorName, + CallingConventions.Standard | CallingConventions.HasThis, null, default(CustomModifiers), new Type[] { typeofEditorBrowsableState }, null); + editorBrowsableNever = CustomAttributeBuilder.__FromBlob(ctor, new byte[] { 01, 00, 01, 00, 00, 00, 00, 00 }); + } + return editorBrowsableNever; + } + + internal static void SetEditorBrowsableNever(TypeBuilder tb) + { + tb.SetCustomAttribute(GetEditorBrowsableNever()); + } + + internal static void SetEditorBrowsableNever(MethodBuilder mb) + { + mb.SetCustomAttribute(GetEditorBrowsableNever()); + } + + internal static void SetEditorBrowsableNever(PropertyBuilder pb) + { + pb.SetCustomAttribute(GetEditorBrowsableNever()); + } + + internal static void SetDeprecatedAttribute(MethodBuilder mb) + { + if (deprecatedAttribute == null) + { + deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + } + mb.SetCustomAttribute(deprecatedAttribute); + } + + internal static void SetDeprecatedAttribute(TypeBuilder tb) + { + if (deprecatedAttribute == null) + { + deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + } + tb.SetCustomAttribute(deprecatedAttribute); + } + + internal static void SetDeprecatedAttribute(FieldBuilder fb) + { + if (deprecatedAttribute == null) + { + deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + } + fb.SetCustomAttribute(deprecatedAttribute); + } + + internal static void SetDeprecatedAttribute(PropertyBuilder pb) + { + if (deprecatedAttribute == null) + { + deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + } + pb.SetCustomAttribute(deprecatedAttribute); + } + + internal static void SetThrowsAttribute(MethodBuilder mb, string[] exceptions) + { + if (exceptions != null && exceptions.Length != 0) + { + if (throwsAttribute == null) + { + throwsAttribute = typeofThrowsAttribute.GetConstructor(new Type[] { JVM.Import(typeof(string[])) }); + } + exceptions = UnicodeUtil.EscapeInvalidSurrogates(exceptions); + mb.SetCustomAttribute(new CustomAttributeBuilder(throwsAttribute, new object[] { exceptions })); + } + } + + internal static void SetGhostInterface(TypeBuilder typeBuilder) + { + if (ghostInterfaceAttribute == null) + { + ghostInterfaceAttribute = new CustomAttributeBuilder(typeofGhostInterfaceAttribute.GetConstructor(Type.EmptyTypes), new object[0]); + } + typeBuilder.SetCustomAttribute(ghostInterfaceAttribute); + } + + internal static void SetNonNestedInnerClass(TypeBuilder typeBuilder, string className) + { + if (nonNestedInnerClassAttribute == null) + { + nonNestedInnerClassAttribute = typeofNonNestedInnerClassAttribute.GetConstructor(new Type[] { Types.String }); + } + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(nonNestedInnerClassAttribute, + new object[] { UnicodeUtil.EscapeInvalidSurrogates(className) })); + } + + internal static void SetNonNestedOuterClass(TypeBuilder typeBuilder, string className) + { + if (nonNestedOuterClassAttribute == null) + { + nonNestedOuterClassAttribute = typeofNonNestedOuterClassAttribute.GetConstructor(new Type[] { Types.String }); + } + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(nonNestedOuterClassAttribute, + new object[] { UnicodeUtil.EscapeInvalidSurrogates(className) })); + } +#endif // IMPORTER + + internal static void HideFromReflection(MethodBuilder mb) + { + mb.SetCustomAttribute(hideFromReflection); + } + + internal static void HideFromReflection(FieldBuilder fb) + { + fb.SetCustomAttribute(hideFromReflection); + } + + internal static void HideFromReflection(PropertyBuilder pb) + { + pb.SetCustomAttribute(hideFromReflection); + } + + internal static void HideFromJava(TypeBuilder typeBuilder) + { + typeBuilder.SetCustomAttribute(hideFromJavaAttribute); + } + + internal static void HideFromJava(MethodBuilder mb) + { + mb.SetCustomAttribute(hideFromJavaAttribute); + } + + internal static void HideFromJava(MethodBuilder mb, HideFromJavaFlags flags) + { + CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(new Type[] { typeofHideFromJavaFlags }), new object[] { flags }); + mb.SetCustomAttribute(cab); + } + + internal static void HideFromJava(FieldBuilder fb) + { + fb.SetCustomAttribute(hideFromJavaAttribute); + } + +#if IMPORTER + + internal static void HideFromJava(PropertyBuilder pb) + { + pb.SetCustomAttribute(hideFromJavaAttribute); + } + +#endif // IMPORTER + + internal static bool IsHideFromJava(Type type) + { + return type.IsDefined(typeofHideFromJavaAttribute, false) || (type.IsNested && (type.DeclaringType.IsDefined(typeofHideFromJavaAttribute, false) || type.Name.StartsWith("__<", StringComparison.Ordinal))); + } + + internal static bool IsHideFromJava(MemberInfo mi) + { + return (GetHideFromJavaFlags(mi) & HideFromJavaFlags.Code) != 0; + } + + internal static HideFromJavaFlags GetHideFromJavaFlags(MemberInfo mi) + { + // NOTE all privatescope fields and methods are "hideFromJava" + // because Java cannot deal with the potential name clashes + var fi = mi as FieldInfo; + if (fi != null && (fi.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope) + return HideFromJavaFlags.All; + + var mb = mi as MethodBase; + if (mb != null && (mb.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope) + return HideFromJavaFlags.All; + if (mi.Name.StartsWith("__<", StringComparison.Ordinal)) + return HideFromJavaFlags.All; + +#if !IMPORTER && !EXPORTER + + var attr = mi.GetCustomAttributes(typeofHideFromJavaAttribute, false); + if (attr.Length == 1) + return ((HideFromJavaAttribute)attr[0]).Flags; + +#else + var attr = CustomAttributeData.__GetCustomAttributes(mi, typeofHideFromJavaAttribute, false); + if (attr.Count == 1) + { + var args = attr[0].ConstructorArguments; + if (args.Count == 1) + return (HideFromJavaFlags)args[0].Value; + + return HideFromJavaFlags.All; + } +#endif + + return HideFromJavaFlags.None; + } + +#if IMPORTER + + internal static void SetImplementsAttribute(TypeBuilder typeBuilder, TypeWrapper[] ifaceWrappers) + { + var interfaces = new string[ifaceWrappers.Length]; + for (int i = 0; i < interfaces.Length; i++) + interfaces[i] = UnicodeUtil.EscapeInvalidSurrogates(ifaceWrappers[i].Name); + + if (implementsAttribute == null) + implementsAttribute = typeofImplementsAttribute.GetConstructor(new Type[] { JVM.Import(typeof(string[])) }); + + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(implementsAttribute, new object[] { interfaces })); + } + +#endif + + internal static bool IsGhostInterface(Type type) + { + return type.IsDefined(typeofGhostInterfaceAttribute, false); + } + + internal static bool IsRemappedType(Type type) + { + return type.IsDefined(typeofRemappedTypeAttribute, false); + } + + internal static bool IsExceptionIsUnsafeForMapping(Type type) + { + return type.IsDefined(typeofExceptionIsUnsafeForMappingAttribute, false); + } + + internal static ModifiersAttribute GetModifiersAttribute(MemberInfo member) + { +#if !IMPORTER && !EXPORTER + var attr = member.GetCustomAttributes(typeof(ModifiersAttribute), false); + return attr.Length == 1 ? (ModifiersAttribute)attr[0] : null; +#else + var attr = CustomAttributeData.__GetCustomAttributes(member, typeofModifiersAttribute, false); + if (attr.Count == 1) + { + var args = attr[0].ConstructorArguments; + if (args.Count == 2) + return new ModifiersAttribute((Modifiers)args[0].Value, (bool)args[1].Value); + + return new ModifiersAttribute((Modifiers)args[0].Value); + } + + return null; +#endif + } + + internal static ExModifiers GetModifiers(MethodBase mb, bool assemblyIsPrivate) + { + var attr = GetModifiersAttribute(mb); + if (attr != null) + return new ExModifiers(attr.Modifiers, attr.IsInternal); + + Modifiers modifiers = 0; + + if (mb.IsPublic) + { + modifiers |= Modifiers.Public; + } + else if (mb.IsPrivate) + { + modifiers |= Modifiers.Private; + } + else if (mb.IsFamily || mb.IsFamilyOrAssembly) + { + modifiers |= Modifiers.Protected; + } + else if (assemblyIsPrivate) + { + modifiers |= Modifiers.Private; + } + + // NOTE Java doesn't support non-virtual methods, but we set the Final modifier for + // non-virtual methods to approximate the semantics + if ((mb.IsFinal || (!mb.IsVirtual && ((modifiers & Modifiers.Private) == 0))) && !mb.IsStatic && !mb.IsConstructor) + { + modifiers |= Modifiers.Final; + } + + if (mb.IsAbstract) + { + modifiers |= Modifiers.Abstract; + } + else + { + // Some .NET interfaces (like System._AppDomain) have synchronized methods, + // Java doesn't allow synchronized on an abstract methods, so we ignore it for + // abstract methods. + if ((mb.GetMethodImplementationFlags() & MethodImplAttributes.Synchronized) != 0) + { + modifiers |= Modifiers.Synchronized; + } + } + + if (mb.IsStatic) + { + modifiers |= Modifiers.Static; + } + + if ((mb.Attributes & MethodAttributes.PinvokeImpl) != 0) + { + modifiers |= Modifiers.Native; + } + + var parameters = mb.GetParameters(); + if (parameters.Length > 0 && parameters[parameters.Length - 1].IsDefined(JVM.Import(typeof(ParamArrayAttribute)), false)) + modifiers |= Modifiers.VarArgs; + + return new ExModifiers(modifiers, false); + } + + internal static ExModifiers GetModifiers(FieldInfo fi, bool assemblyIsPrivate) + { + var attr = GetModifiersAttribute(fi); + if (attr != null) + return new ExModifiers(attr.Modifiers, attr.IsInternal); + + Modifiers modifiers = 0; + if (fi.IsPublic) + { + modifiers |= Modifiers.Public; + } + else if (fi.IsPrivate) + { + modifiers |= Modifiers.Private; + } + else if (fi.IsFamily || fi.IsFamilyOrAssembly) + { + modifiers |= Modifiers.Protected; + } + else if (assemblyIsPrivate) + { + modifiers |= Modifiers.Private; + } + + if (fi.IsInitOnly || fi.IsLiteral) + { + modifiers |= Modifiers.Final; + } + + if (fi.IsNotSerialized) + { + modifiers |= Modifiers.Transient; + } + + if (fi.IsStatic) + { + modifiers |= Modifiers.Static; + } + + if (Array.IndexOf(fi.GetRequiredCustomModifiers(), Types.IsVolatile) != -1) + { + modifiers |= Modifiers.Volatile; + } + + return new ExModifiers(modifiers, false); + } + +#if IMPORTER + internal static void SetModifiers(MethodBuilder mb, Modifiers modifiers, bool isInternal) + { + CustomAttributeBuilder customAttributeBuilder; + if (isInternal) + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); + else + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + + mb.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetModifiers(FieldBuilder fb, Modifiers modifiers, bool isInternal) + { + CustomAttributeBuilder customAttributeBuilder; + if (isInternal) + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); + else + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + + fb.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetModifiers(PropertyBuilder pb, Modifiers modifiers, bool isInternal) + { + CustomAttributeBuilder customAttributeBuilder; + if (isInternal) + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); + else + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + + pb.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetModifiers(TypeBuilder tb, Modifiers modifiers, bool isInternal) + { + CustomAttributeBuilder customAttributeBuilder; + if (isInternal) + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); + else + customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + + tb.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetNameSig(MethodBuilder mb, string name, string sig) + { + var customAttributeBuilder = new CustomAttributeBuilder(typeofNameSigAttribute.GetConstructor(new Type[] { Types.String, Types.String }), new object[] { UnicodeUtil.EscapeInvalidSurrogates(name), UnicodeUtil.EscapeInvalidSurrogates(sig) }); + mb.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetInnerClass(TypeBuilder typeBuilder, string innerClass, Modifiers modifiers) + { + var argTypes = new Type[] { Types.String, typeofModifiers }; + var args = new object[] { UnicodeUtil.EscapeInvalidSurrogates(innerClass), modifiers }; + var ci = typeofInnerClassAttribute.GetConstructor(argTypes); + var customAttributeBuilder = new CustomAttributeBuilder(ci, args); + typeBuilder.SetCustomAttribute(customAttributeBuilder); + } + + internal static void SetSourceFile(TypeBuilder typeBuilder, string filename) + { + if (sourceFileAttribute == null) + sourceFileAttribute = typeofSourceFileAttribute.GetConstructor(new Type[] { Types.String }); + + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(sourceFileAttribute, new object[] { filename })); + } + + internal static void SetSourceFile(ModuleBuilder moduleBuilder, string filename) + { + if (sourceFileAttribute == null) + { + sourceFileAttribute = typeofSourceFileAttribute.GetConstructor(new Type[] { Types.String }); + } + moduleBuilder.SetCustomAttribute(new CustomAttributeBuilder(sourceFileAttribute, new object[] { filename })); + } + + internal static void SetLineNumberTable(MethodBuilder mb, IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter writer) + { + object arg; + ConstructorInfo con; + if (writer.Count == 1) + { + if (lineNumberTableAttribute2 == null) + { + lineNumberTableAttribute2 = typeofLineNumberTableAttribute.GetConstructor(new Type[] { Types.UInt16 }); + } + con = lineNumberTableAttribute2; + arg = (ushort)writer.LineNo; + } + else + { + if (lineNumberTableAttribute1 == null) + { + lineNumberTableAttribute1 = typeofLineNumberTableAttribute.GetConstructor(new Type[] { JVM.Import(typeof(byte[])) }); + } + con = lineNumberTableAttribute1; + arg = writer.ToArray(); + } + mb.SetCustomAttribute(new CustomAttributeBuilder(con, new object[] { arg })); + } + + internal static void SetEnclosingMethodAttribute(TypeBuilder tb, string className, string methodName, string methodSig) + { + if (enclosingMethodAttribute == null) + { + enclosingMethodAttribute = typeofEnclosingMethodAttribute.GetConstructor(new Type[] { Types.String, Types.String, Types.String }); + } + tb.SetCustomAttribute(new CustomAttributeBuilder(enclosingMethodAttribute, + new object[] { UnicodeUtil.EscapeInvalidSurrogates(className), UnicodeUtil.EscapeInvalidSurrogates(methodName), UnicodeUtil.EscapeInvalidSurrogates(methodSig) })); + } + + internal static void SetSignatureAttribute(TypeBuilder tb, string signature) + { + signatureAttribute ??= typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); + tb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); + } + + internal static void SetSignatureAttribute(FieldBuilder fb, string signature) + { + signatureAttribute ??= typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); + fb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); + } + + internal static void SetSignatureAttribute(MethodBuilder mb, string signature) + { + signatureAttribute ??= typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); + mb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); + } + + internal static void SetMethodParametersAttribute(MethodBuilder mb, Modifiers[] modifiers) + { + methodParametersAttribute ??= typeofMethodParametersAttribute.GetConstructor(new Type[] { typeofModifiers.MakeArrayType() }); + mb.SetCustomAttribute(new CustomAttributeBuilder(methodParametersAttribute, new object[] { modifiers })); + } + + internal static void SetRuntimeVisibleTypeAnnotationsAttribute(TypeBuilder tb, IReadOnlyList data) + { + var r = new RuntimeVisibleTypeAnnotationsAttributeRecord(data.Select(i => i.Record).ToArray()); + var m = new byte[r.GetSize()]; + var w = new ClassFormatWriter(m); + if (r.TryWrite(ref w) == false) + throw new InternalException(); + + runtimeVisibleTypeAnnotationsAttribute ??= typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); + tb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { m })); + } + + internal static void SetRuntimeVisibleTypeAnnotationsAttribute(FieldBuilder fb, IReadOnlyList data) + { + var r = new RuntimeVisibleTypeAnnotationsAttributeRecord(data.Select(i => i.Record).ToArray()); + var m = new byte[r.GetSize()]; + var w = new ClassFormatWriter(m); + if (r.TryWrite(ref w) == false) + throw new InternalException(); + + runtimeVisibleTypeAnnotationsAttribute ??= typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); + fb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { m })); + } + + internal static void SetRuntimeVisibleTypeAnnotationsAttribute(MethodBuilder mb, IReadOnlyList data) + { + var r = new RuntimeVisibleTypeAnnotationsAttributeRecord(data.Select(i => i.Record).ToArray()); + var m = new byte[r.GetSize()]; + var w = new ClassFormatWriter(m); + if (r.TryWrite(ref w) == false) + throw new InternalException(); + + runtimeVisibleTypeAnnotationsAttribute ??= typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); + mb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { m })); + } + + internal static void SetConstantPoolAttribute(TypeBuilder tb, object[] constantPool) + { + constantPoolAttribute ??= typeofConstantPoolAttribute.GetConstructor(new Type[] { Types.Object.MakeArrayType() }); + tb.SetCustomAttribute(new CustomAttributeBuilder(constantPoolAttribute, new object[] { constantPool })); + } + + internal static void SetParamArrayAttribute(ParameterBuilder pb) + { + paramArrayAttribute ??= new CustomAttributeBuilder(JVM.Import(typeof(ParamArrayAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + pb.SetCustomAttribute(paramArrayAttribute); + } + +#endif // IMPORTER + + internal static NameSigAttribute GetNameSig(MemberInfo member) + { +#if !IMPORTER && !EXPORTER + object[] attr = member.GetCustomAttributes(typeof(NameSigAttribute), false); + return attr.Length == 1 ? (NameSigAttribute)attr[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofNameSigAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new NameSigAttribute((string)args[0].Value, (string)args[1].Value); + } + return null; +#endif + } + + internal static T[] DecodeArray(CustomAttributeTypedArgument arg) + { + IList elems = (IList)arg.Value; + T[] arr = new T[elems.Count]; + for (int i = 0; i < arr.Length; i++) + { + arr[i] = (T)elems[i].Value; + } + return arr; + } + + internal static ImplementsAttribute GetImplements(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attribs = type.GetCustomAttributes(typeof(ImplementsAttribute), false); + return attribs.Length == 1 ? (ImplementsAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofImplementsAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new ImplementsAttribute(DecodeArray(args[0])); + } + return null; +#endif + } + + internal static ThrowsAttribute GetThrows(MethodBase mb) + { +#if !IMPORTER && !EXPORTER + object[] attribs = mb.GetCustomAttributes(typeof(ThrowsAttribute), false); + return attribs.Length == 1 ? (ThrowsAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(mb, typeofThrowsAttribute, false)) + { + IList args = cad.ConstructorArguments; + if (args[0].ArgumentType == Types.String.MakeArrayType()) + { + return new ThrowsAttribute(DecodeArray(args[0])); + } + else if (args[0].ArgumentType == Types.Type.MakeArrayType()) + { + return new ThrowsAttribute(DecodeArray(args[0])); + } + else + { + return new ThrowsAttribute((Type)args[0].Value); + } + } + return null; +#endif + } + + internal static string[] GetNonNestedInnerClasses(Type t) + { +#if !IMPORTER && !EXPORTER + object[] attribs = t.GetCustomAttributes(typeof(NonNestedInnerClassAttribute), false); + string[] classes = new string[attribs.Length]; + for (int i = 0; i < attribs.Length; i++) + { + classes[i] = ((NonNestedInnerClassAttribute)attribs[i]).InnerClassName; + } + return classes; +#else + List list = new List(); + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(t, typeofNonNestedInnerClassAttribute, false)) + { + IList args = cad.ConstructorArguments; + list.Add(UnicodeUtil.UnescapeInvalidSurrogates((string)args[0].Value)); + } + return list.ToArray(); +#endif + } + + internal static string GetNonNestedOuterClasses(Type t) + { +#if !IMPORTER && !EXPORTER + object[] attribs = t.GetCustomAttributes(typeof(NonNestedOuterClassAttribute), false); + return attribs.Length == 1 ? ((NonNestedOuterClassAttribute)attribs[0]).OuterClassName : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(t, typeofNonNestedOuterClassAttribute, false)) + { + IList args = cad.ConstructorArguments; + return UnicodeUtil.UnescapeInvalidSurrogates((string)args[0].Value); + } + return null; +#endif + } + + internal static SignatureAttribute GetSignature(MemberInfo member) + { +#if !IMPORTER && !EXPORTER + object[] attribs = member.GetCustomAttributes(typeof(SignatureAttribute), false); + return attribs.Length == 1 ? (SignatureAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofSignatureAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new SignatureAttribute((string)args[0].Value); + } + return null; +#endif + } + + internal static MethodParametersAttribute GetMethodParameters(MethodBase method) + { +#if !IMPORTER && !EXPORTER + object[] attribs = method.GetCustomAttributes(typeof(MethodParametersAttribute), false); + return attribs.Length == 1 ? (MethodParametersAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(method, typeofMethodParametersAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new MethodParametersAttribute(DecodeArray(args[0])); + } + return null; +#endif + } + + internal static object[] GetConstantPool(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attribs = type.GetCustomAttributes(typeof(ConstantPoolAttribute), false); + return attribs.Length == 1 ? ((ConstantPoolAttribute)attribs[0]).constantPool : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofConstantPoolAttribute, false)) + { + return ConstantPoolAttribute.Decompress(DecodeArray(cad.ConstructorArguments[0])); + } + return null; +#endif + } + + internal static byte[] GetRuntimeVisibleTypeAnnotations(MemberInfo member) + { +#if !IMPORTER && !EXPORTER + object[] attribs = member.GetCustomAttributes(typeof(RuntimeVisibleTypeAnnotationsAttribute), false); + return attribs.Length == 1 ? ((RuntimeVisibleTypeAnnotationsAttribute)attribs[0]).data : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofRuntimeVisibleTypeAnnotationsAttribute, false)) + { + return DecodeArray(cad.ConstructorArguments[0]); + } + return null; +#endif + } + + internal static InnerClassAttribute GetInnerClass(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attribs = type.GetCustomAttributes(typeof(InnerClassAttribute), false); + return attribs.Length == 1 ? (InnerClassAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofInnerClassAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new InnerClassAttribute((string)args[0].Value, (Modifiers)args[1].Value); + } + return null; +#endif + } + + internal static RemappedInterfaceMethodAttribute[] GetRemappedInterfaceMethods(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attr = type.GetCustomAttributes(typeof(RemappedInterfaceMethodAttribute), false); + RemappedInterfaceMethodAttribute[] attr1 = new RemappedInterfaceMethodAttribute[attr.Length]; + Array.Copy(attr, attr1, attr.Length); + return attr1; +#else + List attrs = new List(); + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofRemappedInterfaceMethodAttribute, false)) + { + IList args = cad.ConstructorArguments; + attrs.Add(new RemappedInterfaceMethodAttribute((string)args[0].Value, (string)args[1].Value, DecodeArray(args[2]))); + } + return attrs.ToArray(); +#endif + } + + internal static RemappedTypeAttribute GetRemappedType(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attribs = type.GetCustomAttributes(typeof(RemappedTypeAttribute), false); + return attribs.Length == 1 ? (RemappedTypeAttribute)attribs[0] : null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofRemappedTypeAttribute, false)) + { + IList args = cad.ConstructorArguments; + return new RemappedTypeAttribute((Type)args[0].Value); + } + return null; +#endif + } + + internal static RemappedClassAttribute[] GetRemappedClasses(Assembly coreAssembly) + { +#if !IMPORTER && !EXPORTER + object[] attr = coreAssembly.GetCustomAttributes(typeof(RemappedClassAttribute), false); + RemappedClassAttribute[] attr1 = new RemappedClassAttribute[attr.Length]; + Array.Copy(attr, attr1, attr.Length); + return attr1; +#else + List attrs = new List(); + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(coreAssembly, typeofRemappedClassAttribute, false)) + { + IList args = cad.ConstructorArguments; + attrs.Add(new RemappedClassAttribute((string)args[0].Value, (Type)args[1].Value)); + } + return attrs.ToArray(); +#endif + } + + internal static string GetAnnotationAttributeType(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attr = type.GetCustomAttributes(typeof(AnnotationAttributeAttribute), false); + if (attr.Length == 1) + { + return ((AnnotationAttributeAttribute)attr[0]).AttributeType; + } + return null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofAnnotationAttributeAttribute, false)) + { + return UnicodeUtil.UnescapeInvalidSurrogates((string)cad.ConstructorArguments[0].Value); + } + return null; +#endif + } + + internal static AssemblyName[] GetInternalsVisibleToAttributes(Assembly assembly) + { + List list = new List(); + foreach (CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(assembly)) + { + if (cad.Constructor.DeclaringType == JVM.Import(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute))) + { + try + { + list.Add(new AssemblyName((string)cad.ConstructorArguments[0].Value)); + } + catch + { + // HACK since there is no list of exception that the AssemblyName constructor can throw, we simply catch all + } + } + } + return list.ToArray(); + } + + internal static bool IsJavaModule(Module mod) + { + return mod.IsDefined(typeofJavaModuleAttribute, false); + } + + internal static object[] GetJavaModuleAttributes(Module mod) + { +#if !IMPORTER && !EXPORTER + return mod.GetCustomAttributes(typeofJavaModuleAttribute, false); +#else + List attrs = new List(); + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(mod, typeofJavaModuleAttribute, false)) + { + IList args = cad.ConstructorArguments; + if (args.Count == 0) + { + attrs.Add(new JavaModuleAttribute()); + } + else + { + attrs.Add(new JavaModuleAttribute(DecodeArray(args[0]))); + } + } + return attrs.ToArray(); +#endif + } + + internal static bool IsNoPackagePrefix(Type type) + { + return type.IsDefined(typeofNoPackagePrefixAttribute, false) || type.Assembly.IsDefined(typeofNoPackagePrefixAttribute, false); + } + + internal static bool HasEnclosingMethodAttribute(Type type) + { + return type.IsDefined(typeofEnclosingMethodAttribute, false); + } + + internal static EnclosingMethodAttribute GetEnclosingMethodAttribute(Type type) + { +#if !IMPORTER && !EXPORTER + object[] attr = type.GetCustomAttributes(typeof(EnclosingMethodAttribute), false); + if (attr.Length == 1) + { + return ((EnclosingMethodAttribute)attr[0]).SetClassName(type); + } + return null; +#else + foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofEnclosingMethodAttribute, false)) + { + return new EnclosingMethodAttribute((string)cad.ConstructorArguments[0].Value, (string)cad.ConstructorArguments[1].Value, (string)cad.ConstructorArguments[2].Value).SetClassName(type); + } + return null; +#endif + } + +#if IMPORTER + internal static void SetRemappedClass(AssemblyBuilder assemblyBuilder, string name, Type shadowType) + { + ConstructorInfo remappedClassAttribute = typeofRemappedClassAttribute.GetConstructor(new Type[] { Types.String, Types.Type }); + assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(remappedClassAttribute, new object[] { name, shadowType })); + } + + internal static void SetRemappedType(TypeBuilder typeBuilder, Type shadowType) + { + ConstructorInfo remappedTypeAttribute = typeofRemappedTypeAttribute.GetConstructor(new Type[] { Types.Type }); + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(remappedTypeAttribute, new object[] { shadowType })); + } + + internal static void SetRemappedInterfaceMethod(TypeBuilder typeBuilder, string name, string mappedTo, string[] throws) + { + CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofRemappedInterfaceMethodAttribute.GetConstructor(new Type[] { Types.String, Types.String, Types.String.MakeArrayType() }), new object[] { name, mappedTo, throws }); + typeBuilder.SetCustomAttribute(cab); + } + + internal static void SetExceptionIsUnsafeForMapping(TypeBuilder typeBuilder) + { + CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofExceptionIsUnsafeForMappingAttribute.GetConstructor(Type.EmptyTypes), new object[0]); + typeBuilder.SetCustomAttribute(cab); + } +#endif // IMPORTER + + internal static void SetRuntimeCompatibilityAttribute(AssemblyBuilder assemblyBuilder) + { + Type runtimeCompatibilityAttribute = JVM.Import(typeof(System.Runtime.CompilerServices.RuntimeCompatibilityAttribute)); + assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder( + runtimeCompatibilityAttribute.GetConstructor(Type.EmptyTypes), new object[0], + new PropertyInfo[] { runtimeCompatibilityAttribute.GetProperty("WrapNonExceptionThrows") }, new object[] { true }, + new FieldInfo[0], new object[0])); + } + + internal static void SetInternalsVisibleToAttribute(AssemblyBuilder assemblyBuilder, string assemblyName) + { + Type internalsVisibleToAttribute = JVM.Import(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute)); + CustomAttributeBuilder cab = new CustomAttributeBuilder( + internalsVisibleToAttribute.GetConstructor(new Type[] { Types.String }), new object[] { assemblyName }); + assemblyBuilder.SetCustomAttribute(cab); + } + } + +} diff --git a/src/IKVM.Runtime/Attributes/AnnotationDefaultAttribute.cs b/src/IKVM.Runtime/Attributes/AnnotationDefaultAttribute.cs index e0c54e1f1a..ed432367c9 100644 --- a/src/IKVM.Runtime/Attributes/AnnotationDefaultAttribute.cs +++ b/src/IKVM.Runtime/Attributes/AnnotationDefaultAttribute.cs @@ -25,78 +25,78 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { [AttributeUsage(AttributeTargets.Method)] - public sealed class AnnotationDefaultAttribute : Attribute - { - public const byte TAG_ENUM = (byte)'e'; - public const byte TAG_CLASS = (byte)'c'; - public const byte TAG_ANNOTATION = (byte)'@'; - public const byte TAG_ARRAY = (byte)'['; - public const byte TAG_ERROR = (byte)'?'; - private object defaultValue; + public sealed class AnnotationDefaultAttribute : Attribute + { + public const byte TAG_ENUM = (byte)'e'; + public const byte TAG_CLASS = (byte)'c'; + public const byte TAG_ANNOTATION = (byte)'@'; + public const byte TAG_ARRAY = (byte)'['; + public const byte TAG_ERROR = (byte)'?'; + private object defaultValue; - // element_value encoding: - // primitives: - // boxed values - // string: - // string - // enum: - // new object[] { (byte)'e', "", "" } - // class: - // new object[] { (byte)'c', "" } - // annotation: - // new object[] { (byte)'@', "", ("name", (element_value))* } - // array: - // new object[] { (byte)'[', (element_value)* } - // error: - // new object[] { (byte)'?', "", "" } - public AnnotationDefaultAttribute(object defaultValue) - { - this.defaultValue = Unescape(defaultValue); - } + // element_value encoding: + // primitives: + // boxed values + // string: + // string + // enum: + // new object[] { (byte)'e', "", "" } + // class: + // new object[] { (byte)'c', "" } + // annotation: + // new object[] { (byte)'@', "", ("name", (element_value))* } + // array: + // new object[] { (byte)'[', (element_value)* } + // error: + // new object[] { (byte)'?', "", "" } + public AnnotationDefaultAttribute(object defaultValue) + { + this.defaultValue = Unescape(defaultValue); + } - public object Value - { - get - { - return defaultValue; - } - } + public object Value + { + get + { + return defaultValue; + } + } - internal static object Escape(object obj) - { - return EscapeOrUnescape(obj, true); - } + internal static object Escape(object obj) + { + return EscapeOrUnescape(obj, true); + } - internal static object Unescape(object obj) - { - return EscapeOrUnescape(obj, false); - } + internal static object Unescape(object obj) + { + return EscapeOrUnescape(obj, false); + } + + private static object EscapeOrUnescape(object obj, bool escape) + { + string str = obj as string; + if (str != null) + { + return escape ? UnicodeUtil.EscapeInvalidSurrogates(str) : UnicodeUtil.UnescapeInvalidSurrogates(str); + } + object[] arr = obj as object[]; + if (arr != null) + { + for (int i = 0; i < arr.Length; i++) + { + arr[i] = EscapeOrUnescape(arr[i], escape); + } + } + return obj; + } + + } - private static object EscapeOrUnescape(object obj, bool escape) - { - string str = obj as string; - if (str != null) - { - return escape - ? UnicodeUtil.EscapeInvalidSurrogates(str) - : UnicodeUtil.UnescapeInvalidSurrogates(str); - } - object[] arr = obj as object[]; - if (arr != null) - { - for (int i = 0; i < arr.Length; i++) - { - arr[i] = EscapeOrUnescape(arr[i], escape); - } - } - return obj; - } - } } diff --git a/src/IKVM.Runtime/Attributes/ConstantPoolAttribute.cs b/src/IKVM.Runtime/Attributes/ConstantPoolAttribute.cs index 7ce4641c5f..2b43038bf8 100644 --- a/src/IKVM.Runtime/Attributes/ConstantPoolAttribute.cs +++ b/src/IKVM.Runtime/Attributes/ConstantPoolAttribute.cs @@ -26,7 +26,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/CustomAssemblyClassLoaderAttribute.cs b/src/IKVM.Runtime/Attributes/CustomAssemblyClassLoaderAttribute.cs index 656e34c057..3ae59ca9b2 100644 --- a/src/IKVM.Runtime/Attributes/CustomAssemblyClassLoaderAttribute.cs +++ b/src/IKVM.Runtime/Attributes/CustomAssemblyClassLoaderAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/DynamicAnnotationAttribute.cs b/src/IKVM.Runtime/Attributes/DynamicAnnotationAttribute.cs index d5a04f28a5..f984fdf32a 100644 --- a/src/IKVM.Runtime/Attributes/DynamicAnnotationAttribute.cs +++ b/src/IKVM.Runtime/Attributes/DynamicAnnotationAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/EnclosingMethodAttribute.cs b/src/IKVM.Runtime/Attributes/EnclosingMethodAttribute.cs index cfe378efaa..d45ba2363b 100644 --- a/src/IKVM.Runtime/Attributes/EnclosingMethodAttribute.cs +++ b/src/IKVM.Runtime/Attributes/EnclosingMethodAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/ExceptionIsUnsafeForMappingAttribute.cs b/src/IKVM.Runtime/Attributes/ExceptionIsUnsafeForMappingAttribute.cs index 01616f870b..66c0fa11d6 100644 --- a/src/IKVM.Runtime/Attributes/ExceptionIsUnsafeForMappingAttribute.cs +++ b/src/IKVM.Runtime/Attributes/ExceptionIsUnsafeForMappingAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/GhostInterfaceAttribute.cs b/src/IKVM.Runtime/Attributes/GhostInterfaceAttribute.cs index fdbcd50b46..974a972609 100644 --- a/src/IKVM.Runtime/Attributes/GhostInterfaceAttribute.cs +++ b/src/IKVM.Runtime/Attributes/GhostInterfaceAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/HideFromJavaAttribute.cs b/src/IKVM.Runtime/Attributes/HideFromJavaAttribute.cs index 0335c3507c..e999de0ed6 100644 --- a/src/IKVM.Runtime/Attributes/HideFromJavaAttribute.cs +++ b/src/IKVM.Runtime/Attributes/HideFromJavaAttribute.cs @@ -23,32 +23,42 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { - // Whenever the VM or compiler generates a helper class/method/field, it should be marked - // with this custom attribute, so that it can be hidden from Java. + + /// + /// Whenever the VM or compiler generates a helper class/method/field, it should be marked with this custom + /// attribute, so that it can be hidden from Java. + /// [AttributeUsage(AttributeTargets.All)] public sealed class HideFromJavaAttribute : Attribute { - private readonly HideFromJavaFlags flags; + readonly HideFromJavaFlags flags; + + /// + /// Initializes a new instance. + /// public HideFromJavaAttribute() { flags = HideFromJavaFlags.All; } + /// + /// Initializes a new instance. + /// + /// public HideFromJavaAttribute(HideFromJavaFlags flags) { this.flags = flags; } - public HideFromJavaFlags Flags - { - get { return flags; } - } + public HideFromJavaFlags Flags => flags; + } + } diff --git a/src/IKVM.Runtime/Attributes/HideFromJavaFlags.cs b/src/IKVM.Runtime/Attributes/HideFromJavaFlags.cs index fdc21d7b39..d9e5fabfd4 100644 --- a/src/IKVM.Runtime/Attributes/HideFromJavaFlags.cs +++ b/src/IKVM.Runtime/Attributes/HideFromJavaFlags.cs @@ -23,20 +23,24 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { + [Flags] - public enum HideFromJavaFlags : byte - { - All = Code | Reflection | StackWalk | StackTrace, - None = 0, - Code = 1, - Reflection = 2, - StackWalk = 4, // used for LambdaForm$Compiled - StackTrace = 8, // used for LambdaForm$Hidden - } + public enum HideFromJavaFlags : byte + { + + All = Code | Reflection | StackWalk | StackTrace, + None = 0, + Code = 1, + Reflection = 2, + StackWalk = 4, // used for LambdaForm$Compiled + StackTrace = 8, // used for LambdaForm$Hidden + + } + } diff --git a/src/IKVM.Runtime/Attributes/ImplementsAttribute.cs b/src/IKVM.Runtime/Attributes/ImplementsAttribute.cs index b8feb7862e..e83822a2c4 100644 --- a/src/IKVM.Runtime/Attributes/ImplementsAttribute.cs +++ b/src/IKVM.Runtime/Attributes/ImplementsAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/InnerClassAttribute.cs b/src/IKVM.Runtime/Attributes/InnerClassAttribute.cs index 9f03ad534e..d87c44d9ea 100644 --- a/src/IKVM.Runtime/Attributes/InnerClassAttribute.cs +++ b/src/IKVM.Runtime/Attributes/InnerClassAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/JavaModuleAttribute.cs b/src/IKVM.Runtime/Attributes/JavaModuleAttribute.cs index a3da34895e..d004336a58 100644 --- a/src/IKVM.Runtime/Attributes/JavaModuleAttribute.cs +++ b/src/IKVM.Runtime/Attributes/JavaModuleAttribute.cs @@ -25,36 +25,40 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { + [AttributeUsage(AttributeTargets.Module)] - public sealed class JavaModuleAttribute : Attribute - { - private string[] classMap; - private string[] jars; - - public JavaModuleAttribute() - { - } - - public JavaModuleAttribute(string[] classMap) - { - this.classMap = UnicodeUtil.UnescapeInvalidSurrogates(classMap); - } - - public string[] GetClassMap() - { - return classMap; - } - - public string[] Jars - { - get { return jars; } - set { jars = value; } - } - } + public sealed class JavaModuleAttribute : Attribute + { + private string[] classMap; + private string[] jars; + + public JavaModuleAttribute() + { + + } + + public JavaModuleAttribute(string[] classMap) + { + this.classMap = UnicodeUtil.UnescapeInvalidSurrogates(classMap); + } + + public string[] GetClassMap() + { + return classMap; + } + + public string[] Jars + { + get { return jars; } + set { jars = value; } + } + + } + } diff --git a/src/IKVM.Runtime/Attributes/JavaResourceAttribute.cs b/src/IKVM.Runtime/Attributes/JavaResourceAttribute.cs index 346c28fbd7..e4e0207513 100644 --- a/src/IKVM.Runtime/Attributes/JavaResourceAttribute.cs +++ b/src/IKVM.Runtime/Attributes/JavaResourceAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/LineNumberTableAttribute.cs b/src/IKVM.Runtime/Attributes/LineNumberTableAttribute.cs index d87303781d..b5355db5eb 100644 --- a/src/IKVM.Runtime/Attributes/LineNumberTableAttribute.cs +++ b/src/IKVM.Runtime/Attributes/LineNumberTableAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/MethodParametersAttribute.cs b/src/IKVM.Runtime/Attributes/MethodParametersAttribute.cs index af326a9827..2b513a44b9 100644 --- a/src/IKVM.Runtime/Attributes/MethodParametersAttribute.cs +++ b/src/IKVM.Runtime/Attributes/MethodParametersAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/ModifiersAttribute.cs b/src/IKVM.Runtime/Attributes/ModifiersAttribute.cs index 2ed5677b43..9460ab3308 100644 --- a/src/IKVM.Runtime/Attributes/ModifiersAttribute.cs +++ b/src/IKVM.Runtime/Attributes/ModifiersAttribute.cs @@ -23,43 +23,44 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { - [AttributeUsage(AttributeTargets.All)] + + [AttributeUsage(AttributeTargets.All)] public sealed class ModifiersAttribute : Attribute { - private Modifiers modifiers; - private bool isInternal; + Modifiers modifiers; + bool isInternal; + + /// + /// Initializes a new instance. + /// + /// public ModifiersAttribute(Modifiers modifiers) { this.modifiers = modifiers; } + /// + /// Initializes a new instance. + /// + /// + /// public ModifiersAttribute(Modifiers modifiers, bool isInternal) { this.modifiers = modifiers; this.isInternal = isInternal; } - public bool IsInternal - { - get - { - return isInternal; - } - } + public bool IsInternal => isInternal; + + public Modifiers Modifiers => modifiers; + + } - public Modifiers Modifiers - { - get - { - return modifiers; - } - } - } } diff --git a/src/IKVM.Runtime/Attributes/NameSigAttribute.cs b/src/IKVM.Runtime/Attributes/NameSigAttribute.cs index bc09c877d2..7a995c9ba3 100644 --- a/src/IKVM.Runtime/Attributes/NameSigAttribute.cs +++ b/src/IKVM.Runtime/Attributes/NameSigAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/NoPackagePrefixAttribute.cs b/src/IKVM.Runtime/Attributes/NoPackagePrefixAttribute.cs index 691fa96844..13b6115c4b 100644 --- a/src/IKVM.Runtime/Attributes/NoPackagePrefixAttribute.cs +++ b/src/IKVM.Runtime/Attributes/NoPackagePrefixAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/NonNestedInnerClassAttribute.cs b/src/IKVM.Runtime/Attributes/NonNestedInnerClassAttribute.cs index 08c9fbde7a..1a4b41201a 100644 --- a/src/IKVM.Runtime/Attributes/NonNestedInnerClassAttribute.cs +++ b/src/IKVM.Runtime/Attributes/NonNestedInnerClassAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/NonNestedOuterClassAttribute.cs b/src/IKVM.Runtime/Attributes/NonNestedOuterClassAttribute.cs index bddd18e58c..92cfca60fc 100644 --- a/src/IKVM.Runtime/Attributes/NonNestedOuterClassAttribute.cs +++ b/src/IKVM.Runtime/Attributes/NonNestedOuterClassAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/PackageListAttribute.cs b/src/IKVM.Runtime/Attributes/PackageListAttribute.cs index 3c5bf818b6..fab144d1d0 100644 --- a/src/IKVM.Runtime/Attributes/PackageListAttribute.cs +++ b/src/IKVM.Runtime/Attributes/PackageListAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/RemappedClassAttribute.cs b/src/IKVM.Runtime/Attributes/RemappedClassAttribute.cs index 776459a89f..7df7b78b3a 100644 --- a/src/IKVM.Runtime/Attributes/RemappedClassAttribute.cs +++ b/src/IKVM.Runtime/Attributes/RemappedClassAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif @@ -37,7 +37,7 @@ public sealed class RemappedClassAttribute : Attribute private string name; private Type remappedType; -#if STUB_GENERATOR +#if EXPORTER public RemappedClassAttribute(string name, System.Type remappedType) { diff --git a/src/IKVM.Runtime/Attributes/RemappedInterfaceMethodAttribute.cs b/src/IKVM.Runtime/Attributes/RemappedInterfaceMethodAttribute.cs index 467c76bff4..322ca38ded 100644 --- a/src/IKVM.Runtime/Attributes/RemappedInterfaceMethodAttribute.cs +++ b/src/IKVM.Runtime/Attributes/RemappedInterfaceMethodAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/Attributes/RemappedTypeAttribute.cs b/src/IKVM.Runtime/Attributes/RemappedTypeAttribute.cs index 51cb7363f5..ea7802a026 100644 --- a/src/IKVM.Runtime/Attributes/RemappedTypeAttribute.cs +++ b/src/IKVM.Runtime/Attributes/RemappedTypeAttribute.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif @@ -34,7 +34,7 @@ public sealed class RemappedTypeAttribute : Attribute { private Type type; -#if STUB_GENERATOR +#if EXPORTER public RemappedTypeAttribute(System.Type type) { } diff --git a/src/IKVM.Runtime/Attributes/RuntimeVisibleTypeAnnotationsAttribute.cs b/src/IKVM.Runtime/Attributes/RuntimeVisibleTypeAnnotationsAttribute.cs index 6d382c705d..ba32d02572 100644 --- a/src/IKVM.Runtime/Attributes/RuntimeVisibleTypeAnnotationsAttribute.cs +++ b/src/IKVM.Runtime/Attributes/RuntimeVisibleTypeAnnotationsAttribute.cs @@ -23,20 +23,28 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif namespace IKVM.Attributes { + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field)] - public sealed class RuntimeVisibleTypeAnnotationsAttribute : Attribute - { - internal readonly byte[] data; - - public RuntimeVisibleTypeAnnotationsAttribute(byte[] data) - { - this.data = data; - } - } + public sealed class RuntimeVisibleTypeAnnotationsAttribute : Attribute + { + + internal readonly byte[] data; + + /// + /// Initializes a new instance. + /// + /// + public RuntimeVisibleTypeAnnotationsAttribute(byte[] data) + { + this.data = data; + } + + } + } diff --git a/src/IKVM.Runtime/Attributes/ThrowsAttribute.cs b/src/IKVM.Runtime/Attributes/ThrowsAttribute.cs index 6326551734..d77f4efd81 100644 --- a/src/IKVM.Runtime/Attributes/ThrowsAttribute.cs +++ b/src/IKVM.Runtime/Attributes/ThrowsAttribute.cs @@ -25,7 +25,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif diff --git a/src/IKVM.Runtime/BigEndianBinaryReader.cs b/src/IKVM.Runtime/BigEndianBinaryReader.cs index 8d89f95e38..587c0b0770 100644 --- a/src/IKVM.Runtime/BigEndianBinaryReader.cs +++ b/src/IKVM.Runtime/BigEndianBinaryReader.cs @@ -22,221 +22,217 @@ Jeroen Frijters */ using System; +using System.Text; sealed class BigEndianBinaryReader { - private byte[] buf; - private int pos; - private int end; - - internal BigEndianBinaryReader(byte[] buf, int offset, int length) - { - this.buf = buf; - this.pos = offset; - this.end = checked(offset + length); - if(offset < 0 || length < 0 || buf.Length - offset < length) - { - throw new ClassFormatError("Truncated class file"); - } - } - - internal BigEndianBinaryReader Section(uint length) - { - BigEndianBinaryReader br = new BigEndianBinaryReader(buf, pos, checked((int)length)); - Skip(length); - return br; - } - - internal bool IsAtEnd - { - get - { - return pos == end; - } - } - - internal int Position - { - get - { - return pos; - } - } - - internal void Skip(uint count) - { - if(end - pos < count) - { - throw new ClassFormatError("Truncated class file"); - } - checked - { - pos += (int)count; - } - } - - internal byte ReadByte() - { - if(pos == end) - { - throw new ClassFormatError("Truncated class file"); - } - return buf[pos++]; - } - - internal sbyte ReadSByte() - { - if(pos == end) - { - throw new ClassFormatError("Truncated class file"); - } - return (sbyte)buf[pos++]; - } - - internal double ReadDouble() - { - return BitConverter.Int64BitsToDouble(ReadInt64()); - } - - internal short ReadInt16() - { - if(end - pos < 2) - { - throw new ClassFormatError("Truncated class file"); - } - short s = (short)((buf[pos] << 8) + buf[pos + 1]); - pos += 2; - return s; - } - - internal int ReadInt32() - { - if(end - pos < 4) - { - throw new ClassFormatError("Truncated class file"); - } - int i = (int)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); - pos += 4; - return i; - } - - internal long ReadInt64() - { - if(end - pos < 8) - { - throw new ClassFormatError("Truncated class file"); - } - uint i1 = (uint)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); - uint i2 = (uint)((buf[pos + 4] << 24) + (buf[pos + 5] << 16) + (buf[pos + 6] << 8) + buf[pos + 7]); - long l = (((long)i1) << 32) + i2; - pos += 8; - return l; - } - - internal float ReadSingle() - { - return BitConverter.ToSingle(BitConverter.GetBytes(ReadInt32()), 0); - } - - internal string ReadString(string classFile, int majorVersion) - { - int len = ReadUInt16(); - if(end - pos < len) - { - throw new ClassFormatError("{0} (Truncated class file)", classFile); - } - // special code path for ASCII strings (which occur *very* frequently) - for(int j = 0; j < len; j++) - { - if(buf[pos + j] == 0 || buf[pos + j] >= 128) - { - // NOTE we *cannot* use System.Text.UTF8Encoding, because this is *not* compatible - // (esp. for embedded nulls) - char[] ch = new char[len]; - int l = 0; - for(int i = 0; i < len; i++) - { - int c = buf[pos + i]; - int char2, char3; - switch (c >> 4) - { - case 0: - if(c == 0) - { - goto default; - } - break; - case 1: case 2: case 3: case 4: case 5: case 6: case 7: - // 0xxxxxxx - break; - case 12: case 13: - // 110x xxxx 10xx xxxx - char2 = buf[pos + ++i]; - if((char2 & 0xc0) != 0x80 || i >= len) - { - goto default; - } - c = (((c & 0x1F) << 6) | (char2 & 0x3F)); - if(c < 0x80 && c != 0 && majorVersion >= 48) - { - goto default; - } - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - char2 = buf[pos + ++i]; - char3 = buf[pos + ++i]; - if((char2 & 0xc0) != 0x80 || (char3 & 0xc0) != 0x80 || i >= len) - { - goto default; - } - c = (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); - if(c < 0x800 && majorVersion >= 48) - { - goto default; - } - break; - default: - throw new ClassFormatError("Illegal UTF8 string in constant pool in class file {0}", classFile ?? ""); - } - ch[l++] = (char)c; - } - pos += len; - return new String(ch, 0, l); - } - } - string s = System.Text.ASCIIEncoding.ASCII.GetString(buf, pos, len); - pos += len; - return s; - } - - internal ushort ReadUInt16() - { - if(end - pos < 2) - { - throw new ClassFormatError("Truncated class file"); - } - ushort s = (ushort)((buf[pos] << 8) + buf[pos + 1]); - pos += 2; - return s; - } - - internal uint ReadUInt32() - { - if(end - pos < 4) - { - throw new ClassFormatError("Truncated class file"); - } - uint i = (uint)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); - pos += 4; - return i; - } - - internal byte[] ToArray() - { - byte[] res = new byte[end - pos]; - Buffer.BlockCopy(buf, pos, res, 0, res.Length); - return res; - } + readonly ReadOnlyMemory buf; + + int pos; + + /// + /// Initializes a new instance. + /// + /// + /// + internal BigEndianBinaryReader(ReadOnlyMemory buf) + { + this.buf = buf; + this.pos = 0; + } + + internal BigEndianBinaryReader Section(uint length) + { + var br = new BigEndianBinaryReader(buf.Slice(pos, checked((int)length))); + Skip(length); + return br; + } + + internal bool IsAtEnd => pos == buf.Length; + + internal int Position => pos; + + internal void Skip(uint count) + { + if (buf.Length - pos < count) + throw new ClassFormatError("Truncated class file"); + + checked + { + pos += (int)count; + } + } + + internal byte ReadByte() + { + if (pos == buf.Length) + throw new ClassFormatError("Truncated class file"); + + return buf.Span[pos++]; + } + + internal sbyte ReadSByte() + { + if (pos == buf.Length) + throw new ClassFormatError("Truncated class file"); + + return (sbyte)buf.Span[pos++]; + } + + internal double ReadDouble() + { + return BitConverter.Int64BitsToDouble(ReadInt64()); + } + + internal short ReadInt16() + { + if (buf.Length - pos < 2) + throw new ClassFormatError("Truncated class file"); + + var s = (short)((buf.Span[pos] << 8) + buf.Span[pos + 1]); + pos += 2; + + return s; + } + + internal int ReadInt32() + { + if (buf.Length - pos < 4) + throw new ClassFormatError("Truncated class file"); + + var i = (int)((buf.Span[pos] << 24) + (buf.Span[pos + 1] << 16) + (buf.Span[pos + 2] << 8) + buf.Span[pos + 3]); + pos += 4; + return i; + } + + internal long ReadInt64() + { + if (buf.Length - pos < 8) + throw new ClassFormatError("Truncated class file"); + + var i1 = (uint)((buf.Span[pos] << 24) + (buf.Span[pos + 1] << 16) + (buf.Span[pos + 2] << 8) + buf.Span[pos + 3]); + var i2 = (uint)((buf.Span[pos + 4] << 24) + (buf.Span[pos + 5] << 16) + (buf.Span[pos + 6] << 8) + buf.Span[pos + 7]); + var l = (((long)i1) << 32) + i2; + pos += 8; + return l; + } + + internal float ReadSingle() + { + return BitConverter.ToSingle(BitConverter.GetBytes(ReadInt32()), 0); + } + + internal unsafe string ReadString(string classFile, int majorVersion) + { + int len = ReadUInt16(); + if (buf.Length - pos < len) + throw new ClassFormatError("{0} (Truncated class file)", classFile); + + // special code path for ASCII strings (which occur *very* frequently) + for (int j = 0; j < len; j++) + { + if (buf.Span[pos + j] == 0 || buf.Span[pos + j] >= 128) + { + // NOTE we *cannot* use System.Text.UTF8Encoding, because this is *not* compatible + // (esp. for embedded nulls) + char[] ch = new char[len]; + int l = 0; + for (int i = 0; i < len; i++) + { + int c = buf.Span[pos + i]; + int char2, char3; + switch (c >> 4) + { + case 0: + if (c == 0) + { + goto default; + } + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = buf.Span[pos + ++i]; + if ((char2 & 0xc0) != 0x80 || i >= len) + { + goto default; + } + c = (((c & 0x1F) << 6) | (char2 & 0x3F)); + if (c < 0x80 && c != 0 && majorVersion >= 48) + { + goto default; + } + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = buf.Span[pos + ++i]; + char3 = buf.Span[pos + ++i]; + if ((char2 & 0xc0) != 0x80 || (char3 & 0xc0) != 0x80 || i >= len) + { + goto default; + } + c = (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + if (c < 0x800 && majorVersion >= 48) + { + goto default; + } + break; + default: + throw new ClassFormatError("Illegal UTF8 string in constant pool in class file {0}", classFile ?? ""); + } + ch[l++] = (char)c; + } + pos += len; + return new string(ch, 0, l); + } + } + +#if NETFRAMEWORK + string s; + fixed (byte* p = buf.Slice(pos, len).Span) + s = Encoding.ASCII.GetString(p, buf.Length); +#else + var s = Encoding.ASCII.GetString(buf.Slice(pos, len).Span); +#endif + pos += len; + return s; + } + + internal ushort ReadUInt16() + { + if (buf.Length - pos < 2) + throw new ClassFormatError("Truncated class file"); + + var s = (ushort)((buf.Span[pos] << 8) + buf.Span[pos + 1]); + pos += 2; + return s; + } + + internal uint ReadUInt32() + { + if (buf.Length - pos < 4) + throw new ClassFormatError("Truncated class file"); + + uint i = (uint)((buf.Span[pos] << 24) + (buf.Span[pos + 1] << 16) + (buf.Span[pos + 2] << 8) + buf.Span[pos + 3]); + pos += 4; + return i; + } + + internal byte[] ToArray() + { + var res = new byte[buf.Length - pos]; + buf.Slice(pos, res.Length).CopyTo(res); + return res; + } + } diff --git a/src/IKVM.Runtime/ByteCodeFlags.cs b/src/IKVM.Runtime/ByteCodeFlags.cs index f770efd17f..629bdac36e 100644 --- a/src/IKVM.Runtime/ByteCodeFlags.cs +++ b/src/IKVM.Runtime/ByteCodeFlags.cs @@ -26,7 +26,9 @@ Jeroen Frijters [Flags] enum ByteCodeFlags : byte { - None = 0, - FixedArg = 1, - CannotThrow = 2 + + None = 0, + FixedArg = 1, + CannotThrow = 2 + } diff --git a/src/IKVM.Runtime/ByteCodeHelper.cs b/src/IKVM.Runtime/ByteCodeHelper.cs index 5cf0b63c0a..28948cfe5e 100644 --- a/src/IKVM.Runtime/ByteCodeHelper.cs +++ b/src/IKVM.Runtime/ByteCodeHelper.cs @@ -23,81 +23,48 @@ Jeroen Frijters */ using System; using System.Diagnostics; -using System.Reflection; using System.Runtime.InteropServices; using System.Threading; +using System.Runtime.CompilerServices; using IKVM.Attributes; +using IKVM.ByteCode; using IKVM.Internal; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + +#if EXPORTER == false + using IKVM.Java.Externs.java.lang.invoke; +using IKVM.Runtime.Accessors.Java.Lang; + +#endif namespace IKVM.Runtime { - static class GhostTag + public static class ByteCodeHelper { - private static volatile PassiveWeakDictionary dict; +#if FIRST_PASS == false && EXPORTER == FALSE - internal static void SetTag(object obj, RuntimeTypeHandle typeHandle) - { - SetTag(obj, ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle))); - } - - internal static void SetTag(object obj, TypeWrapper wrapper) - { - if (dict == null) - { - PassiveWeakDictionary newDict = new PassiveWeakDictionary(); -#pragma warning disable 0420 // don't whine about CompareExchange not respecting 'volatile' - if (Interlocked.CompareExchange(ref dict, newDict, null) != null) -#pragma warning restore - { - newDict.Dispose(); - } - } - dict.Add(obj, wrapper); - } - - internal static TypeWrapper GetTag(object obj) - { - if (dict != null) - { - TypeWrapper tw; - dict.TryGetValue(obj, out tw); - return tw; - } - return null; - } + static ObjectAccessor objectAccessor; - // this method is called from .IsInstanceArray() - internal static bool IsGhostArrayInstance(object obj, RuntimeTypeHandle typeHandle, int rank) - { - TypeWrapper tw1 = GhostTag.GetTag(obj); - if (tw1 != null) - { - TypeWrapper tw2 = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle)).MakeArrayType(rank); - return tw1.IsAssignableTo(tw2); - } - return false; - } + static ObjectAccessor ObjectAccessor => JVM.BaseAccessors.Get(ref objectAccessor); - // this method is called from .CastArray() - [HideFromJava] - internal static void ThrowClassCastException(object obj, RuntimeTypeHandle typeHandle, int rank) - { -#if !FIRST_PASS - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - sb.Append(global::ikvm.runtime.Util.getClassFromObject(obj).getName()).Append(" cannot be cast to ") - .Append('[', rank).Append('L').Append(global::ikvm.runtime.Util.getClassFromTypeHandle(typeHandle).getName()).Append(';'); - throw new global::java.lang.ClassCastException(sb.ToString()); #endif - } - } - public static class ByteCodeHelper - { - [DebuggerStepThroughAttribute] +#if EXPORTER == false + + [DebuggerStepThrough] public static object multianewarray(RuntimeTypeHandle typeHandle, int[] lengths) { for (int i = 0; i < lengths.Length; i++) @@ -109,10 +76,12 @@ public static object multianewarray(RuntimeTypeHandle typeHandle, int[] lengths) #endif } } + return MultianewarrayHelper(Type.GetTypeFromHandle(typeHandle).GetElementType(), lengths, 0); } - private static object MultianewarrayHelper(Type elemType, int[] lengths, int index) + + static object MultianewarrayHelper(Type elemType, int[] lengths, int index) { object o = Array.CreateInstance(elemType, lengths[index++]); if (index < lengths.Length) @@ -127,10 +96,10 @@ private static object MultianewarrayHelper(Type elemType, int[] lengths, int ind return o; } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static object multianewarray_ghost(RuntimeTypeHandle typeHandle, int[] lengths) { - Type type = Type.GetTypeFromHandle(typeHandle); + var type = Type.GetTypeFromHandle(typeHandle); int rank = 0; while (ReflectUtil.IsVector(type)) { @@ -142,7 +111,7 @@ public static object multianewarray_ghost(RuntimeTypeHandle typeHandle, int[] le return obj; } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static T[] anewarray_ghost(int length, RuntimeTypeHandle typeHandle) { T[] obj = new T[length]; @@ -150,11 +119,11 @@ public static T[] anewarray_ghost(int length, RuntimeTypeHandle typeHandle) return obj; } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static object DynamicMultianewarray(int[] lengths, global::java.lang.Class clazz) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Profiler.Count("DynamicMultianewarray"); TypeWrapper wrapper = TypeWrapper.FromClass(clazz); @@ -167,11 +136,11 @@ public static object DynamicMultianewarray(int[] lengths, global::java.lang.Clas #endif } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static object DynamicNewarray(int length, global::java.lang.Class clazz) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Profiler.Count("DynamicNewarray"); if (length < 0) @@ -188,31 +157,39 @@ public static object DynamicNewarray(int length, global::java.lang.Class clazz) #endif } - [DebuggerStepThroughAttribute] +#endif + + [DebuggerStepThrough] public static void DynamicAastore(object arrayref, int index, object val) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else Profiler.Count("DynamicAastore"); ((Array)arrayref).SetValue(val, index); #endif } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static object DynamicAaload(object arrayref, int index) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Profiler.Count("DynamicAaload"); return ((Array)arrayref).GetValue(index); #endif } +#if EXPORTER == false + // the sole purpose of this method is to check whether the clazz can be instantiated (but not to actually do it) [DebuggerStepThroughAttribute] public static void DynamicNewCheckOnly(global::java.lang.Class clazz) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else Profiler.Count("DynamicNewCheckOnly"); TypeWrapper wrapper = TypeWrapper.FromClass(clazz); if (wrapper.IsAbstract) @@ -226,7 +203,7 @@ public static void DynamicNewCheckOnly(global::java.lang.Class clazz) private static TypeWrapper LoadTypeWrapper(string clazz, ikvm.@internal.CallerID callerId) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else try { @@ -235,7 +212,9 @@ private static TypeWrapper LoadTypeWrapper(string clazz, ikvm.@internal.CallerID global::java.lang.ClassLoader loader = callerId.getCallerClassLoader(); if (loader != null) { - loader.checkPackageAccess(wrapper.ClassObject, callerId.getCallerClass().pd); + ClassLoaderAccessor cla = null; + JVM.BaseAccessors.Get(ref cla); + cla.InvokeCheckPackageAccess(loader, wrapper.ClassObject, callerId.getCallerClass().pd); } if (!wrapper.IsAccessibleFrom(context)) { @@ -255,7 +234,7 @@ private static TypeWrapper LoadTypeWrapper(string clazz, ikvm.@internal.CallerID public static global::java.lang.Class DynamicClassLiteral(string clazz, ikvm.@internal.CallerID callerId) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Profiler.Count("DynamicClassLiteral"); return LoadTypeWrapper(clazz, callerId).ClassObject; @@ -266,7 +245,7 @@ private static TypeWrapper LoadTypeWrapper(string clazz, ikvm.@internal.CallerID public static object DynamicCast(object obj, global::java.lang.Class clazz) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Debug.Assert(obj != null); Profiler.Count("DynamicCast"); @@ -282,7 +261,7 @@ public static object DynamicCast(object obj, global::java.lang.Class clazz) public static bool DynamicInstanceOf(object obj, global::java.lang.Class clazz) { #if FIRST_PASS - return false; + throw new NotImplementedException(); #else Debug.Assert(obj != null); Profiler.Count("DynamicInstanceOf"); @@ -311,12 +290,15 @@ public static bool DynamicInstanceOf(object obj, global::java.lang.Class clazz) { DynamicLoadMethodTypeImpl(ref cache, sig, callerID); } + return cache; } private static void DynamicLoadMethodTypeImpl(ref global::java.lang.invoke.MethodType cache, string sig, global::ikvm.@internal.CallerID callerID) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else try { ClassLoaderWrapper loader = ClassLoaderWrapper.FromCallerID(callerID); @@ -342,30 +324,31 @@ private static void DynamicLoadMethodTypeImpl(ref global::java.lang.invoke.Metho { Interlocked.CompareExchange(ref cache, DynamicLoadMethodHandleImpl(kind, clazz, name, sig, callerID), null); } + return cache; } private static global::java.lang.invoke.MethodHandle DynamicLoadMethodHandleImpl(int kind, string clazz, string name, string sig, ikvm.@internal.CallerID callerID) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else global::java.lang.Class refc = LoadTypeWrapper(clazz, callerID).ClassObject; try { - switch ((ClassFile.RefKind)kind) + switch ((ReferenceKind)kind) { - case ClassFile.RefKind.getStatic: - case ClassFile.RefKind.putStatic: - case ClassFile.RefKind.getField: - case ClassFile.RefKind.putField: + case ReferenceKind.GetStatic: + case ReferenceKind.PutStatic: + case ReferenceKind.GetField: + case ReferenceKind.PutField: global::java.lang.Class type = ClassLoaderWrapper.FromCallerID(callerID).FieldTypeWrapperFromSig(sig, LoadMode.LoadOrThrow).ClassObject; return global::java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(callerID.getCallerClass(), kind, refc, name, type); default: global::java.lang.invoke.MethodType mt = null; DynamicLoadMethodType(ref mt, sig, callerID); // HACK linkMethodHandleConstant is broken for MethodHandle.invoke[Exact] - if (kind == (int)ClassFile.RefKind.invokeVirtual && refc == CoreClasses.java.lang.invoke.MethodHandle.Wrapper.ClassObject) + if (kind == (int)ReferenceKind.InvokeVirtual && refc == CoreClasses.java.lang.invoke.MethodHandle.Wrapper.ClassObject) { switch (name) { @@ -390,7 +373,7 @@ public static T DynamicBinderMemberLookup(int kind, string clazz, string name where T : class /* delegate */ { #if FIRST_PASS - return null; + return null; #else try { @@ -420,7 +403,7 @@ public static T DynamicBinderMemberLookup(int kind, string clazz, string name public static Delegate DynamicCreateDelegate(object obj, Type delegateType, string name, string sig) { #if FIRST_PASS - return null; + return null; #else TypeWrapper tw = TypeWrapper.FromClass(global::ikvm.runtime.Util.getClassFromObject(obj)); MethodWrapper mw = tw.GetMethodWrapper(name, sig, true); @@ -479,7 +462,7 @@ public static ikvm.@internal.CallerID DynamicCallerID(object capability) public static global::java.lang.invoke.MethodHandle DynamicEraseInvokeExact(global::java.lang.invoke.MethodHandle mh, global::java.lang.invoke.MethodType expected, global::java.lang.invoke.MethodType target) { #if FIRST_PASS - return null; + return null; #else if (mh.type() != expected) { @@ -489,7 +472,9 @@ public static ikvm.@internal.CallerID DynamicCallerID(object capability) #endif } - [DebuggerStepThroughAttribute] +#endif + + [DebuggerStepThrough] public static int f2i(float f) { if (f > int.MinValue && f < int.MaxValue) @@ -561,14 +546,16 @@ public static long d2l(double d) return 0; } +#if EXPORTER == false + // This is used by static JNI and synchronized methods that need a class object - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static object GetClassFromTypeHandle(RuntimeTypeHandle typeHandle) { return IKVM.Java.Externs.ikvm.runtime.Util.getClassFromTypeHandle(typeHandle); } - [DebuggerStepThroughAttribute] + [DebuggerStepThrough] public static void arraycopy(object src, int srcStart, object dest, int destStart, int len) { #if !FIRST_PASS @@ -660,7 +647,7 @@ public static void arraycopy_fast(Array src, int srcStart, Array dest, int destS { throw new global::java.lang.ArrayIndexOutOfBoundsException(); } -#endif // !FIRST_PASS +#endif } [DebuggerStepThroughAttribute] @@ -797,77 +784,276 @@ public static void VerboseCastFailure(RuntimeTypeHandle typeHandle, object obj) #endif // !FIRST_PASS } + /// + /// Returns true if the Java finalizer should be skipped. + /// + /// public static bool SkipFinalizer() { #if FIRST_PASS - return false; + throw new NotImplementedException(); #else return Environment.HasShutdownStarted && !global::java.lang.Shutdown.runFinalizersOnExit; #endif } - public static long VolatileRead(ref long v) + /// + /// Returns true if the Java finalizer for the given object should be skipped. + /// + /// + /// + public static bool SkipFinalizer(object o) { -#if NO_REF_EMIT && !FIRST_PASS - lock (VolatileLongDoubleFieldWrapper.lockObject) - { - return v; - } +#if FIRST_PASS + throw new NotImplementedException(); #else - return Interlocked.Read(ref v); + if (SkipFinalizer()) + return true; + + if (o is global::java.lang.Object jlo) + if (ObjectAccessor.GetInit(jlo) == false) + return true; + + return false; #endif } - public static void VolatileWrite(ref long v, long newValue) - { -#if NO_REF_EMIT && !FIRST_PASS - lock (VolatileLongDoubleFieldWrapper.lockObject) - { - v = newValue; - } -#else - Interlocked.Exchange(ref v, newValue); #endif + + /// + /// Implements a volatile read against a reference to a bool. In Java, a volatile operation is atomic. + /// + /// + /// + public static bool VolatileRead(ref bool location) + { + return Volatile.Read(ref location); } - public static double VolatileRead(ref double v) + /// + /// Implements a volatile write against a reference to a bool. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref bool location, bool value) { -#if NO_REF_EMIT && !FIRST_PASS - lock (VolatileLongDoubleFieldWrapper.lockObject) - { - return v; - } -#else - return Interlocked.CompareExchange(ref v, 0.0, 0.0); -#endif + Volatile.Write(ref location, value); } - public static void VolatileWrite(ref double v, double newValue) + /// + /// Implements a volatile read against a reference to a byte. In Java, a volatile operation is atomic. + /// + /// + /// + public static byte VolatileRead(ref byte location) { -#if NO_REF_EMIT && !FIRST_PASS - lock (VolatileLongDoubleFieldWrapper.lockObject) - { - v = newValue; - } -#else - Interlocked.Exchange(ref v, newValue); -#endif + return Volatile.Read(ref location); + } + + /// + /// Implements a volatile write against a reference to a byte. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref byte location, byte value) + { + Volatile.Write(ref location, value); + } + + /// + /// Implements a volatile read against a reference to a char. In Java, a volatile operation is atomic. + /// + /// + /// + public static char VolatileRead(ref char location) + { + return (char)Volatile.Read(ref Unsafe.As(ref location)); + } + + /// + /// Implements a volatile write against a reference to a char. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref char location, char value) + { + Volatile.Write(ref Unsafe.As(ref location), (short)value); + } + + /// + /// Implements a volatile read against a reference to a short. In Java, a volatile operation is atomic. + /// + /// + /// + public static short VolatileRead(ref short location) + { + return Volatile.Read(ref location); } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + /// + /// Implements a volatile write against a reference to a short. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref short location, short value) + { + Volatile.Write(ref location, value); + } + + /// + /// Implements a volatile read against a reference to a int. In Java, a volatile operation is atomic. + /// + /// + /// + public static int VolatileRead(ref int location) + { + return Volatile.Read(ref location); + } + + /// + /// Implements a volatile write against a reference to a int. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref int location, int value) + { + Volatile.Write(ref location, value); + } + + /// + /// Implements a volatile read against a reference to a long. In Java, a volatile operation is atomic. + /// + /// + /// + public static long VolatileRead(ref long location) + { + return Interlocked.CompareExchange(ref location, 0, 0); + } + + /// + /// Implements a volatile write against a reference to a long. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref long location, long value) + { + Interlocked.Exchange(ref location, value); + } + + /// + /// Implements a volatile read against a reference to a float. In Java, a volatile operation is atomic. + /// + /// + /// + public static float VolatileRead(ref float location) + { + return Volatile.Read(ref location); + } + + /// + /// Implements a volatile write against a reference to a float. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref float location, float value) + { + Volatile.Write(ref location, value); + } + + /// + /// Implements a volatile read against a reference to a double. In Java, a volatile operation is atomic. + /// + /// + /// + public static double VolatileRead(ref double location) + { + return Interlocked.CompareExchange(ref location, 0, 0); + } + + /// + /// Implements a volatile write against a reference to a double. In Java, a volatile operation is atomic. + /// + /// + /// + public static void VolatileWrite(ref double location, double value) + { + Interlocked.Exchange(ref location, value); + } + + /// + /// Implements the compare and swap operation against a reference to an object. + /// + /// + /// + /// + /// + public static bool CompareAndSwap(ref object location, object expected, object value) + { + return Interlocked.CompareExchange(ref location, value, expected) == expected; + } + + /// + /// Implements the compare and swap operation against a reference to an int. + /// + /// + /// + /// + /// + public static bool CompareAndSwap(ref int location, int expected, int value) + { + return Interlocked.CompareExchange(ref location, value, expected) == expected; + } + + /// + /// Implements a compare and swap operation against a reference to a long. + /// + /// + /// + /// + /// + public static bool CompareAndSwap(ref long location, long expected, long value) + { + return Interlocked.CompareExchange(ref location, value, expected) == expected; + } + + /// + /// Implements a compare and swap operation against a reference to a double. + /// + /// + /// + /// + /// + public static bool CompareAndSwap(ref double location, double expected, double value) + { + return Interlocked.CompareExchange(ref location, value, expected) == expected; + } + +#if EXPORTER == false + + /// + /// Invoked by exported assemblies. + /// + /// + /// + [MethodImpl(MethodImplOptions.NoInlining)] public static void InitializeModule(Module module) { - Assembly asm = Assembly.GetCallingAssembly(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var asm = Assembly.GetCallingAssembly(); if (module.Assembly != asm) - { throw new ArgumentOutOfRangeException(); - } - object classLoader = AssemblyClassLoader.FromAssembly(asm).GetJavaClassLoader(); - Action init = (Action)Delegate.CreateDelegate(typeof(Action), classLoader, "InitializeModule", false, false); - if (init != null) + + // check for InitializeModule method present on classloader + var classLoader = AssemblyClassLoader.FromAssembly(asm).GetJavaClassLoader(); + if (classLoader != null) { - init(module); + var init = (Action)Delegate.CreateDelegate(typeof(Action), classLoader, "InitializeModule", false, false); + if (init != null) + init(module); } +#endif } public static T GetDotNetEnumField(string name) @@ -876,7 +1062,7 @@ public static T GetDotNetEnumField(string name) #endif { #if FIRST_PASS - return default(T); + throw new NotImplementedException(); #else try { @@ -907,7 +1093,7 @@ public static T MapException(Exception x, MapFlags mode) where T : Exception public static Exception DynamicMapException(Exception x, MapFlags mode, global::java.lang.Class exceptionClass) { #if FIRST_PASS - return null; + return null; #else TypeWrapper exceptionTypeWrapper = TypeWrapper.FromClass(exceptionClass); mode &= ~MapFlags.NoRemapping; @@ -924,7 +1110,7 @@ public static T GetDelegateForInvokeExact(global::java.lang.invoke.MethodHand where T : class { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else T del = h._invokeExactDelegate as T; if (del == null) @@ -939,7 +1125,7 @@ public static T GetDelegateForInvoke(global::java.lang.invoke.MethodHandle h, where T : class { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else T del; if (cache.Type == h.type() && (del = (h.isVarargsCollector() ? cache.varArg : cache.fixedArg)) != null) @@ -981,7 +1167,7 @@ public static T GetDelegateForInvokeBasic(global::java.lang.invoke.MethodHand where T : class { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else T del = h.form.vmentry.vmtarget as T; if (del == null) @@ -996,7 +1182,7 @@ public static T GetDelegateForInvokeBasic(global::java.lang.invoke.MethodHand where T : class // Delegate { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else return MethodHandleUtil.GetDelegateMethodType(typeof(T)); #endif @@ -1097,15 +1283,21 @@ public static void DynamicLinkIndyCallSite(ref IndyCallSite site, global:: } #endif } + +#endif + } +#if EXPORTER == false + interface IIndyCallSite { + void SetTarget(global::java.lang.invoke.MethodHandle mh); + } - public sealed class IndyCallSite - : IIndyCallSite + public sealed class IndyCallSite : IIndyCallSite where T : class // Delegate { internal readonly bool IsBootstrap; @@ -1137,12 +1329,18 @@ public T GetTarget() { return target; } + } public struct InvokeCache where T : class { + + internal T fixedArg; + internal T varArg; + #if CLASSGC + private WeakReference weakRef; internal global::java.lang.invoke.MethodType Type @@ -1171,11 +1369,13 @@ internal bool TrySetType(global::java.lang.invoke.MethodType newType) Interlocked.CompareExchange(ref type, newType, null); return type == newType; } + #endif - internal T fixedArg; - internal T varArg; + } +#endif + [StructLayout(LayoutKind.Explicit)] public struct DoubleConverter { @@ -1264,6 +1464,9 @@ public MHA(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) public static class LiveObjectHolder { + public static object[] values; + } + } diff --git a/src/IKVM.Runtime/ByteCodeHelperMethods.cs b/src/IKVM.Runtime/ByteCodeHelperMethods.cs new file mode 100644 index 0000000000..83ea5cf229 --- /dev/null +++ b/src/IKVM.Runtime/ByteCodeHelperMethods.cs @@ -0,0 +1,192 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +using IKVM.Internal; + +#if IMPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + + +namespace IKVM.Runtime +{ + +#if EXPORTER == FALSE + + static class ByteCodeHelperMethods + { + + internal static readonly MethodInfo multianewarray; + internal static readonly MethodInfo multianewarray_ghost; + internal static readonly MethodInfo anewarray_ghost; + internal static readonly MethodInfo f2i; + internal static readonly MethodInfo d2i; + internal static readonly MethodInfo f2l; + internal static readonly MethodInfo d2l; + internal static readonly MethodInfo arraycopy_fast; + internal static readonly MethodInfo arraycopy_primitive_8; + internal static readonly MethodInfo arraycopy_primitive_4; + internal static readonly MethodInfo arraycopy_primitive_2; + internal static readonly MethodInfo arraycopy_primitive_1; + internal static readonly MethodInfo arraycopy; + internal static readonly MethodInfo DynamicCast; + internal static readonly MethodInfo DynamicAaload; + internal static readonly MethodInfo DynamicAastore; + internal static readonly MethodInfo DynamicClassLiteral; + internal static readonly MethodInfo DynamicMultianewarray; + internal static readonly MethodInfo DynamicNewarray; + internal static readonly MethodInfo DynamicNewCheckOnly; + internal static readonly MethodInfo DynamicCreateDelegate; + internal static readonly MethodInfo DynamicLoadMethodType; + internal static readonly MethodInfo DynamicLoadMethodHandle; + internal static readonly MethodInfo DynamicBinderMemberLookup; + internal static readonly MethodInfo DynamicMapException; + internal static readonly MethodInfo DynamicCallerID; + internal static readonly MethodInfo DynamicLinkIndyCallSite; + internal static readonly MethodInfo DynamicEraseInvokeExact; + internal static readonly MethodInfo VerboseCastFailure; + internal static readonly MethodInfo SkipFinalizer; + internal static readonly MethodInfo SkipFinalizerOf; + internal static readonly MethodInfo DynamicInstanceOf; + internal static readonly MethodInfo VolatileReadBoolean; + internal static readonly MethodInfo VolatileReadByte; + internal static readonly MethodInfo VolatileReadChar; + internal static readonly MethodInfo VolatileReadShort; + internal static readonly MethodInfo VolatileReadInt; + internal static readonly MethodInfo VolatileReadLong; + internal static readonly MethodInfo VolatileReadFloat; + internal static readonly MethodInfo VolatileReadDouble; + internal static readonly MethodInfo VolatileWriteBoolean; + internal static readonly MethodInfo VolatileWriteByte; + internal static readonly MethodInfo VolatileWriteChar; + internal static readonly MethodInfo VolatileWriteShort; + internal static readonly MethodInfo VolatileWriteInt; + internal static readonly MethodInfo VolatileWriteLong; + internal static readonly MethodInfo VolatileWriteFloat; + internal static readonly MethodInfo VolatileWriteDouble; + internal static readonly MethodInfo CompareAndSwapObject; + internal static readonly MethodInfo CompareAndSwapInt; + internal static readonly MethodInfo CompareAndSwapLong; + internal static readonly MethodInfo CompareAndSwapDouble; + internal static readonly MethodInfo mapException; + internal static readonly MethodInfo GetDelegateForInvokeExact; + internal static readonly MethodInfo GetDelegateForInvoke; + internal static readonly MethodInfo GetDelegateForInvokeBasic; + internal static readonly MethodInfo LoadMethodType; + internal static readonly MethodInfo LinkIndyCallSite; + + static ByteCodeHelperMethods() + { +#if IMPORTER + var typeofByteCodeHelper = StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper"); +#else + var typeofByteCodeHelper = typeof(IKVM.Runtime.ByteCodeHelper); +#endif + multianewarray = GetHelper(typeofByteCodeHelper, "multianewarray"); + multianewarray_ghost = GetHelper(typeofByteCodeHelper, "multianewarray_ghost"); + anewarray_ghost = GetHelper(typeofByteCodeHelper, "anewarray_ghost"); + f2i = GetHelper(typeofByteCodeHelper, "f2i"); + d2i = GetHelper(typeofByteCodeHelper, "d2i"); + f2l = GetHelper(typeofByteCodeHelper, "f2l"); + d2l = GetHelper(typeofByteCodeHelper, "d2l"); + arraycopy_fast = GetHelper(typeofByteCodeHelper, "arraycopy_fast"); + arraycopy_primitive_8 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_8"); + arraycopy_primitive_4 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_4"); + arraycopy_primitive_2 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_2"); + arraycopy_primitive_1 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_1"); + arraycopy = GetHelper(typeofByteCodeHelper, "arraycopy"); + DynamicCast = GetHelper(typeofByteCodeHelper, "DynamicCast"); + DynamicAaload = GetHelper(typeofByteCodeHelper, "DynamicAaload"); + DynamicAastore = GetHelper(typeofByteCodeHelper, "DynamicAastore"); + DynamicClassLiteral = GetHelper(typeofByteCodeHelper, "DynamicClassLiteral"); + DynamicMultianewarray = GetHelper(typeofByteCodeHelper, "DynamicMultianewarray"); + DynamicNewarray = GetHelper(typeofByteCodeHelper, "DynamicNewarray"); + DynamicNewCheckOnly = GetHelper(typeofByteCodeHelper, "DynamicNewCheckOnly"); + DynamicCreateDelegate = GetHelper(typeofByteCodeHelper, "DynamicCreateDelegate"); + DynamicLoadMethodType = GetHelper(typeofByteCodeHelper, "DynamicLoadMethodType"); + DynamicLoadMethodHandle = GetHelper(typeofByteCodeHelper, "DynamicLoadMethodHandle"); + DynamicBinderMemberLookup = GetHelper(typeofByteCodeHelper, "DynamicBinderMemberLookup"); + DynamicMapException = GetHelper(typeofByteCodeHelper, "DynamicMapException"); + DynamicCallerID = GetHelper(typeofByteCodeHelper, "DynamicCallerID"); + DynamicLinkIndyCallSite = GetHelper(typeofByteCodeHelper, "DynamicLinkIndyCallSite"); + DynamicEraseInvokeExact = GetHelper(typeofByteCodeHelper, "DynamicEraseInvokeExact"); + VerboseCastFailure = GetHelper(typeofByteCodeHelper, "VerboseCastFailure"); + SkipFinalizer = GetHelper(typeofByteCodeHelper, "SkipFinalizer", new Type[] { }); + SkipFinalizerOf = GetHelper(typeofByteCodeHelper, "SkipFinalizer", new Type[] { Types.Object }); + DynamicInstanceOf = GetHelper(typeofByteCodeHelper, "DynamicInstanceOf"); + VolatileReadBoolean = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Boolean.MakeByRefType() }); + VolatileReadByte = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Byte.MakeByRefType() }); + VolatileReadChar = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Char.MakeByRefType() }); + VolatileReadShort = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Int16.MakeByRefType() }); + VolatileReadInt = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Int32.MakeByRefType() }); + VolatileReadLong = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Int64.MakeByRefType() }); + VolatileReadFloat = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Single.MakeByRefType() }); + VolatileReadDouble = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Double.MakeByRefType() }); + VolatileWriteBoolean = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Boolean.MakeByRefType(), Types.Boolean }); + VolatileWriteByte = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Byte.MakeByRefType(), Types.Byte }); + VolatileWriteChar = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Char.MakeByRefType(), Types.Char }); + VolatileWriteShort = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Int16.MakeByRefType(), Types.Int16 }); + VolatileWriteInt = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Int32.MakeByRefType(), Types.Int32 }); + VolatileWriteLong = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Int64.MakeByRefType(), Types.Int64 }); + VolatileWriteFloat = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Single.MakeByRefType(), Types.Single }); + VolatileWriteDouble = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Double.MakeByRefType(), Types.Double }); + CompareAndSwapObject = GetHelper(typeofByteCodeHelper, "CompareAndSwap", new[] { Types.Object.MakeByRefType(), Types.Object, Types.Object }); + CompareAndSwapInt = GetHelper(typeofByteCodeHelper, "CompareAndSwap", new[] { Types.Int32.MakeByRefType(), Types.Int32, Types.Int32 }); + CompareAndSwapLong = GetHelper(typeofByteCodeHelper, "CompareAndSwap", new[] { Types.Int64.MakeByRefType(), Types.Int64, Types.Int64 }); + CompareAndSwapDouble = GetHelper(typeofByteCodeHelper, "CompareAndSwap", new[] { Types.Double.MakeByRefType(), Types.Double, Types.Double }); + mapException = GetHelper(typeofByteCodeHelper, "MapException"); + GetDelegateForInvokeExact = GetHelper(typeofByteCodeHelper, "GetDelegateForInvokeExact"); + GetDelegateForInvoke = GetHelper(typeofByteCodeHelper, "GetDelegateForInvoke"); + GetDelegateForInvokeBasic = GetHelper(typeofByteCodeHelper, "GetDelegateForInvokeBasic"); + LoadMethodType = GetHelper(typeofByteCodeHelper, "LoadMethodType"); + LinkIndyCallSite = GetHelper(typeofByteCodeHelper, "LinkIndyCallSite"); + } + + static MethodInfo GetHelper(Type type, string method) + { + return GetHelper(type, method, null); + } + + static MethodInfo GetHelper(Type type, string method, Type[] parameters) + { + var mi = parameters == null ? type.GetMethod(method) : type.GetMethod(method, parameters); +#if IMPORTER + if (mi == null) + throw new FatalCompilerErrorException(Message.RuntimeMethodMissing, method); +#endif + return mi; + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/ClassFile/ByteCode.cs b/src/IKVM.Runtime/ClassFile/ByteCode.cs index 6cdf3053dd..bd8358bd2d 100644 --- a/src/IKVM.Runtime/ClassFile/ByteCode.cs +++ b/src/IKVM.Runtime/ClassFile/ByteCode.cs @@ -227,4 +227,5 @@ enum ByteCode : byte __ifnonnull = 199, __goto_w = 200, __jsr_w = 201 + } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.BootstrapMethod.cs b/src/IKVM.Runtime/ClassFile/ClassFile.BootstrapMethod.cs index 3665e643c1..cc76bb6d7c 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.BootstrapMethod.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.BootstrapMethod.cs @@ -26,33 +26,33 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal struct BootstrapMethod - { - private ushort bsm_index; - private ushort[] args; - - internal BootstrapMethod(ushort bsm_index, ushort[] args) - { - this.bsm_index = bsm_index; - this.args = args; - } - - internal int BootstrapMethodIndex - { - get { return bsm_index; } - } - - internal int ArgumentCount - { - get { return args.Length; } - } - - internal int GetArgument(int index) - { - return args[index]; - } - } - } + { + private ushort bsm_index; + private ushort[] args; + + internal BootstrapMethod(ushort bsm_index, ushort[] args) + { + this.bsm_index = bsm_index; + this.args = args; + } + + internal int BootstrapMethodIndex + { + get { return bsm_index; } + } + + internal int ArgumentCount + { + get { return args.Length; } + } + + internal int GetArgument(int index) + { + return args[index]; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Constant.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Constant.cs index 49e3b1e88a..dee3c30377 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Constant.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Constant.cs @@ -26,24 +26,24 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal enum Constant - { - Utf8 = 1, - Integer = 3, - Float = 4, - Long = 5, - Double = 6, - Class = 7, - String = 8, - Fieldref = 9, - Methodref = 10, - InterfaceMethodref = 11, - NameAndType = 12, - MethodHandle = 15, - MethodType = 16, - InvokeDynamic = 18, - } - } + { + Utf8 = 1, + Integer = 3, + Float = 4, + Long = 5, + Double = 6, + Class = 7, + String = 8, + Fieldref = 9, + Methodref = 10, + InterfaceMethodref = 11, + NameAndType = 12, + MethodHandle = 15, + MethodType = 16, + InvokeDynamic = 18, + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItem.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItem.cs index 0f398b10d2..d63c7e087a 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItem.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItem.cs @@ -27,33 +27,33 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal abstract class ConstantPoolItem - { - internal virtual void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - } - - internal virtual void Link(TypeWrapper thisType, LoadMode mode) - { - } - - internal virtual ConstantType GetConstantType() - { - throw new InvalidOperationException(); - } - - internal virtual void MarkLinkRequired() - { - } - - // this is used for sun.reflect.ConstantPool - // it returns a boxed System.Int32, System.Int64, System.Float, System.Double or a string - internal virtual object GetRuntimeValue() - { - return null; - } - } - } + { + internal virtual void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + } + + internal virtual void Link(TypeWrapper thisType, LoadMode mode) + { + } + + internal virtual ConstantType GetConstantType() + { + throw new InvalidOperationException(); + } + + internal virtual void MarkLinkRequired() + { + } + + // this is used for sun.reflect.ConstantPool + // it returns a boxed System.Int32, System.Int64, System.Float, System.Double or a string + internal virtual object GetRuntimeValue() + { + return null; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemClass.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemClass.cs index 3d54440862..0a825a685a 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemClass.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemClass.cs @@ -23,178 +23,195 @@ Jeroen Frijters */ using System; +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemClass : ConstantPoolItem, IEquatable - { - private ushort name_index; - private string name; - private TypeWrapper typeWrapper; - private static char[] invalidJava15Characters = { '.', ';', '[', ']' }; - - internal ConstantPoolItemClass(BigEndianBinaryReader br) - { - name_index = br.ReadUInt16(); - } - - internal ConstantPoolItemClass(string name, TypeWrapper typeWrapper) - { - this.name = name; - this.typeWrapper = typeWrapper; - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - // if the item was patched, we already have a name - if(name != null) - { - return; - } - name = classFile.GetConstantPoolUtf8String(utf8_cp, name_index); - if(name.Length > 0) - { - // We don't enforce the strict class name rules in the static compiler, since HotSpot doesn't enforce *any* rules on - // class names for the system (and boot) class loader. We still need to enforce the 1.5 restrictions, because we - // rely on those invariants. -#if !STATIC_COMPILER - if(classFile.MajorVersion < 49 && (options & ClassFileParseOptions.RelaxedClassNameValidation) == 0) - { - char prev = name[0]; - if(Char.IsLetter(prev) || prev == '$' || prev == '_' || prev == '[' || prev == '/') - { - int skip = 1; - int end = name.Length; - if(prev == '[') - { - if(!IsValidFieldSig(name)) - { - goto barf; - } - while(name[skip] == '[') - { - skip++; - } - if(name.EndsWith(";")) - { - end--; - } - } - for(int i = skip; i < end; i++) - { - char c = name[i]; - if(!Char.IsLetterOrDigit(c) && c != '$' && c != '_' && (c != '/' || prev == '/')) - { - goto barf; - } - prev = c; - } - name = String.Intern(name.Replace('/', '.')); - return; - } - } - else + { + + static readonly char[] invalidJava15Characters = { '.', ';', '[', ']' }; + + readonly ClassConstantReader reader; + + string name; + TypeWrapper typeWrapper; + + /// + /// Initializes a new instance. + /// + /// + /// + internal ConstantPoolItemClass(ClassConstantReader reader) + { + this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); + } + + /// + /// Initializes a new instance. + /// + /// + /// + internal ConstantPoolItemClass(string name, TypeWrapper typeWrapper) + { + this.name = name; + this.typeWrapper = typeWrapper; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + // if the item was patched, we already have a name + if (name != null) + return; + + name = classFile.GetConstantPoolUtf8String(utf8_cp, reader.Record.NameIndex); + if (name.Length > 0) + { + // We don't enforce the strict class name rules in the static compiler, since HotSpot doesn't enforce *any* rules on + // class names for the system (and boot) class loader. We still need to enforce the 1.5 restrictions, because we + // rely on those invariants. +#if !IMPORTER + if (classFile.MajorVersion < 49 && (options & ClassFileParseOptions.RelaxedClassNameValidation) == 0) + { + char prev = name[0]; + if (Char.IsLetter(prev) || prev == '$' || prev == '_' || prev == '[' || prev == '/') + { + int skip = 1; + int end = name.Length; + if (prev == '[') + { + if (!IsValidFieldSig(name)) + { + goto barf; + } + while (name[skip] == '[') + { + skip++; + } + if (name.EndsWith(";")) + { + end--; + } + } + for (int i = skip; i < end; i++) + { + char c = name[i]; + if (!Char.IsLetterOrDigit(c) && c != '$' && c != '_' && (c != '/' || prev == '/')) + { + goto barf; + } + prev = c; + } + name = String.Intern(name.Replace('/', '.')); + return; + } + } + else #endif - { - // since 1.5 the restrictions on class names have been greatly reduced - int start = 0; - int end = name.Length; - if(name[0] == '[') - { - if(!IsValidFieldSig(name)) - { - goto barf; - } - // the semicolon is only allowed at the end and IsValidFieldSig enforces this, - // but since invalidJava15Characters contains the semicolon, we decrement end - // to make the following check against invalidJava15Characters ignore the - // trailing semicolon. - if(name[end - 1] == ';') - { - end--; - } - while(name[start] == '[') - { - start++; - } - } - if(name.IndexOfAny(invalidJava15Characters, start, end - start) >= 0) - { - goto barf; - } - name = String.Intern(name.Replace('/', '.')); - return; - } - } - barf: - throw new ClassFormatError("Invalid class name \"{0}\"", name); - } - - internal override void MarkLinkRequired() - { - if(typeWrapper == null) - { - typeWrapper = VerifierTypeWrapper.Null; - } - } - - internal void LinkSelf(TypeWrapper thisType) - { - this.typeWrapper = thisType; - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - if(typeWrapper == VerifierTypeWrapper.Null) - { - TypeWrapper tw = thisType.GetClassLoader().LoadClass(name, mode | LoadMode.WarnClassNotFound); -#if !STATIC_COMPILER && !FIRST_PASS - if(!tw.IsUnloadable) - { - try - { - thisType.GetClassLoader().CheckPackageAccess(tw, thisType.ClassObject.pd); - } - catch(java.lang.SecurityException) - { - tw = new UnloadableTypeWrapper(name); - } - } + { + // since 1.5 the restrictions on class names have been greatly reduced + int start = 0; + int end = name.Length; + if (name[0] == '[') + { + if (!IsValidFieldSig(name)) + { + goto barf; + } + // the semicolon is only allowed at the end and IsValidFieldSig enforces this, + // but since invalidJava15Characters contains the semicolon, we decrement end + // to make the following check against invalidJava15Characters ignore the + // trailing semicolon. + if (name[end - 1] == ';') + { + end--; + } + while (name[start] == '[') + { + start++; + } + } + if (name.IndexOfAny(invalidJava15Characters, start, end - start) >= 0) + { + goto barf; + } + name = String.Intern(name.Replace('/', '.')); + return; + } + } + barf: + throw new ClassFormatError("Invalid class name \"{0}\"", name); + } + + internal override void MarkLinkRequired() + { + if (typeWrapper == null) + { + typeWrapper = VerifierTypeWrapper.Null; + } + } + + internal void LinkSelf(TypeWrapper thisType) + { + this.typeWrapper = thisType; + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + if (typeWrapper == VerifierTypeWrapper.Null) + { + TypeWrapper tw = thisType.GetClassLoader().LoadClass(name, mode | LoadMode.WarnClassNotFound); +#if !IMPORTER && !FIRST_PASS + if (!tw.IsUnloadable) + { + try + { + thisType.GetClassLoader().CheckPackageAccess(tw, thisType.ClassObject.pd); + } + catch (java.lang.SecurityException) + { + tw = new UnloadableTypeWrapper(name); + } + } #endif - typeWrapper = tw; - } - } - - internal string Name - { - get - { - return name; - } - } - - internal TypeWrapper GetClassType() - { - return typeWrapper; - } - - internal override ConstantType GetConstantType() - { - return ConstantType.Class; - } - - public sealed override int GetHashCode() - { - return name.GetHashCode(); - } - - public bool Equals(ConstantPoolItemClass other) - { - return ReferenceEquals(name, other.name); - } - } - } + typeWrapper = tw; + } + } + + internal string Name + { + get + { + return name; + } + } + + internal TypeWrapper GetClassType() + { + return typeWrapper; + } + + internal override ConstantType GetConstantType() + { + return ConstantType.Class; + } + + public sealed override int GetHashCode() + { + return name.GetHashCode(); + } + + public bool Equals(ConstantPoolItemClass other) + { + return ReferenceEquals(name, other.name); + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemDouble.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemDouble.cs index c7b02a9b83..c7d67c49b9 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemDouble.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemDouble.cs @@ -22,38 +22,36 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemDouble : ConstantPoolItem - { - internal double d; - - internal ConstantPoolItemDouble(BigEndianBinaryReader br) - { - d = br.ReadDouble(); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.Double; - } - - internal double Value - { - get - { - return d; - } - } - - internal override object GetRuntimeValue() - { - return d; - } - } - } + { + + sealed class ConstantPoolItemDouble : ConstantPoolItem + { + + internal double d; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemDouble(DoubleConstantReader reader) + { + d = reader.Value; + } + + internal override ConstantType GetConstantType() => ConstantType.Double; + + internal double Value => d; + + internal override object GetRuntimeValue() => d; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFMI.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFMI.cs index 0f230440ed..2d5d5811f1 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFMI.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFMI.cs @@ -27,79 +27,87 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal abstract class ConstantPoolItemFMI : ConstantPoolItem - { - private ushort class_index; - private ushort name_and_type_index; - private ConstantPoolItemClass clazz; - private string name; - private string descriptor; - - internal ConstantPoolItemFMI(BigEndianBinaryReader br) - { - class_index = br.ReadUInt16(); - name_and_type_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index); - clazz = (ConstantPoolItemClass)classFile.GetConstantPoolItem(class_index); - // if the constant pool items referred to were strings, GetConstantPoolItem returns null - if(name_and_type == null || clazz == null) - { - throw new ClassFormatError("Bad index in constant pool"); - } - name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.name_index)); - descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptor_index); - Validate(name, descriptor, classFile.MajorVersion); - descriptor = String.Intern(descriptor.Replace('/', '.')); - } - - protected abstract void Validate(string name, string descriptor, int majorVersion); - - internal override void MarkLinkRequired() - { - clazz.MarkLinkRequired(); - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - clazz.Link(thisType, mode); - } - - internal string Name - { - get - { - return name; - } - } - - internal string Signature - { - get - { - return descriptor; - } - } - - internal string Class - { - get - { - return clazz.Name; - } - } - - internal TypeWrapper GetClassType() - { - return clazz.GetClassType(); - } - - internal abstract MemberWrapper GetMember(); - } - } + { + + readonly ushort class_index; + readonly ushort name_and_type_index; + + ConstantPoolItemClass clazz; + string name; + string descriptor; + + /// + /// Initializes a new instance. + /// + /// + /// + internal ConstantPoolItemFMI(ushort classIndex, ushort nameAndTypeIndex) + { + class_index = classIndex; + name_and_type_index = nameAndTypeIndex; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index); + clazz = (ConstantPoolItemClass)classFile.GetConstantPoolItem(class_index); + // if the constant pool items referred to were strings, GetConstantPoolItem returns null + if (name_and_type == null || clazz == null) + { + throw new ClassFormatError("Bad index in constant pool"); + } + name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.nameIndex)); + descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptorIndex); + Validate(name, descriptor, classFile.MajorVersion); + descriptor = String.Intern(descriptor.Replace('/', '.')); + } + + protected abstract void Validate(string name, string descriptor, int majorVersion); + + internal override void MarkLinkRequired() + { + clazz.MarkLinkRequired(); + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + clazz.Link(thisType, mode); + } + + internal string Name + { + get + { + return name; + } + } + + internal string Signature + { + get + { + return descriptor; + } + } + + internal string Class + { + get + { + return clazz.Name; + } + } + + internal TypeWrapper GetClassType() + { + return clazz.GetClassType(); + } + + internal abstract MemberWrapper GetMember(); + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFieldref.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFieldref.cs index 6179829d6a..0063137d53 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFieldref.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFieldref.cs @@ -22,83 +22,90 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemFieldref : ConstantPoolItemFMI - { - private FieldWrapper field; - private TypeWrapper fieldTypeWrapper; - - internal ConstantPoolItemFieldref(BigEndianBinaryReader br) : base(br) - { - } - - protected override void Validate(string name, string descriptor, int majorVersion) - { - if(!IsValidFieldSig(descriptor)) - { - throw new ClassFormatError("Invalid field signature \"{0}\"", descriptor); - } - if(!IsValidFieldName(name, majorVersion)) - { - throw new ClassFormatError("Invalid field name \"{0}\"", name); - } - } - - internal TypeWrapper GetFieldType() - { - return fieldTypeWrapper; - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - base.Link(thisType, mode); - lock(this) - { - if(fieldTypeWrapper != null) - { - return; - } - } - FieldWrapper fw = null; - TypeWrapper wrapper = GetClassType(); - if(wrapper == null) - { - return; - } - if(!wrapper.IsUnloadable) - { - fw = wrapper.GetFieldWrapper(Name, Signature); - if(fw != null) - { - fw.Link(mode); - } - } - ClassLoaderWrapper classLoader = thisType.GetClassLoader(); - TypeWrapper fld = classLoader.FieldTypeWrapperFromSig(this.Signature, mode); - lock(this) - { - if(fieldTypeWrapper == null) - { - fieldTypeWrapper = fld; - field = fw; - } - } - } - - internal FieldWrapper GetField() - { - return field; - } - - internal override MemberWrapper GetMember() - { - return field; - } - } - } + { + + FieldWrapper field; + TypeWrapper fieldTypeWrapper; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemFieldref(FieldrefConstantReader reader) : base(reader.Record.ClassIndex, reader.Record.NameAndTypeIndex) + { + + } + + protected override void Validate(string name, string descriptor, int majorVersion) + { + if (!IsValidFieldSig(descriptor)) + throw new ClassFormatError("Invalid field signature \"{0}\"", descriptor); + if (!IsValidFieldName(name, majorVersion)) + throw new ClassFormatError("Invalid field name \"{0}\"", name); + } + + internal TypeWrapper GetFieldType() + { + return fieldTypeWrapper; + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + base.Link(thisType, mode); + lock (this) + { + if (fieldTypeWrapper != null) + { + return; + } + } + FieldWrapper fw = null; + TypeWrapper wrapper = GetClassType(); + if (wrapper == null) + { + return; + } + if (!wrapper.IsUnloadable) + { + fw = wrapper.GetFieldWrapper(Name, Signature); + if (fw != null) + { + fw.Link(mode); + } + } + ClassLoaderWrapper classLoader = thisType.GetClassLoader(); + TypeWrapper fld = classLoader.FieldTypeWrapperFromSig(this.Signature, mode); + lock (this) + { + if (fieldTypeWrapper == null) + { + fieldTypeWrapper = fld; + field = fw; + } + } + } + + internal FieldWrapper GetField() + { + return field; + } + + internal override MemberWrapper GetMember() + { + return field; + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFloat.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFloat.cs index c7c0afc44b..04e882106a 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFloat.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemFloat.cs @@ -22,38 +22,39 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemFloat : ConstantPoolItem - { - internal float v; - - internal ConstantPoolItemFloat(BigEndianBinaryReader br) - { - v = br.ReadSingle(); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.Float; - } - - internal float Value - { - get - { - return v; - } - } - - internal override object GetRuntimeValue() - { - return v; - } - } - } + { + + sealed class ConstantPoolItemFloat : ConstantPoolItem + { + + internal float v; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemFloat(FloatConstantReader reader) + { + v = reader.Value; + } + + internal override ConstantType GetConstantType() + { + return ConstantType.Float; + } + + internal float Value => v; + + internal override object GetRuntimeValue() => v; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInteger.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInteger.cs index da5a7db8a8..e156ce7f02 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInteger.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInteger.cs @@ -22,38 +22,36 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemInteger : ConstantPoolItem - { - internal int v; - - internal ConstantPoolItemInteger(BigEndianBinaryReader br) - { - v = br.ReadInt32(); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.Integer; - } - - internal int Value - { - get - { - return v; - } - } - - internal override object GetRuntimeValue() - { - return v; - } - } - } + { + + sealed class ConstantPoolItemInteger : ConstantPoolItem + { + + internal int v; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemInteger(IntegerConstantReader reader) + { + v = reader.Value; + } + + internal override ConstantType GetConstantType() => ConstantType.Integer; + + internal int Value => v; + + internal override object GetRuntimeValue() => v; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInterfaceMethodref.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInterfaceMethodref.cs index d373bb6f80..a693495349 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInterfaceMethodref.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInterfaceMethodref.cs @@ -22,39 +22,50 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemInterfaceMethodref : ConstantPoolItemMI - { - internal ConstantPoolItemInterfaceMethodref(BigEndianBinaryReader br) : base(br) - { - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - base.Link(thisType, mode); - TypeWrapper wrapper = GetClassType(); - if(wrapper != null) - { - if(!wrapper.IsUnloadable) - { - method = wrapper.GetInterfaceMethod(Name, Signature); - } - if(method == null) - { - // NOTE vmspec 5.4.3.4 clearly states that an interfacemethod may also refer to a method in Object - method = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(Name, Signature, false); - } - if(method != null) - { - method.Link(mode); - } - } - } - } - } + { + + /// + /// initializes a new instance. + /// + /// + internal ConstantPoolItemInterfaceMethodref(InterfaceMethodrefConstantReader reader) : base(reader.Record.ClassIndex, reader.Record.NameAndTypeIndex) + { + + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + base.Link(thisType, mode); + TypeWrapper wrapper = GetClassType(); + if (wrapper != null) + { + if (!wrapper.IsUnloadable) + { + method = wrapper.GetInterfaceMethod(Name, Signature); + } + if (method == null) + { + // NOTE vmspec 5.4.3.4 clearly states that an interfacemethod may also refer to a method in Object + method = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(Name, Signature, false); + } + if (method != null) + { + method.Link(mode); + } + } + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInvokeDynamic.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInvokeDynamic.cs index f2a3989dd9..b108241312 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInvokeDynamic.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemInvokeDynamic.cs @@ -21,87 +21,96 @@ Jeroen Frijters jeroen@frijters.net */ -using System; +using IKVM.ByteCode.Reading; namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed class ConstantPoolItemInvokeDynamic : ConstantPoolItem - { - private ushort bootstrap_specifier_index; - private ushort name_and_type_index; - private string name; - private string descriptor; - private TypeWrapper[] argTypeWrappers; - private TypeWrapper retTypeWrapper; - - internal ConstantPoolItemInvokeDynamic(BigEndianBinaryReader br) - { - bootstrap_specifier_index = br.ReadUInt16(); - name_and_type_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index); - // if the constant pool items referred to were strings, GetConstantPoolItem returns null - if (name_and_type == null) - { - throw new ClassFormatError("Bad index in constant pool"); - } - name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.name_index)); - descriptor = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptor_index).Replace('/', '.')); - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - lock (this) - { - if (argTypeWrappers != null) - { - return; - } - } - ClassLoaderWrapper classLoader = thisType.GetClassLoader(); - TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSig(descriptor, mode); - TypeWrapper ret = classLoader.RetTypeWrapperFromSig(descriptor, mode); - lock (this) - { - if (argTypeWrappers == null) - { - argTypeWrappers = args; - retTypeWrapper = ret; - } - } - } - - internal TypeWrapper[] GetArgTypes() - { - return argTypeWrappers; - } - - internal TypeWrapper GetRetType() - { - return retTypeWrapper; - } - - internal string Name - { - get { return name; } - } - - internal string Signature - { - get { return descriptor; } - } - - internal ushort BootstrapMethod - { - get { return bootstrap_specifier_index; } - } - } - } + { + + readonly ushort bootstrap_specifier_index; + readonly ushort name_and_type_index; + + string name; + string descriptor; + TypeWrapper[] argTypeWrappers; + TypeWrapper retTypeWrapper; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemInvokeDynamic(InvokeDynamicConstantReader reader) + { + bootstrap_specifier_index = reader.Record.BootstrapMethodAttributeIndex; + name_and_type_index = reader.Record.NameAndTypeIndex; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + var name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index); + // if the constant pool items referred to were strings, GetConstantPoolItem returns null + if (name_and_type == null) + throw new ClassFormatError("Bad index in constant pool"); + + name = string.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.nameIndex)); + descriptor = string.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, name_and_type.descriptorIndex).Replace('/', '.')); + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + lock (this) + { + if (argTypeWrappers != null) + { + return; + } + } + + var classLoader = thisType.GetClassLoader(); + var args = classLoader.ArgTypeWrapperListFromSig(descriptor, mode); + var ret = classLoader.RetTypeWrapperFromSig(descriptor, mode); + + lock (this) + { + if (argTypeWrappers == null) + { + argTypeWrappers = args; + retTypeWrapper = ret; + } + } + } + + internal TypeWrapper[] GetArgTypes() + { + return argTypeWrappers; + } + + internal TypeWrapper GetRetType() + { + return retTypeWrapper; + } + + internal string Name + { + get { return name; } + } + + internal string Signature + { + get { return descriptor; } + } + + internal ushort BootstrapMethod + { + get { return bootstrap_specifier_index; } + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLiveObject.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLiveObject.cs index abaeb0900e..9289d14bed 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLiveObject.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLiveObject.cs @@ -26,21 +26,21 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { private sealed class ConstantPoolItemLiveObject : ConstantPoolItem - { - internal readonly object Value; - - internal ConstantPoolItemLiveObject(object value) - { - this.Value = value; - } - - internal override ConstantType GetConstantType() - { - return ConstantType.LiveObject; - } - } - } + { + internal readonly object Value; + + internal ConstantPoolItemLiveObject(object value) + { + this.Value = value; + } + + internal override ConstantType GetConstantType() + { + return ConstantType.LiveObject; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLong.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLong.cs index af308b403a..13f2bf3ce0 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLong.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemLong.cs @@ -22,38 +22,36 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemLong : ConstantPoolItem - { - internal long l; - - internal ConstantPoolItemLong(BigEndianBinaryReader br) - { - l = br.ReadInt64(); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.Long; - } - - internal long Value - { - get - { - return l; - } - } - - internal override object GetRuntimeValue() - { - return l; - } - } - } + { + + sealed class ConstantPoolItemLong : ConstantPoolItem + { + + internal long l; + + /// + /// initializes a new instance. + /// + /// + internal ConstantPoolItemLong(LongConstantReader reader) + { + l = reader.Value; + } + + internal override ConstantType GetConstantType() => ConstantType.Long; + + internal long Value => l; + + internal override object GetRuntimeValue() => l; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMI.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMI.cs index 18f1d1cd8e..86dea52bd5 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMI.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMI.cs @@ -26,85 +26,93 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal class ConstantPoolItemMI : ConstantPoolItemFMI - { - private TypeWrapper[] argTypeWrappers; - private TypeWrapper retTypeWrapper; - protected MethodWrapper method; - protected MethodWrapper invokespecialMethod; - - internal ConstantPoolItemMI(BigEndianBinaryReader br) : base(br) - { - } - - protected override void Validate(string name, string descriptor, int majorVersion) - { - if(!IsValidMethodSig(descriptor)) - { - throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor); - } - if(!IsValidMethodName(name, majorVersion)) - { - if(!ReferenceEquals(name, StringConstants.INIT)) - { - throw new ClassFormatError("Invalid method name \"{0}\"", name); - } - if(!descriptor.EndsWith("V")) - { - throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor); - } - } - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - base.Link(thisType, mode); - lock(this) - { - if(argTypeWrappers != null) - { - return; - } - } - ClassLoaderWrapper classLoader = thisType.GetClassLoader(); - TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSig(this.Signature, mode); - TypeWrapper ret = classLoader.RetTypeWrapperFromSig(this.Signature, mode); - lock(this) - { - if(argTypeWrappers == null) - { - argTypeWrappers = args; - retTypeWrapper = ret; - } - } - } - - internal TypeWrapper[] GetArgTypes() - { - return argTypeWrappers; - } - - internal TypeWrapper GetRetType() - { - return retTypeWrapper; - } - - internal MethodWrapper GetMethod() - { - return method; - } - - internal MethodWrapper GetMethodForInvokespecial() - { - return invokespecialMethod != null ? invokespecialMethod : method; - } - - internal override MemberWrapper GetMember() - { - return method; - } - } - } + { + + TypeWrapper[] argTypeWrappers; + TypeWrapper retTypeWrapper; + protected MethodWrapper method; + protected MethodWrapper invokespecialMethod; + + /// + /// Initializes a new instance. + /// + /// + /// + internal ConstantPoolItemMI(ushort classIndex, ushort nameAndTypeIndex) : base(classIndex, nameAndTypeIndex) + { + + } + + protected override void Validate(string name, string descriptor, int majorVersion) + { + if (!IsValidMethodSig(descriptor)) + { + throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor); + } + if (!IsValidMethodName(name, majorVersion)) + { + if (!ReferenceEquals(name, StringConstants.INIT)) + { + throw new ClassFormatError("Invalid method name \"{0}\"", name); + } + if (!descriptor.EndsWith("V")) + { + throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor); + } + } + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + base.Link(thisType, mode); + lock (this) + { + if (argTypeWrappers != null) + { + return; + } + } + ClassLoaderWrapper classLoader = thisType.GetClassLoader(); + TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSig(this.Signature, mode); + TypeWrapper ret = classLoader.RetTypeWrapperFromSig(this.Signature, mode); + lock (this) + { + if (argTypeWrappers == null) + { + argTypeWrappers = args; + retTypeWrapper = ret; + } + } + } + + internal TypeWrapper[] GetArgTypes() + { + return argTypeWrappers; + } + + internal TypeWrapper GetRetType() + { + return retTypeWrapper; + } + + internal MethodWrapper GetMethod() + { + return method; + } + + internal MethodWrapper GetMethodForInvokespecial() + { + return invokespecialMethod != null ? invokespecialMethod : method; + } + + internal override MemberWrapper GetMember() + { + return method; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodHandle.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodHandle.cs index d1e954059c..37b4f67c34 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodHandle.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodHandle.cs @@ -22,105 +22,114 @@ Jeroen Frijters */ +using System; + +using IKVM.ByteCode; +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemMethodHandle : ConstantPoolItem - { - private byte ref_kind; - private ushort method_index; - private ConstantPoolItemFMI cpi; - - internal ConstantPoolItemMethodHandle(BigEndianBinaryReader br) - { - ref_kind = br.ReadByte(); - method_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - switch ((RefKind)ref_kind) - { - case RefKind.getField: - case RefKind.getStatic: - case RefKind.putField: - case RefKind.putStatic: - cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemFieldref; - break; - case RefKind.invokeSpecial: - case RefKind.invokeVirtual: - case RefKind.invokeStatic: - case RefKind.newInvokeSpecial: - cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemMethodref; - if (cpi == null && classFile.MajorVersion >= 52 && ((RefKind)ref_kind == RefKind.invokeStatic || (RefKind)ref_kind == RefKind.invokeSpecial)) - goto case RefKind.invokeInterface; - break; - case RefKind.invokeInterface: - cpi = classFile.GetConstantPoolItem(method_index) as ConstantPoolItemInterfaceMethodref; - break; - } - if (cpi == null) - { - throw new ClassFormatError("Invalid constant pool item MethodHandle"); - } - if (ReferenceEquals(cpi.Name, StringConstants.INIT) && Kind != RefKind.newInvokeSpecial) - { - throw new ClassFormatError("Bad method name"); - } - } - - internal override void MarkLinkRequired() - { - cpi.MarkLinkRequired(); - } - - internal string Class - { - get { return cpi.Class; } - } - - internal string Name - { - get { return cpi.Name; } - } - - internal string Signature - { - get { return cpi.Signature; } - } - - internal ConstantPoolItemFMI MemberConstantPoolItem - { - get { return cpi; } - } - - internal RefKind Kind - { - get { return (RefKind)ref_kind; } - } - - internal MemberWrapper Member - { - get { return cpi.GetMember(); } - } - - internal TypeWrapper GetClassType() - { - return cpi.GetClassType(); - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - cpi.Link(thisType, mode); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.MethodHandle; - } - } - } + { + + readonly MethodHandleConstantReader reader; + ConstantPoolItemFMI cpi; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemMethodHandle(MethodHandleConstantReader reader) + { + this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + switch (reader.ReferenceKind) + { + case ReferenceKind.GetField: + case ReferenceKind.GetStatic: + case ReferenceKind.PutField: + case ReferenceKind.PutStatic: + cpi = classFile.GetConstantPoolItem(reader.Record.Index) as ConstantPoolItemFieldref; + break; + case ReferenceKind.InvokeSpecial: + case ReferenceKind.InvokeVirtual: + case ReferenceKind.InvokeStatic: + case ReferenceKind.NewInvokeSpecial: + cpi = classFile.GetConstantPoolItem(reader.Record.Index) as ConstantPoolItemMethodref; + if (cpi == null && classFile.MajorVersion >= 52 && (reader.ReferenceKind is ReferenceKind.InvokeStatic or ReferenceKind.InvokeSpecial)) + goto case ReferenceKind.InvokeInterface; + break; + case ReferenceKind.InvokeInterface: + cpi = classFile.GetConstantPoolItem(reader.Record.Index) as ConstantPoolItemInterfaceMethodref; + break; + } + + if (cpi == null) + throw new ClassFormatError("Invalid constant pool item MethodHandle"); + + if (ReferenceEquals(cpi.Name, StringConstants.INIT) && reader.ReferenceKind != ReferenceKind.NewInvokeSpecial) + throw new ClassFormatError("Bad method name"); + } + + internal override void MarkLinkRequired() + { + cpi.MarkLinkRequired(); + } + + internal string Class + { + get { return cpi.Class; } + } + + internal string Name + { + get { return cpi.Name; } + } + + internal string Signature + { + get { return cpi.Signature; } + } + + internal ConstantPoolItemFMI MemberConstantPoolItem + { + get { return cpi; } + } + + internal ReferenceKind Kind + { + get { return reader.ReferenceKind; } + } + + internal MemberWrapper Member + { + get { return cpi.GetMember(); } + } + + internal TypeWrapper GetClassType() + { + return cpi.GetClassType(); + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + cpi.Link(thisType, mode); + } + + internal override ConstantType GetConstantType() + { + return ConstantType.MethodHandle; + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodType.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodType.cs index 481a97956f..a280438258 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodType.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodType.cs @@ -21,77 +21,87 @@ Jeroen Frijters jeroen@frijters.net */ -using System; +using IKVM.ByteCode.Reading; namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemMethodType : ConstantPoolItem - { - private ushort signature_index; - private string descriptor; - private TypeWrapper[] argTypeWrappers; - private TypeWrapper retTypeWrapper; - - internal ConstantPoolItemMethodType(BigEndianBinaryReader br) - { - signature_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - string descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, signature_index); - if (descriptor == null || !IsValidMethodSig(descriptor)) - { - throw new ClassFormatError("Invalid MethodType signature"); - } - this.descriptor = String.Intern(descriptor.Replace('/', '.')); - } - - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - lock (this) - { - if (argTypeWrappers != null) - { - return; - } - } - ClassLoaderWrapper classLoader = thisType.GetClassLoader(); - TypeWrapper[] args = classLoader.ArgTypeWrapperListFromSig(descriptor, mode); - TypeWrapper ret = classLoader.RetTypeWrapperFromSig(descriptor, mode); - lock (this) - { - if (argTypeWrappers == null) - { - argTypeWrappers = args; - retTypeWrapper = ret; - } - } - } - - internal string Signature - { - get { return descriptor; } - } - - internal TypeWrapper[] GetArgTypes() - { - return argTypeWrappers; - } - - internal TypeWrapper GetRetType() - { - return retTypeWrapper; - } - - internal override ConstantType GetConstantType() - { - return ConstantType.MethodType; - } - } - } + { + + readonly ushort signature_index; + + string descriptor; + TypeWrapper[] argTypeWrappers; + TypeWrapper retTypeWrapper; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemMethodType(MethodTypeConstantReader reader) + { + signature_index = reader.Record.DescriptorIndex; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + var descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, signature_index); + if (descriptor == null || !IsValidMethodSig(descriptor)) + throw new ClassFormatError("Invalid MethodType signature"); + + this.descriptor = string.Intern(descriptor.Replace('/', '.')); + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + lock (this) + { + if (argTypeWrappers != null) + { + return; + } + } + + var classLoader = thisType.GetClassLoader(); + var args = classLoader.ArgTypeWrapperListFromSig(descriptor, mode); + var ret = classLoader.RetTypeWrapperFromSig(descriptor, mode); + + lock (this) + { + if (argTypeWrappers == null) + { + argTypeWrappers = args; + retTypeWrapper = ret; + } + } + } + + internal string Signature + { + get { return descriptor; } + } + + internal TypeWrapper[] GetArgTypes() + { + return argTypeWrappers; + } + + internal TypeWrapper GetRetType() + { + return retTypeWrapper; + } + + internal override ConstantType GetConstantType() + { + return ConstantType.MethodType; + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodref.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodref.cs index 4f1c04405f..adf6ed1767 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodref.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemMethodref.cs @@ -22,44 +22,55 @@ Jeroen Frijters */ using IKVM.Attributes; +using IKVM.ByteCode.Reading; +using IKVM.Runtime; namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class ConstantPoolItemMethodref : ConstantPoolItemMI - { - internal ConstantPoolItemMethodref(BigEndianBinaryReader br) : base(br) - { - } + { + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemMethodref(MethodrefConstantReader reader) : base(reader.Record.ClassIndex, reader.Record.NameAndTypeIndex) + { + + } + + internal override void Link(TypeWrapper thisType, LoadMode mode) + { + base.Link(thisType, mode); + var wrapper = GetClassType(); + if (wrapper != null && !wrapper.IsUnloadable) + { + method = wrapper.GetMethodWrapper(Name, Signature, !ReferenceEquals(Name, StringConstants.INIT)); + if (method != null) + { + method.Link(mode); + } + if (Name != StringConstants.INIT + && !thisType.IsInterface + && (!JVM.AllowNonVirtualCalls || (thisType.Modifiers & Modifiers.Super) == Modifiers.Super) + && thisType != wrapper + && thisType.IsSubTypeOf(wrapper)) + { + invokespecialMethod = thisType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true); + if (invokespecialMethod != null) + { + invokespecialMethod.Link(mode); + } + } + } + } + + } - internal override void Link(TypeWrapper thisType, LoadMode mode) - { - base.Link(thisType, mode); - TypeWrapper wrapper = GetClassType(); - if(wrapper != null && !wrapper.IsUnloadable) - { - method = wrapper.GetMethodWrapper(Name, Signature, !ReferenceEquals(Name, StringConstants.INIT)); - if(method != null) - { - method.Link(mode); - } - if(Name != StringConstants.INIT - && !thisType.IsInterface - && (!JVM.AllowNonVirtualCalls || (thisType.Modifiers & Modifiers.Super) == Modifiers.Super) - && thisType != wrapper - && thisType.IsSubTypeOf(wrapper)) - { - invokespecialMethod = thisType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true); - if(invokespecialMethod != null) - { - invokespecialMethod.Link(mode); - } - } - } - } - } - } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemNameAndType.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemNameAndType.cs index 9f15b74557..9a2c381bd0 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemNameAndType.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemNameAndType.cs @@ -22,31 +22,38 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemNameAndType : ConstantPoolItem - { - internal ushort name_index; - internal ushort descriptor_index; - - internal ConstantPoolItemNameAndType(BigEndianBinaryReader br) - { - name_index = br.ReadUInt16(); - descriptor_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - if(classFile.GetConstantPoolUtf8String(utf8_cp, name_index) == null - || classFile.GetConstantPoolUtf8String(utf8_cp, descriptor_index) == null) - { - throw new ClassFormatError("Illegal constant pool index"); - } - } - } - } + { + + sealed class ConstantPoolItemNameAndType : ConstantPoolItem + { + + internal ushort nameIndex; + internal ushort descriptorIndex; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemNameAndType(NameAndTypeConstantReader reader) + { + nameIndex = reader.Record.NameIndex; + descriptorIndex = reader.Record.DescriptorIndex; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + if (classFile.GetConstantPoolUtf8String(utf8_cp, nameIndex) == null || classFile.GetConstantPoolUtf8String(utf8_cp, descriptorIndex) == null) + throw new ClassFormatError("Illegal constant pool index"); + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemString.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemString.cs index 96f627a6ac..c870bc53ba 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemString.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemString.cs @@ -22,39 +22,40 @@ Jeroen Frijters */ +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { - private sealed class ConstantPoolItemString : ConstantPoolItem - { - private ushort string_index; - private string s; - - internal ConstantPoolItemString(BigEndianBinaryReader br) - { - string_index = br.ReadUInt16(); - } - - internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) - { - s = classFile.GetConstantPoolUtf8String(utf8_cp, string_index); - } - - internal override ConstantType GetConstantType() - { - return ConstantType.String; - } - - internal string Value - { - get - { - return s; - } - } - } - } + { + + sealed class ConstantPoolItemString : ConstantPoolItem + { + + readonly ushort valueIndex; + string s; + + /// + /// Initializes a new instance. + /// + /// + internal ConstantPoolItemString(StringConstantReader reader) + { + valueIndex = reader.Record.ValueIndex; + } + + internal override void Resolve(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options) + { + s = classFile.GetConstantPoolUtf8String(utf8_cp, valueIndex); + } + + internal override ConstantType GetConstantType() => ConstantType.String; + + internal string Value => s; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemUtf8.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemUtf8.cs index ee19d133c2..a9c19ffbe4 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemUtf8.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantPoolItemUtf8.cs @@ -26,23 +26,23 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { // this is only used to copy strings into "constantpool" when we see a RuntimeVisibleTypeAnnotations attribute, // because we need a consistent way of exposing constant pool items to the runtime and that case private sealed class ConstantPoolItemUtf8 : ConstantPoolItem - { - private readonly string str; - - internal ConstantPoolItemUtf8(string str) - { - this.str = str; - } - - internal override object GetRuntimeValue() - { - return str; - } - } - } + { + private readonly string str; + + internal ConstantPoolItemUtf8(string str) + { + this.str = str; + } + + internal override object GetRuntimeValue() + { + return str; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantType.cs b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantType.cs index 858b873367..2db6705f23 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.ConstantType.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.ConstantType.cs @@ -26,19 +26,19 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal enum ConstantType - { - Integer, - Long, - Float, - Double, - String, - Class, - MethodHandle, - MethodType, - LiveObject, // used by anonymous class constant pool patching - } - } + { + Integer, + Long, + Float, + Double, + String, + Class, + MethodHandle, + MethodType, + LiveObject, // used by anonymous class constant pool patching + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Field.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Field.cs index 68710cfe3c..3b70595d04 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Field.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Field.cs @@ -24,238 +24,203 @@ Jeroen Frijters using System; using IKVM.Attributes; +using IKVM.ByteCode; +using IKVM.ByteCode.Reading; namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed class Field : FieldOrMethod - { - private object constantValue; - private string[] propertyGetterSetter; - - internal Field(ClassFile classFile, string[] utf8_cp, BigEndianBinaryReader br) : base(classFile, utf8_cp, br) - { - if((IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) - || (IsFinal && IsVolatile) - || (classFile.IsInterface && (!IsPublic || !IsStatic || !IsFinal || IsTransient))) - { - throw new ClassFormatError("{0} (Illegal field modifiers: 0x{1:X})", classFile.Name, access_flags); - } - int attributes_count = br.ReadUInt16(); - for(int i = 0; i < attributes_count; i++) - { - switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())) - { - case "Deprecated": - if(br.ReadUInt32() != 0) - { - throw new ClassFormatError("Invalid Deprecated attribute length"); - } - flags |= FLAG_MASK_DEPRECATED; - break; - case "ConstantValue": - { - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("Invalid ConstantValue attribute length"); - } - ushort index = br.ReadUInt16(); - try - { - switch(Signature) - { - case "I": - constantValue = classFile.GetConstantPoolConstantInteger(index); - break; - case "S": - constantValue = (short)classFile.GetConstantPoolConstantInteger(index); - break; - case "B": - constantValue = (byte)classFile.GetConstantPoolConstantInteger(index); - break; - case "C": - constantValue = (char)classFile.GetConstantPoolConstantInteger(index); - break; - case "Z": - constantValue = classFile.GetConstantPoolConstantInteger(index) != 0; - break; - case "J": - constantValue = classFile.GetConstantPoolConstantLong(index); - break; - case "F": - constantValue = classFile.GetConstantPoolConstantFloat(index); - break; - case "D": - constantValue = classFile.GetConstantPoolConstantDouble(index); - break; - case "Ljava.lang.String;": - constantValue = classFile.GetConstantPoolConstantString(index); - break; - default: - throw new ClassFormatError("{0} (Invalid signature for constant)", classFile.Name); - } - } - catch(InvalidCastException) - { - throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); - } - catch(IndexOutOfRangeException) - { - throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); - } - catch(InvalidOperationException) - { - throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); - } - catch(NullReferenceException) - { - throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); - } - break; - } - case "Signature": - if(classFile.MajorVersion < 49) - { - goto default; - } - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("Signature attribute has incorrect length"); - } - signature = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - break; - case "RuntimeVisibleAnnotations": - if(classFile.MajorVersion < 49) - { - goto default; - } - annotations = ReadAnnotations(br, classFile, utf8_cp); - break; - case "RuntimeInvisibleAnnotations": - if(classFile.MajorVersion < 49) - { - goto default; - } - foreach(object[] annot in ReadAnnotations(br, classFile, utf8_cp)) - { - if(annot[1].Equals("Likvm/lang/Property;")) - { - DecodePropertyAnnotation(classFile, annot); - } -#if STATIC_COMPILER + { + + object constantValue; + string[] propertyGetterSetter; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + internal Field(ClassFile classFile, string[] utf8_cp, FieldReader reader) : + base(classFile, utf8_cp, reader.AccessFlags, reader.Record.NameIndex, reader.Record.DescriptorIndex) + { + if ((IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) || (IsFinal && IsVolatile) || (classFile.IsInterface && (!IsPublic || !IsStatic || !IsFinal || IsTransient))) + throw new ClassFormatError("{0} (Illegal field modifiers: 0x{1:X})", classFile.Name, accessFlags); + + for (int i = 0; i < reader.Attributes.Count; i++) + { + var attribute = reader.Attributes[i]; + + switch (classFile.GetConstantPoolUtf8String(utf8_cp, attribute.Info.Record.NameIndex)) + { + case "Deprecated": + if (attribute is not DeprecatedAttributeReader deprecatedAttribute) + throw new ClassFormatError("Invalid Deprecated attribute type."); + + flags |= FLAG_MASK_DEPRECATED; + break; + case "ConstantValue": + if (attribute is not ConstantValueAttributeReader constantValueAttribute) + throw new ClassFormatError("Invalid ConstantValue attribute type."); + + try + { + constantValue = Signature switch + { + "I" => classFile.GetConstantPoolConstantInteger(constantValueAttribute.Record.ValueIndex), + "S" => (short)classFile.GetConstantPoolConstantInteger(constantValueAttribute.Record.ValueIndex), + "B" => (byte)classFile.GetConstantPoolConstantInteger(constantValueAttribute.Record.ValueIndex), + "C" => (char)classFile.GetConstantPoolConstantInteger(constantValueAttribute.Record.ValueIndex), + "Z" => classFile.GetConstantPoolConstantInteger(constantValueAttribute.Record.ValueIndex) != 0, + "J" => classFile.GetConstantPoolConstantLong(constantValueAttribute.Record.ValueIndex), + "F" => classFile.GetConstantPoolConstantFloat(constantValueAttribute.Record.ValueIndex), + "D" => classFile.GetConstantPoolConstantDouble(constantValueAttribute.Record.ValueIndex), + "Ljava.lang.String;" => classFile.GetConstantPoolConstantString(constantValueAttribute.Record.ValueIndex), + _ => throw new ClassFormatError("{0} (Invalid signature for constant)", classFile.Name), + }; + } + catch (InvalidCastException) + { + throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + catch (IndexOutOfRangeException) + { + throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + catch (InvalidOperationException) + { + throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + catch (NullReferenceException) + { + throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + catch (ByteCodeException) + { + throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + break; + case "Signature": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not SignatureAttributeReader signatureAttribute) + throw new ClassFormatError("Invalid Signature attribute type."); + + signature = classFile.GetConstantPoolUtf8String(utf8_cp, signatureAttribute.Record.SignatureIndex); + break; + case "RuntimeVisibleAnnotations": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not RuntimeVisibleAnnotationsAttributeReader runtimeVisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleAnnotations attribute type."); + + annotations = ReadAnnotations(runtimeVisibleAnnotationsAttribute.Annotations, classFile, utf8_cp); + break; + case "RuntimeInvisibleAnnotations": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not RuntimeInvisibleAnnotationsAttributeReader runtimeInvisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeInvisibleAnnotations attribute type."); + + foreach (object[] annot in ReadAnnotations(runtimeInvisibleAnnotationsAttribute.Annotations, classFile, utf8_cp)) + { + if (annot[1].Equals("Likvm/lang/Property;")) + DecodePropertyAnnotation(classFile, annot); +#if IMPORTER else if(annot[1].Equals("Likvm/lang/Internal;")) { - this.access_flags &= ~Modifiers.AccessMask; + this.accessFlags &= ~Modifiers.AccessMask; flags |= FLAG_MASK_INTERNAL; } #endif - } - break; - case "RuntimeVisibleTypeAnnotations": - if (classFile.MajorVersion < 52) - { - goto default; - } - classFile.CreateUtf8ConstantPoolItems(utf8_cp); - runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray(); - break; - default: - br.Skip(br.ReadUInt32()); - break; - } - } - } - - private void DecodePropertyAnnotation(ClassFile classFile, object[] annot) - { - if(propertyGetterSetter != null) - { - Tracer.Error(Tracer.ClassLoading, "Ignoring duplicate ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name); - return; - } - propertyGetterSetter = new string[2]; - for(int i = 2; i < annot.Length - 1; i += 2) - { - string value = annot[i + 1] as string; - if(value == null) - { - propertyGetterSetter = null; - break; - } - if(annot[i].Equals("get") && propertyGetterSetter[0] == null) - { - propertyGetterSetter[0] = value; - } - else if(annot[i].Equals("set") && propertyGetterSetter[1] == null) - { - propertyGetterSetter[1] = value; - } - else - { - propertyGetterSetter = null; - break; - } - } - if(propertyGetterSetter == null || propertyGetterSetter[0] == null) - { - propertyGetterSetter = null; - Tracer.Error(Tracer.ClassLoading, "Ignoring malformed ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name); - return; - } - } - - protected override void ValidateSig(ClassFile classFile, string descriptor) - { - if(!IsValidFieldSig(descriptor)) - { - throw new ClassFormatError("{0} (Field \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor); - } - } - - internal object ConstantValue - { - get - { - return constantValue; - } - } - - internal void PatchConstantValue(object value) - { - constantValue = value; - } - - internal bool IsStaticFinalConstant - { - get { return (access_flags & (Modifiers.Final | Modifiers.Static)) == (Modifiers.Final | Modifiers.Static) && constantValue != null; } - } - - internal bool IsProperty - { - get - { - return propertyGetterSetter != null; - } - } - - internal string PropertyGetter - { - get - { - return propertyGetterSetter[0]; - } - } - - internal string PropertySetter - { - get - { - return propertyGetterSetter[1]; - } - } - } - } + } + + break; + case "RuntimeVisibleTypeAnnotations": + if (classFile.MajorVersion < 52) + goto default; + + if (attribute is not RuntimeVisibleTypeAnnotationsAttributeReader runtimeVisibleTypeAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleTypeAnnotations attribute type."); + + classFile.CreateUtf8ConstantPoolItems(utf8_cp); + runtimeVisibleTypeAnnotations = runtimeVisibleTypeAnnotationsAttribute.Annotations; + break; + default: + break; + } + } + + } + + private void DecodePropertyAnnotation(ClassFile classFile, object[] annot) + { + if (propertyGetterSetter != null) + { + Tracer.Error(Tracer.ClassLoading, "Ignoring duplicate ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name); + return; + } + + propertyGetterSetter = new string[2]; + for (int i = 2; i < annot.Length - 1; i += 2) + { + string value = annot[i + 1] as string; + if (value == null) + { + propertyGetterSetter = null; + break; + } + if (annot[i].Equals("get") && propertyGetterSetter[0] == null) + { + propertyGetterSetter[0] = value; + } + else if (annot[i].Equals("set") && propertyGetterSetter[1] == null) + { + propertyGetterSetter[1] = value; + } + else + { + propertyGetterSetter = null; + break; + } + } + + if (propertyGetterSetter == null || propertyGetterSetter[0] == null) + { + propertyGetterSetter = null; + Tracer.Error(Tracer.ClassLoading, "Ignoring malformed ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name); + return; + } + } + + protected override void ValidateSig(ClassFile classFile, string descriptor) + { + if (!IsValidFieldSig(descriptor)) + throw new ClassFormatError("{0} (Field \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor); + } + + internal object ConstantValue => constantValue; + + internal void PatchConstantValue(object value) => constantValue = value; + + internal bool IsStaticFinalConstant => (accessFlags & (Modifiers.Final | Modifiers.Static)) == (Modifiers.Final | Modifiers.Static) && constantValue != null; + + internal bool IsProperty => propertyGetterSetter != null; + + internal string PropertyGetter => propertyGetterSetter[0]; + + internal string PropertySetter => propertyGetterSetter[1]; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.FieldOrMethod.cs b/src/IKVM.Runtime/ClassFile/ClassFile.FieldOrMethod.cs index e07b3c223c..ff47745968 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.FieldOrMethod.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.FieldOrMethod.cs @@ -22,198 +22,93 @@ Jeroen Frijters */ using System; +using System.Collections.Generic; using IKVM.Attributes; +using IKVM.ByteCode; +using IKVM.ByteCode.Reading; namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal abstract class FieldOrMethod : IEquatable - { - // Note that Modifiers is a ushort, so it combines nicely with the following ushort field - protected Modifiers access_flags; - protected ushort flags; - private string name; - private string descriptor; - protected string signature; - protected object[] annotations; - protected byte[] runtimeVisibleTypeAnnotations; - - internal FieldOrMethod(ClassFile classFile, string[] utf8_cp, BigEndianBinaryReader br) - { - access_flags = (Modifiers)br.ReadUInt16(); - name = String.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())); - descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - ValidateSig(classFile, descriptor); - descriptor = String.Intern(descriptor.Replace('/', '.')); - } - - protected abstract void ValidateSig(ClassFile classFile, string descriptor); - - internal string Name - { - get - { - return name; - } - } - - internal string Signature - { - get - { - return descriptor; - } - } - - internal object[] Annotations - { - get - { - return annotations; - } - } - - internal string GenericSignature - { - get - { - return signature; - } - } - - internal Modifiers Modifiers - { - get - { - return (Modifiers)access_flags; - } - } - - internal bool IsAbstract - { - get - { - return (access_flags & Modifiers.Abstract) != 0; - } - } - - internal bool IsFinal - { - get - { - return (access_flags & Modifiers.Final) != 0; - } - } - - internal bool IsPublic - { - get - { - return (access_flags & Modifiers.Public) != 0; - } - } - - internal bool IsPrivate - { - get - { - return (access_flags & Modifiers.Private) != 0; - } - } - - internal bool IsProtected - { - get - { - return (access_flags & Modifiers.Protected) != 0; - } - } - - internal bool IsStatic - { - get - { - return (access_flags & Modifiers.Static) != 0; - } - } - - internal bool IsSynchronized - { - get - { - return (access_flags & Modifiers.Synchronized) != 0; - } - } - - internal bool IsVolatile - { - get - { - return (access_flags & Modifiers.Volatile) != 0; - } - } - - internal bool IsTransient - { - get - { - return (access_flags & Modifiers.Transient) != 0; - } - } - - internal bool IsNative - { - get - { - return (access_flags & Modifiers.Native) != 0; - } - } - - internal bool IsEnum - { - get - { - return (access_flags & Modifiers.Enum) != 0; - } - } - - internal bool DeprecatedAttribute - { - get - { - return (flags & FLAG_MASK_DEPRECATED) != 0; - } - } - - internal bool IsInternal - { - get - { - return (flags & FLAG_MASK_INTERNAL) != 0; - } - } - - internal byte[] RuntimeVisibleTypeAnnotations - { - get - { - return runtimeVisibleTypeAnnotations; - } - } - - public sealed override int GetHashCode() - { - return name.GetHashCode() ^ descriptor.GetHashCode(); - } - - public bool Equals(FieldOrMethod other) - { - return ReferenceEquals(name, other.name) && ReferenceEquals(descriptor, other.descriptor); - } - } - } + { + + protected Modifiers accessFlags; + protected ushort flags; + string name; + string descriptor; + protected string signature; + protected object[] annotations; + protected IReadOnlyList runtimeVisibleTypeAnnotations; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + internal FieldOrMethod(ClassFile classFile, string[] utf8_cp, AccessFlag accessFlags, ushort nameIndex, ushort descriptorIndex) + { + this.accessFlags = (Modifiers)accessFlags; + this.name = string.Intern(classFile.GetConstantPoolUtf8String(utf8_cp, nameIndex)); + this.descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, descriptorIndex); + + ValidateSig(classFile, descriptor); + this.descriptor = string.Intern(descriptor.Replace('/', '.')); + } + + protected abstract void ValidateSig(ClassFile classFile, string descriptor); + + internal string Name => name; + + internal string Signature => descriptor; + + internal object[] Annotations => annotations; + + internal string GenericSignature => signature; + + internal Modifiers Modifiers => (Modifiers)accessFlags; + + internal bool IsAbstract => (accessFlags & Modifiers.Abstract) != 0; + + internal bool IsFinal => (accessFlags & Modifiers.Final) != 0; + + internal bool IsPublic => (accessFlags & Modifiers.Public) != 0; + + internal bool IsPrivate => (accessFlags & Modifiers.Private) != 0; + + internal bool IsProtected => (accessFlags & Modifiers.Protected) != 0; + + internal bool IsStatic => (accessFlags & Modifiers.Static) != 0; + + internal bool IsSynchronized => (accessFlags & Modifiers.Synchronized) != 0; + + internal bool IsVolatile => (accessFlags & Modifiers.Volatile) != 0; + + internal bool IsTransient => (accessFlags & Modifiers.Transient) != 0; + + internal bool IsNative => (accessFlags & Modifiers.Native) != 0; + + internal bool IsEnum => (accessFlags & Modifiers.Enum) != 0; + + internal bool DeprecatedAttribute => (flags & FLAG_MASK_DEPRECATED) != 0; + + internal bool IsInternal => (flags & FLAG_MASK_INTERNAL) != 0; + + internal IReadOnlyList RuntimeVisibleTypeAnnotations => runtimeVisibleTypeAnnotations; + + public sealed override int GetHashCode() => name.GetHashCode() ^ descriptor.GetHashCode(); + + public bool Equals(FieldOrMethod other) => ReferenceEquals(name, other.name) && ReferenceEquals(descriptor, other.descriptor); + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.InnerClass.cs b/src/IKVM.Runtime/ClassFile/ClassFile.InnerClass.cs index c7aebe4710..7edde4f238 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.InnerClass.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.InnerClass.cs @@ -27,14 +27,14 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal struct InnerClass - { - internal ushort innerClass; // ConstantPoolItemClass - internal ushort outerClass; // ConstantPoolItemClass - internal ushort name; // ConstantPoolItemUtf8 - internal Modifiers accessFlags; - } - } + { + internal ushort innerClass; // ConstantPoolItemClass + internal ushort outerClass; // ConstantPoolItemClass + internal ushort name; // ConstantPoolItemUtf8 + internal Modifiers accessFlags; + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.Code.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.Code.cs index 7b051b8c54..baa0a1ea41 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.Code.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.Code.cs @@ -24,239 +24,229 @@ Jeroen Frijters using System; using System.Collections.Generic; +using IKVM.ByteCode.Reading; + namespace IKVM.Internal { sealed partial class ClassFile - { + { + internal sealed partial class Method { - private struct Code - { - internal bool hasJsr; - internal string verifyError; - internal ushort max_stack; - internal ushort max_locals; - internal Instruction[] instructions; - internal ExceptionTableEntry[] exception_table; - internal int[] argmap; - internal LineNumberTableEntry[] lineNumberTable; - internal LocalVariableTableEntry[] localVariableTable; - - internal void Read(ClassFile classFile, string[] utf8_cp, Method method, BigEndianBinaryReader br, ClassFileParseOptions options) - { - max_stack = br.ReadUInt16(); - max_locals = br.ReadUInt16(); - uint code_length = br.ReadUInt32(); - if(code_length == 0 || code_length > 65535) - { - throw new ClassFormatError("Invalid method Code length {1} in class file {0}", classFile.Name, code_length); - } - Instruction[] instructions = new Instruction[code_length + 1]; - int basePosition = br.Position; - int instructionIndex = 0; - try - { - BigEndianBinaryReader rdr = br.Section(code_length); - while(!rdr.IsAtEnd) - { - instructions[instructionIndex].Read((ushort)(rdr.Position - basePosition), rdr, classFile); - hasJsr |= instructions[instructionIndex].NormalizedOpCode == NormalizedByteCode.__jsr; - instructionIndex++; - } - // we add an additional nop instruction to make it easier for consumers of the code array - instructions[instructionIndex++].SetTermNop((ushort)(rdr.Position - basePosition)); - } - catch(ClassFormatError x) - { - // any class format errors in the code block are actually verify errors - verifyError = x.Message; - } - this.instructions = new Instruction[instructionIndex]; - Array.Copy(instructions, 0, this.instructions, 0, instructionIndex); - // build the pcIndexMap - int[] pcIndexMap = new int[this.instructions[instructionIndex - 1].PC + 1]; - for(int i = 0; i < pcIndexMap.Length; i++) - { - pcIndexMap[i] = -1; - } - for(int i = 0; i < instructionIndex - 1; i++) - { - pcIndexMap[this.instructions[i].PC] = i; - } - // convert branch offsets to indexes - for(int i = 0; i < instructionIndex - 1; i++) - { - switch(this.instructions[i].NormalizedOpCode) - { - case NormalizedByteCode.__ifeq: - case NormalizedByteCode.__ifne: - case NormalizedByteCode.__iflt: - case NormalizedByteCode.__ifge: - case NormalizedByteCode.__ifgt: - case NormalizedByteCode.__ifle: - case NormalizedByteCode.__if_icmpeq: - case NormalizedByteCode.__if_icmpne: - case NormalizedByteCode.__if_icmplt: - case NormalizedByteCode.__if_icmpge: - case NormalizedByteCode.__if_icmpgt: - case NormalizedByteCode.__if_icmple: - case NormalizedByteCode.__if_acmpeq: - case NormalizedByteCode.__if_acmpne: - case NormalizedByteCode.__ifnull: - case NormalizedByteCode.__ifnonnull: - case NormalizedByteCode.__goto: - case NormalizedByteCode.__jsr: - this.instructions[i].SetTargetIndex(pcIndexMap[this.instructions[i].Arg1 + this.instructions[i].PC]); - break; - case NormalizedByteCode.__tableswitch: - case NormalizedByteCode.__lookupswitch: - this.instructions[i].MapSwitchTargets(pcIndexMap); - break; - } - } - // read exception table - ushort exception_table_length = br.ReadUInt16(); - exception_table = new ExceptionTableEntry[exception_table_length]; - for(int i = 0; i < exception_table_length; i++) - { - ushort start_pc = br.ReadUInt16(); - ushort end_pc = br.ReadUInt16(); - ushort handler_pc = br.ReadUInt16(); - ushort catch_type = br.ReadUInt16(); - if(start_pc >= end_pc - || end_pc > code_length - || handler_pc >= code_length - || (catch_type != 0 && !classFile.SafeIsConstantPoolClass(catch_type))) - { - throw new ClassFormatError("Illegal exception table: {0}.{1}{2}", classFile.Name, method.Name, method.Signature); - } - classFile.MarkLinkRequiredConstantPoolItem(catch_type); - // if start_pc, end_pc or handler_pc is invalid (i.e. doesn't point to the start of an instruction), - // the index will be -1 and this will be handled by the verifier - int startIndex = pcIndexMap[start_pc]; - int endIndex; - if (end_pc == code_length) - { - // it is legal for end_pc to point to just after the last instruction, - // but since there isn't an entry in our pcIndexMap for that, we have - // a special case for this - endIndex = instructionIndex - 1; - } - else - { - endIndex = pcIndexMap[end_pc]; - } - int handlerIndex = pcIndexMap[handler_pc]; - exception_table[i] = new ExceptionTableEntry(startIndex, endIndex, handlerIndex, catch_type, i); - } - ushort attributes_count = br.ReadUInt16(); - for(int i = 0; i < attributes_count; i++) - { - switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())) - { - case "LineNumberTable": - if((options & ClassFileParseOptions.LineNumberTable) != 0) - { - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - int count = rdr.ReadUInt16(); - lineNumberTable = new LineNumberTableEntry[count]; - for(int j = 0; j < count; j++) - { - lineNumberTable[j].start_pc = rdr.ReadUInt16(); - lineNumberTable[j].line_number = rdr.ReadUInt16(); - if(lineNumberTable[j].start_pc >= code_length) - { - throw new ClassFormatError("{0} (LineNumberTable has invalid pc)", classFile.Name); - } - } - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (LineNumberTable attribute has wrong length)", classFile.Name); - } - } - else - { - br.Skip(br.ReadUInt32()); - } - break; - case "LocalVariableTable": - if((options & ClassFileParseOptions.LocalVariableTable) != 0) - { - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - int count = rdr.ReadUInt16(); - localVariableTable = new LocalVariableTableEntry[count]; - for(int j = 0; j < count; j++) - { - localVariableTable[j].start_pc = rdr.ReadUInt16(); - localVariableTable[j].length = rdr.ReadUInt16(); - localVariableTable[j].name = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()); - localVariableTable[j].descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()).Replace('/', '.'); - localVariableTable[j].index = rdr.ReadUInt16(); - } - // NOTE we're intentionally not checking that we're at the end of the section - // (optional attributes shouldn't cause ClassFormatError) - } - else - { - br.Skip(br.ReadUInt32()); - } - break; - default: - br.Skip(br.ReadUInt32()); - break; - } - } - // build the argmap - string sig = method.Signature; - List args = new List(); - int pos = 0; - if(!method.IsStatic) - { - args.Add(pos++); - } - for(int i = 1; sig[i] != ')'; i++) - { - args.Add(pos++); - switch(sig[i]) - { - case 'L': - i = sig.IndexOf(';', i); - break; - case 'D': - case 'J': - args.Add(-1); - break; - case '[': - { - while(sig[i] == '[') - { - i++; - } - if(sig[i] == 'L') - { - i = sig.IndexOf(';', i); - } - break; - } - } - } - argmap = args.ToArray(); - if(args.Count > max_locals) - { - throw new ClassFormatError("{0} (Arguments can't fit into locals)", classFile.Name); - } - } - - internal bool IsEmpty - { - get - { - return instructions == null; - } - } - } - } - } + + struct Code + { + + internal bool hasJsr; + internal string verifyError; + internal ushort max_stack; + internal ushort max_locals; + internal Instruction[] instructions; + internal ExceptionTableEntry[] exception_table; + internal int[] argmap; + internal LineNumberTableEntry[] lineNumberTable; + internal LocalVariableTableEntry[] localVariableTable; + + internal void Read(ClassFile classFile, string[] utf8_cp, Method method, CodeAttributeReader reader, ClassFileParseOptions options) + { + max_stack = reader.MaxStack; + max_locals = reader.MaxLocals; + var code_length = (uint)reader.Code.Length; + if (code_length == 0 || code_length > 65535) + throw new ClassFormatError("Invalid method Code length {1} in class file {0}", classFile.Name, code_length); + + var instructions = new Instruction[code_length + 1]; + int basePosition = 0; + int instructionIndex = 0; + + try + { + var rdr = new BigEndianBinaryReader(reader.Code); + while (rdr.IsAtEnd == false) + { + instructions[instructionIndex].Read((ushort)(rdr.Position - basePosition), rdr, classFile); + hasJsr |= instructions[instructionIndex].NormalizedOpCode == NormalizedByteCode.__jsr; + instructionIndex++; + } + + // we add an additional nop instruction to make it easier for consumers of the code array + instructions[instructionIndex++].SetTermNop((ushort)(rdr.Position - basePosition)); + } + catch (ClassFormatError x) + { + // any class format errors in the code block are actually verify errors + verifyError = x.Message; + } + + this.instructions = new Instruction[instructionIndex]; + Array.Copy(instructions, 0, this.instructions, 0, instructionIndex); + + // build the pcIndexMap + var pcIndexMap = new int[this.instructions[instructionIndex - 1].PC + 1]; + for (int i = 0; i < pcIndexMap.Length; i++) + pcIndexMap[i] = -1; + for (int i = 0; i < instructionIndex - 1; i++) + pcIndexMap[this.instructions[i].PC] = i; + + // convert branch offsets to indexes + for (int i = 0; i < instructionIndex - 1; i++) + { + switch (this.instructions[i].NormalizedOpCode) + { + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + case NormalizedByteCode.__ifnull: + case NormalizedByteCode.__ifnonnull: + case NormalizedByteCode.__goto: + case NormalizedByteCode.__jsr: + this.instructions[i].SetTargetIndex(pcIndexMap[this.instructions[i].Arg1 + this.instructions[i].PC]); + break; + case NormalizedByteCode.__tableswitch: + case NormalizedByteCode.__lookupswitch: + this.instructions[i].MapSwitchTargets(pcIndexMap); + break; + } + } + + // read exception table + exception_table = new ExceptionTableEntry[reader.ExceptionTable.Count]; + for (int i = 0; i < reader.ExceptionTable.Count; i++) + { + var handler = reader.ExceptionTable[i]; + var start_pc = handler.StartOffset; + var end_pc = handler.EndOffset; + var handler_pc = handler.HandlerOffset; + var catch_type = handler.CatchTypeIndex; + + if (start_pc >= end_pc || end_pc > code_length || handler_pc >= code_length || (catch_type != 0 && !classFile.SafeIsConstantPoolClass(catch_type))) + throw new ClassFormatError("Illegal exception table: {0}.{1}{2}", classFile.Name, method.Name, method.Signature); + + classFile.MarkLinkRequiredConstantPoolItem(catch_type); + + // if start_pc, end_pc or handler_pc is invalid (i.e. doesn't point to the start of an instruction), + // the index will be -1 and this will be handled by the verifier + var startIndex = pcIndexMap[start_pc]; + var endIndex = 0; + if (end_pc == code_length) + { + // it is legal for end_pc to point to just after the last instruction, + // but since there isn't an entry in our pcIndexMap for that, we have + // a special case for this + endIndex = instructionIndex - 1; + } + else + { + endIndex = pcIndexMap[end_pc]; + } + + var handlerIndex = pcIndexMap[handler_pc]; + exception_table[i] = new ExceptionTableEntry(startIndex, endIndex, handlerIndex, catch_type, i); + } + + for (int i = 0; i < reader.Attributes.Count; i++) + { + var attribute = reader.Attributes[i]; + + switch (classFile.GetConstantPoolUtf8String(utf8_cp, attribute.Info.Record.NameIndex)) + { + case "LineNumberTable": + if (attribute is not LineNumberTableAttributeReader lnt) + throw new ClassFormatError("Invalid reader for line number table."); + + if ((options & ClassFileParseOptions.LineNumberTable) != 0) + { + lineNumberTable = new LineNumberTableEntry[lnt.Record.Items.Length]; + for (int j = 0; j < lnt.Record.Items.Length; j++) + { + var item = lnt.Record.Items[j]; + lineNumberTable[j].start_pc = item.CodeOffset; + lineNumberTable[j].line_number = item.LineNumber; + if (lineNumberTable[j].start_pc >= code_length) + throw new ClassFormatError("{0} (LineNumberTable has invalid pc)", classFile.Name); + } + } + break; + case "LocalVariableTable": + if (attribute is not LocalVariableTableAttributeReader lvt) + throw new ClassFormatError("Invalid reader for local variable table."); + + if ((options & ClassFileParseOptions.LocalVariableTable) != 0) + { + localVariableTable = new LocalVariableTableEntry[lvt.Record.Items.Length]; + for (int j = 0; j < lvt.Record.Items.Length; j++) + { + var item = lvt.Record.Items[j]; + localVariableTable[j].start_pc = item.CodeOffset; + localVariableTable[j].length = item.CodeLength; + localVariableTable[j].name = classFile.GetConstantPoolUtf8String(utf8_cp, item.NameIndex); + localVariableTable[j].descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, item.DescriptorIndex).Replace('/', '.'); + localVariableTable[j].index = item.Index; + } + } + break; + default: + break; + } + } + + // build the argmap + var sig = method.Signature; + var args = new List(); + int pos = 0; + if (!method.IsStatic) + args.Add(pos++); + + for (int i = 1; sig[i] != ')'; i++) + { + args.Add(pos++); + switch (sig[i]) + { + case 'L': + i = sig.IndexOf(';', i); + break; + case 'D': + case 'J': + args.Add(-1); + break; + case '[': + { + while (sig[i] == '[') + { + i++; + } + if (sig[i] == 'L') + { + i = sig.IndexOf(';', i); + } + break; + } + } + } + argmap = args.ToArray(); + + if (args.Count > max_locals) + throw new ClassFormatError("{0} (Arguments can't fit into locals)", classFile.Name); + } + + internal bool IsEmpty => instructions == null; + + } + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.ExceptionTableEntry.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.ExceptionTableEntry.cs index a4447ddafb..9614c51e19 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.ExceptionTableEntry.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.ExceptionTableEntry.cs @@ -26,34 +26,34 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { internal sealed class ExceptionTableEntry - { - internal readonly int startIndex; - internal readonly int endIndex; - internal readonly int handlerIndex; - internal readonly ushort catch_type; - internal readonly int ordinal; - internal readonly bool isFinally; - - internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal) - : this(startIndex, endIndex, handlerIndex, catch_type, ordinal, false) - { - } - - internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal, bool isFinally) - { - this.startIndex = startIndex; - this.endIndex = endIndex; - this.handlerIndex = handlerIndex; - this.catch_type = catch_type; - this.ordinal = ordinal; - this.isFinally = isFinally; - } - } - } - } + { + internal readonly int startIndex; + internal readonly int endIndex; + internal readonly int handlerIndex; + internal readonly ushort catch_type; + internal readonly int ordinal; + internal readonly bool isFinally; + + internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal) + : this(startIndex, endIndex, handlerIndex, catch_type, ordinal, false) + { + } + + internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal, bool isFinally) + { + this.startIndex = startIndex; + this.endIndex = endIndex; + this.handlerIndex = handlerIndex; + this.catch_type = catch_type; + this.ordinal = ordinal; + this.isFinally = isFinally; + } + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.Instruction.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.Instruction.cs index 391b2de265..35b45e72e5 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.Instruction.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.Instruction.cs @@ -26,309 +26,309 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { internal struct Instruction - { - private ushort pc; - private NormalizedByteCode normopcode; - private int arg1; - private short arg2; - private SwitchEntry[] switch_entries; + { + private ushort pc; + private NormalizedByteCode normopcode; + private int arg1; + private short arg2; + private SwitchEntry[] switch_entries; - struct SwitchEntry - { - internal int value; - internal int target; - } + struct SwitchEntry + { + internal int value; + internal int target; + } - internal void SetHardError(HardError error, int messageId) - { - normopcode = NormalizedByteCode.__static_error; - arg2 = (short)error; - arg1 = messageId; - } + internal void SetHardError(HardError error, int messageId) + { + normopcode = NormalizedByteCode.__static_error; + arg2 = (short)error; + arg1 = messageId; + } - internal HardError HardError - { - get - { - return (HardError)arg2; - } - } + internal HardError HardError + { + get + { + return (HardError)arg2; + } + } - internal int HandlerIndex - { - get { return (ushort)arg2; } - } + internal int HandlerIndex + { + get { return (ushort)arg2; } + } - internal int HardErrorMessageId - { - get - { - return arg1; - } - } + internal int HardErrorMessageId + { + get + { + return arg1; + } + } - internal void PatchOpCode(NormalizedByteCode bc) - { - this.normopcode = bc; - } + internal void PatchOpCode(NormalizedByteCode bc) + { + this.normopcode = bc; + } - internal void PatchOpCode(NormalizedByteCode bc, int arg1) - { - this.normopcode = bc; - this.arg1 = arg1; - } + internal void PatchOpCode(NormalizedByteCode bc, int arg1) + { + this.normopcode = bc; + this.arg1 = arg1; + } - internal void PatchOpCode(NormalizedByteCode bc, int arg1, short arg2) - { - this.normopcode = bc; - this.arg1 = arg1; - this.arg2 = arg2; - } + internal void PatchOpCode(NormalizedByteCode bc, int arg1, short arg2) + { + this.normopcode = bc; + this.arg1 = arg1; + this.arg2 = arg2; + } - internal void SetPC(int pc) - { - this.pc = (ushort)pc; - } + internal void SetPC(int pc) + { + this.pc = (ushort)pc; + } - internal void SetTargetIndex(int targetIndex) - { - this.arg1 = targetIndex; - } + internal void SetTargetIndex(int targetIndex) + { + this.arg1 = targetIndex; + } - internal void SetTermNop(ushort pc) - { - // TODO what happens if we already have exactly the maximum number of instructions? - this.pc = pc; - this.normopcode = NormalizedByteCode.__nop; - } + internal void SetTermNop(ushort pc) + { + // TODO what happens if we already have exactly the maximum number of instructions? + this.pc = pc; + this.normopcode = NormalizedByteCode.__nop; + } - internal void MapSwitchTargets(int[] pcIndexMap) - { - arg1 = pcIndexMap[arg1 + pc]; - for (int i = 0; i < switch_entries.Length; i++) - { - switch_entries[i].target = pcIndexMap[switch_entries[i].target + pc]; - } - } + internal void MapSwitchTargets(int[] pcIndexMap) + { + arg1 = pcIndexMap[arg1 + pc]; + for (int i = 0; i < switch_entries.Length; i++) + { + switch_entries[i].target = pcIndexMap[switch_entries[i].target + pc]; + } + } - internal void Read(ushort pc, BigEndianBinaryReader br, ClassFile classFile) - { - this.pc = pc; - ByteCode bc = (ByteCode)br.ReadByte(); - switch(ByteCodeMetaData.GetMode(bc)) - { - case ByteCodeMode.Simple: - break; - case ByteCodeMode.Constant_1: - arg1 = br.ReadByte(); - classFile.MarkLinkRequiredConstantPoolItem(arg1); - break; - case ByteCodeMode.Local_1: - arg1 = br.ReadByte(); - break; - case ByteCodeMode.Constant_2: - arg1 = br.ReadUInt16(); - classFile.MarkLinkRequiredConstantPoolItem(arg1); - break; - case ByteCodeMode.Branch_2: - arg1 = br.ReadInt16(); - break; - case ByteCodeMode.Branch_4: - arg1 = br.ReadInt32(); - break; - case ByteCodeMode.Constant_2_1_1: - arg1 = br.ReadUInt16(); - classFile.MarkLinkRequiredConstantPoolItem(arg1); - arg2 = br.ReadByte(); - if(br.ReadByte() != 0) - { - throw new ClassFormatError("invokeinterface filler must be zero"); - } - break; - case ByteCodeMode.Immediate_1: - arg1 = br.ReadSByte(); - break; - case ByteCodeMode.Immediate_2: - arg1 = br.ReadInt16(); - break; - case ByteCodeMode.Local_1_Immediate_1: - arg1 = br.ReadByte(); - arg2 = br.ReadSByte(); - break; - case ByteCodeMode.Constant_2_Immediate_1: - arg1 = br.ReadUInt16(); - classFile.MarkLinkRequiredConstantPoolItem(arg1); - arg2 = br.ReadSByte(); - break; - case ByteCodeMode.Tableswitch: - { - // skip the padding - uint p = pc + 1u; - uint align = ((p + 3) & 0x7ffffffc) - p; - br.Skip(align); - int default_offset = br.ReadInt32(); - this.arg1 = default_offset; - int low = br.ReadInt32(); - int high = br.ReadInt32(); - if(low > high || high > 16384L + low) - { - throw new ClassFormatError("Incorrect tableswitch"); - } - SwitchEntry[] entries = new SwitchEntry[high - low + 1]; - for(int i = low; i < high; i++) - { - entries[i - low].value = i; - entries[i - low].target = br.ReadInt32(); - } - // do the last entry outside the loop, to avoid overflowing "i", if high == int.MaxValue - entries[high - low].value = high; - entries[high - low].target = br.ReadInt32(); - this.switch_entries = entries; - break; - } - case ByteCodeMode.Lookupswitch: - { - // skip the padding - uint p = pc + 1u; - uint align = ((p + 3) & 0x7ffffffc) - p; - br.Skip(align); - int default_offset = br.ReadInt32(); - this.arg1 = default_offset; - int count = br.ReadInt32(); - if(count < 0 || count > 16384) - { - throw new ClassFormatError("Incorrect lookupswitch"); - } - SwitchEntry[] entries = new SwitchEntry[count]; - for(int i = 0; i < count; i++) - { - entries[i].value = br.ReadInt32(); - entries[i].target = br.ReadInt32(); - } - this.switch_entries = entries; - break; - } - case ByteCodeMode.WidePrefix: - bc = (ByteCode)br.ReadByte(); - // NOTE the PC of a wide instruction is actually the PC of the - // wide prefix, not the following instruction (vmspec 4.9.2) - switch(ByteCodeMetaData.GetWideMode(bc)) - { - case ByteCodeModeWide.Local_2: - arg1 = br.ReadUInt16(); - break; - case ByteCodeModeWide.Local_2_Immediate_2: - arg1 = br.ReadUInt16(); - arg2 = br.ReadInt16(); - break; - default: - throw new ClassFormatError("Invalid wide prefix on opcode: {0}", bc); - } - break; - default: - throw new ClassFormatError("Invalid opcode: {0}", bc); - } - this.normopcode = ByteCodeMetaData.GetNormalizedByteCode(bc); - arg1 = ByteCodeMetaData.GetArg(bc, arg1); - } + internal void Read(ushort pc, BigEndianBinaryReader br, ClassFile classFile) + { + this.pc = pc; + var bc = (global::ByteCode)br.ReadByte(); + switch (ByteCodeMetaData.GetMode(bc)) + { + case ByteCodeMode.Simple: + break; + case ByteCodeMode.Constant_1: + arg1 = br.ReadByte(); + classFile.MarkLinkRequiredConstantPoolItem(arg1); + break; + case ByteCodeMode.Local_1: + arg1 = br.ReadByte(); + break; + case ByteCodeMode.Constant_2: + arg1 = br.ReadUInt16(); + classFile.MarkLinkRequiredConstantPoolItem(arg1); + break; + case ByteCodeMode.Branch_2: + arg1 = br.ReadInt16(); + break; + case ByteCodeMode.Branch_4: + arg1 = br.ReadInt32(); + break; + case ByteCodeMode.Constant_2_1_1: + arg1 = br.ReadUInt16(); + classFile.MarkLinkRequiredConstantPoolItem(arg1); + arg2 = br.ReadByte(); + if (br.ReadByte() != 0) + { + throw new ClassFormatError("invokeinterface filler must be zero"); + } + break; + case ByteCodeMode.Immediate_1: + arg1 = br.ReadSByte(); + break; + case ByteCodeMode.Immediate_2: + arg1 = br.ReadInt16(); + break; + case ByteCodeMode.Local_1_Immediate_1: + arg1 = br.ReadByte(); + arg2 = br.ReadSByte(); + break; + case ByteCodeMode.Constant_2_Immediate_1: + arg1 = br.ReadUInt16(); + classFile.MarkLinkRequiredConstantPoolItem(arg1); + arg2 = br.ReadSByte(); + break; + case ByteCodeMode.Tableswitch: + { + // skip the padding + uint p = pc + 1u; + uint align = ((p + 3) & 0x7ffffffc) - p; + br.Skip(align); + int default_offset = br.ReadInt32(); + this.arg1 = default_offset; + int low = br.ReadInt32(); + int high = br.ReadInt32(); + if (low > high || high > 16384L + low) + { + throw new ClassFormatError("Incorrect tableswitch"); + } + SwitchEntry[] entries = new SwitchEntry[high - low + 1]; + for (int i = low; i < high; i++) + { + entries[i - low].value = i; + entries[i - low].target = br.ReadInt32(); + } + // do the last entry outside the loop, to avoid overflowing "i", if high == int.MaxValue + entries[high - low].value = high; + entries[high - low].target = br.ReadInt32(); + this.switch_entries = entries; + break; + } + case ByteCodeMode.Lookupswitch: + { + // skip the padding + uint p = pc + 1u; + uint align = ((p + 3) & 0x7ffffffc) - p; + br.Skip(align); + int default_offset = br.ReadInt32(); + this.arg1 = default_offset; + int count = br.ReadInt32(); + if (count < 0 || count > 16384) + { + throw new ClassFormatError("Incorrect lookupswitch"); + } + SwitchEntry[] entries = new SwitchEntry[count]; + for (int i = 0; i < count; i++) + { + entries[i].value = br.ReadInt32(); + entries[i].target = br.ReadInt32(); + } + this.switch_entries = entries; + break; + } + case ByteCodeMode.WidePrefix: + bc = (global::ByteCode)br.ReadByte(); + // NOTE the PC of a wide instruction is actually the PC of the + // wide prefix, not the following instruction (vmspec 4.9.2) + switch (ByteCodeMetaData.GetWideMode(bc)) + { + case ByteCodeModeWide.Local_2: + arg1 = br.ReadUInt16(); + break; + case ByteCodeModeWide.Local_2_Immediate_2: + arg1 = br.ReadUInt16(); + arg2 = br.ReadInt16(); + break; + default: + throw new ClassFormatError("Invalid wide prefix on opcode: {0}", bc); + } + break; + default: + throw new ClassFormatError("Invalid opcode: {0}", bc); + } + this.normopcode = ByteCodeMetaData.GetNormalizedByteCode(bc); + arg1 = ByteCodeMetaData.GetArg(bc, arg1); + } - internal int PC - { - get - { - return pc; - } - } + internal int PC + { + get + { + return pc; + } + } - internal NormalizedByteCode NormalizedOpCode - { - get - { - return normopcode; - } - } + internal NormalizedByteCode NormalizedOpCode + { + get + { + return normopcode; + } + } - internal int Arg1 - { - get - { - return arg1; - } - } + internal int Arg1 + { + get + { + return arg1; + } + } - internal int TargetIndex - { - get - { - return arg1; - } - set - { - arg1 = value; - } - } + internal int TargetIndex + { + get + { + return arg1; + } + set + { + arg1 = value; + } + } - internal int Arg2 - { - get - { - return arg2; - } - } + internal int Arg2 + { + get + { + return arg2; + } + } - internal int NormalizedArg1 - { - get - { - return arg1; - } - } + internal int NormalizedArg1 + { + get + { + return arg1; + } + } - internal int DefaultTarget - { - get - { - return arg1; - } - set - { - arg1 = value; - } - } + internal int DefaultTarget + { + get + { + return arg1; + } + set + { + arg1 = value; + } + } - internal int SwitchEntryCount - { - get - { - return switch_entries.Length; - } - } + internal int SwitchEntryCount + { + get + { + return switch_entries.Length; + } + } - internal int GetSwitchValue(int i) - { - return switch_entries[i].value; - } + internal int GetSwitchValue(int i) + { + return switch_entries[i].value; + } - internal int GetSwitchTargetIndex(int i) - { - return switch_entries[i].target; - } + internal int GetSwitchTargetIndex(int i) + { + return switch_entries[i].target; + } - internal void SetSwitchTargets(int[] targets) - { - SwitchEntry[] newEntries = (SwitchEntry[])switch_entries.Clone(); - for (int i = 0; i < newEntries.Length; i++) - { - newEntries[i].target = targets[i]; - } - switch_entries = newEntries; - } - } - } - } + internal void SetSwitchTargets(int[] targets) + { + SwitchEntry[] newEntries = (SwitchEntry[])switch_entries.Clone(); + for (int i = 0; i < newEntries.Length; i++) + { + newEntries[i].target = targets[i]; + } + switch_entries = newEntries; + } + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.InstructionFlags.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.InstructionFlags.cs index 03d6af697b..6074d9a0dd 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.InstructionFlags.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.InstructionFlags.cs @@ -27,17 +27,17 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { [Flags] - internal enum InstructionFlags : byte - { - Reachable = 1, - Processed = 2, - BranchTarget = 4, - } - } - } + internal enum InstructionFlags : byte + { + Reachable = 1, + Processed = 2, + BranchTarget = 4, + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LineNumberTableEntry.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LineNumberTableEntry.cs index 2d95571609..6d08be72b9 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LineNumberTableEntry.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LineNumberTableEntry.cs @@ -26,15 +26,15 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { internal struct LineNumberTableEntry - { - internal ushort start_pc; - internal ushort line_number; - } - } - } + { + internal ushort start_pc; + internal ushort line_number; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LocalVariableTableEntry.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LocalVariableTableEntry.cs index b2b0b56080..495753d7eb 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LocalVariableTableEntry.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LocalVariableTableEntry.cs @@ -26,18 +26,18 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { internal struct LocalVariableTableEntry - { - internal ushort start_pc; - internal ushort length; - internal string name; - internal string descriptor; - internal ushort index; - } - } - } + { + internal ushort start_pc; + internal ushort length; + internal string name; + internal string descriptor; + internal ushort index; + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LowFreqData.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LowFreqData.cs index 538aafd51b..3ec561f09c 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.LowFreqData.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.LowFreqData.cs @@ -26,20 +26,20 @@ namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method { sealed class LowFreqData - { - internal object annotationDefault; - internal object[][] parameterAnnotations; -#if STATIC_COMPILER + { + internal object annotationDefault; + internal object[][] parameterAnnotations; +#if IMPORTER internal string DllExportName; internal int DllExportOrdinal; internal string InterlockedCompareAndSetField; #endif - } - } - } + } + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.Method.cs b/src/IKVM.Runtime/ClassFile/ClassFile.Method.cs index 5f800f7818..4675b982fc 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.Method.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.Method.cs @@ -21,546 +21,374 @@ Jeroen Frijters jeroen@frijters.net */ +using System.Collections.Generic; +using System.Linq; + using IKVM.Attributes; +using IKVM.ByteCode.Reading; + +#if IMPORTER +using IKVM.Tools.Importer; +#endif namespace IKVM.Internal { sealed partial class ClassFile - { + { internal sealed partial class Method : FieldOrMethod - { - - private Code code; - private string[] exceptions; - private LowFreqData low; - private MethodParametersEntry[] parameters; - - internal Method(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options, BigEndianBinaryReader br) : base(classFile, utf8_cp, br) - { - // vmspec 4.6 says that all flags, except ACC_STRICT are ignored on - // however, since Java 7 it does need to be marked static - if(ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && (classFile.MajorVersion < 51 || IsStatic)) - { - access_flags &= Modifiers.Strictfp; - access_flags |= (Modifiers.Static | Modifiers.Private); - } - else - { - // LAMESPEC: vmspec 4.6 says that abstract methods can not be strictfp (and this makes sense), but - // javac (pre 1.5) is broken and marks abstract methods as strictfp (if you put the strictfp on the class) - if((ReferenceEquals(Name, StringConstants.INIT) && (IsStatic || IsSynchronized || IsFinal || IsAbstract || IsNative)) - || (IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) - || (IsAbstract && (IsFinal || IsNative || IsPrivate || IsStatic || IsSynchronized)) - || (classFile.IsInterface && classFile.MajorVersion <= 51 && (!IsPublic || IsFinal || IsNative || IsSynchronized || !IsAbstract)) - || (classFile.IsInterface && classFile.MajorVersion >= 52 && (!(IsPublic || IsPrivate) || IsFinal || IsNative || IsSynchronized))) - { - throw new ClassFormatError("Method {0} in class {1} has illegal modifiers: 0x{2:X}", Name, classFile.Name, (int)access_flags); - } - } - int attributes_count = br.ReadUInt16(); - for(int i = 0; i < attributes_count; i++) - { - switch(classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())) - { - case "Deprecated": - if(br.ReadUInt32() != 0) - { - throw new ClassFormatError("Invalid Deprecated attribute length"); - } - flags |= FLAG_MASK_DEPRECATED; - break; - case "Code": - { - if(!code.IsEmpty) - { - throw new ClassFormatError("{0} (Duplicate Code attribute)", classFile.Name); - } - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - code.Read(classFile, utf8_cp, this, rdr, options); - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (Code attribute has wrong length)", classFile.Name); - } - break; - } - case "Exceptions": - { - if(exceptions != null) - { - throw new ClassFormatError("{0} (Duplicate Exceptions attribute)", classFile.Name); - } - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - ushort count = rdr.ReadUInt16(); - exceptions = new string[count]; - for(int j = 0; j < count; j++) - { - exceptions[j] = classFile.GetConstantPoolClass(rdr.ReadUInt16()); - } - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (Exceptions attribute has wrong length)", classFile.Name); - } - break; - } - case "Signature": - if(classFile.MajorVersion < 49) - { - goto default; - } - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("Signature attribute has incorrect length"); - } - signature = classFile.GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - break; - case "RuntimeVisibleAnnotations": - if(classFile.MajorVersion < 49) - { - goto default; - } - annotations = ReadAnnotations(br, classFile, utf8_cp); - if ((options & ClassFileParseOptions.TrustedAnnotations) != 0) - { - foreach(object[] annot in annotations) - { - switch((string)annot[1]) - { -#if STATIC_COMPILER - case "Lsun/reflect/CallerSensitive;": - flags |= FLAG_CALLERSENSITIVE; - break; + { + + Code code; + string[] exceptions; + LowFreqData low; + MethodParametersEntry[] parameters; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + internal Method(ClassFile classFile, string[] utf8_cp, ClassFileParseOptions options, MethodReader reader) : + base(classFile, utf8_cp, reader.AccessFlags, reader.Record.NameIndex, reader.Record.DescriptorIndex) + { + // vmspec 4.6 says that all flags, except ACC_STRICT are ignored on + // however, since Java 7 it does need to be marked static + if (ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && (classFile.MajorVersion < 51 || IsStatic)) + { + accessFlags &= Modifiers.Strictfp; + accessFlags |= (Modifiers.Static | Modifiers.Private); + } + else + { + // LAMESPEC: vmspec 4.6 says that abstract methods can not be strictfp (and this makes sense), but + // javac (pre 1.5) is broken and marks abstract methods as strictfp (if you put the strictfp on the class) + if ((ReferenceEquals(Name, StringConstants.INIT) && (IsStatic || IsSynchronized || IsFinal || IsAbstract || IsNative)) + || (IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) + || (IsAbstract && (IsFinal || IsNative || IsPrivate || IsStatic || IsSynchronized)) + || (classFile.IsInterface && classFile.MajorVersion <= 51 && (!IsPublic || IsFinal || IsNative || IsSynchronized || !IsAbstract)) + || (classFile.IsInterface && classFile.MajorVersion >= 52 && (!(IsPublic || IsPrivate) || IsFinal || IsNative || IsSynchronized))) + { + throw new ClassFormatError("Method {0} in class {1} has illegal modifiers: 0x{2:X}", Name, classFile.Name, (int)accessFlags); + } + } + + for (int i = 0; i < reader.Attributes.Count; i++) + { + var attribute = reader.Attributes[i]; + + switch (classFile.GetConstantPoolUtf8String(utf8_cp, attribute.Info.Record.NameIndex)) + { + case "Deprecated": + if (attribute is not DeprecatedAttributeReader deprecatedAttribute) + throw new ClassFormatError("Invalid Deprecated attribute type."); + + flags |= FLAG_MASK_DEPRECATED; + break; + case "Code": + { + if (attribute is not CodeAttributeReader codeAttribute) + throw new ClassFormatError("Invalid attribute reader type."); + if (code.IsEmpty == false) + throw new ClassFormatError("{0} (Duplicate Code attribute)", classFile.Name); + + code.Read(classFile, utf8_cp, this, codeAttribute, options); + break; + } + case "Exceptions": + { + if (attribute is not ExceptionsAttributeReader exceptionsAttribute) + throw new ClassFormatError("Invalid Exceptions attribute type."); + if (exceptions != null) + throw new ClassFormatError("{0} (Duplicate Exceptions attribute)", classFile.Name); + + exceptions = new string[exceptionsAttribute.Record.ExceptionsIndexes.Length]; + for (int j = 0; j < exceptionsAttribute.Record.ExceptionsIndexes.Length; j++) + exceptions[j] = classFile.GetConstantPoolClass(exceptionsAttribute.Record.ExceptionsIndexes[j]); + + break; + } + case "Signature": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not SignatureAttributeReader signatureAttribute) + throw new ClassFormatError("Invalid Signature attribute type."); + + signature = classFile.GetConstantPoolUtf8String(utf8_cp, signatureAttribute.Record.SignatureIndex); + break; + case "RuntimeVisibleAnnotations": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not RuntimeVisibleAnnotationsAttributeReader runtimeVisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleAnnotations attribute type."); + + annotations = ReadAnnotations(runtimeVisibleAnnotationsAttribute.Annotations, classFile, utf8_cp); + if ((options & ClassFileParseOptions.TrustedAnnotations) != 0) + { + foreach (object[] annot in annotations) + { + switch ((string)annot[1]) + { +#if IMPORTER + case "Lsun/reflect/CallerSensitive;": + flags |= FLAG_CALLERSENSITIVE; + break; #endif - case "Ljava/lang/invoke/LambdaForm$Compiled;": - flags |= FLAG_LAMBDAFORM_COMPILED; - break; - case "Ljava/lang/invoke/LambdaForm$Hidden;": - flags |= FLAG_LAMBDAFORM_HIDDEN; - break; - case "Ljava/lang/invoke/ForceInline;": - flags |= FLAG_FORCEINLINE; - break; - } - } - } - break; - case "RuntimeVisibleParameterAnnotations": - { - if(classFile.MajorVersion < 49) - { - goto default; - } - if(low == null) - { - low = new LowFreqData(); - } - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - byte num_parameters = rdr.ReadByte(); - low.parameterAnnotations = new object[num_parameters][]; - for(int j = 0; j < num_parameters; j++) - { - ushort num_annotations = rdr.ReadUInt16(); - low.parameterAnnotations[j] = new object[num_annotations]; - for(int k = 0; k < num_annotations; k++) - { - low.parameterAnnotations[j][k] = ReadAnnotation(rdr, classFile, utf8_cp); - } - } - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (RuntimeVisibleParameterAnnotations attribute has wrong length)", classFile.Name); - } - break; - } - case "AnnotationDefault": - { - if(classFile.MajorVersion < 49) - { - goto default; - } - if(low == null) - { - low = new LowFreqData(); - } - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - low.annotationDefault = ReadAnnotationElementValue(rdr, classFile, utf8_cp); - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (AnnotationDefault attribute has wrong length)", classFile.Name); - } - break; - } -#if STATIC_COMPILER - case "RuntimeInvisibleAnnotations": - if(classFile.MajorVersion < 49) - { - goto default; - } - foreach(object[] annot in ReadAnnotations(br, classFile, utf8_cp)) - { - if(annot[1].Equals("Likvm/lang/Internal;")) - { - if (classFile.IsInterface) - { - StaticCompiler.IssueMessage(Message.InterfaceMethodCantBeInternal, classFile.Name, this.Name, this.Signature); - } - else - { - this.access_flags &= ~Modifiers.AccessMask; - flags |= FLAG_MASK_INTERNAL; - } - } - if(annot[1].Equals("Likvm/lang/DllExport;")) - { - string name = null; - int? ordinal = null; - for (int j = 2; j < annot.Length; j += 2) - { - if (annot[j].Equals("name") && annot[j + 1] is string) - { - name = (string)annot[j + 1]; - } - else if (annot[j].Equals("ordinal") && annot[j + 1] is int) - { - ordinal = (int)annot[j + 1]; - } - } - if (name != null && ordinal != null) - { - if (!IsStatic) - { - StaticCompiler.IssueMessage(Message.DllExportMustBeStaticMethod, classFile.Name, this.Name, this.Signature); - } - else - { - if (low == null) - { - low = new LowFreqData(); - } - low.DllExportName = name; - low.DllExportOrdinal = ordinal.Value; - } - } - } - if(annot[1].Equals("Likvm/internal/InterlockedCompareAndSet;")) - { - string field = null; - for (int j = 2; j < annot.Length; j += 2) - { - if (annot[j].Equals("value") && annot[j + 1] is string) - { - field = (string)annot[j + 1]; - } - } - if (field != null) - { - if (low == null) - { - low = new LowFreqData(); - } - low.InterlockedCompareAndSetField = field; - } - } - } - break; + case "Ljava/lang/invoke/LambdaForm$Compiled;": + flags |= FLAG_LAMBDAFORM_COMPILED; + break; + case "Ljava/lang/invoke/LambdaForm$Hidden;": + flags |= FLAG_LAMBDAFORM_HIDDEN; + break; + case "Ljava/lang/invoke/ForceInline;": + flags |= FLAG_FORCEINLINE; + break; + } + } + } + break; + case "RuntimeVisibleParameterAnnotations": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not RuntimeVisibleParameterAnnotationsAttributeReader runtimeVisibleParameterAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleParameterAnnotations attribute type."); + + low ??= new LowFreqData(); + low.parameterAnnotations = new object[runtimeVisibleParameterAnnotationsAttribute.Parameters.Count][]; + for (int j = 0; j < runtimeVisibleParameterAnnotationsAttribute.Parameters.Count; j++) + { + var parameter = runtimeVisibleParameterAnnotationsAttribute.Parameters[j]; + low.parameterAnnotations[j] = new object[parameter.Annotations.Count]; + for (int k = 0; k < parameter.Annotations.Count; k++) + low.parameterAnnotations[j][k] = ReadAnnotation(parameter.Annotations[k], classFile, utf8_cp); + } + + break; + case "AnnotationDefault": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not AnnotationDefaultAttributeReader annotationDefaultAttribute) + throw new ClassFormatError("Invalid AnnotationDefault attribute type."); + + low ??= new LowFreqData(); + low.annotationDefault = ReadAnnotationElementValue(annotationDefaultAttribute.DefaultValue, classFile, utf8_cp); + + break; +#if IMPORTER + case "RuntimeInvisibleAnnotations": + if (classFile.MajorVersion < 49) + goto default; + + if (attribute is not RuntimeInvisibleAnnotationsAttributeReader runtimeInvisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeInvisibleAnnotations attribute type."); + + foreach (object[] annot in ReadAnnotations(runtimeInvisibleAnnotationsAttribute.Annotations, classFile, utf8_cp)) + { + if (annot[1].Equals("Likvm/lang/Internal;")) + { + if (classFile.IsInterface) + { + StaticCompiler.IssueMessage(Message.InterfaceMethodCantBeInternal, classFile.Name, Name, Signature); + } + else + { + accessFlags &= ~Modifiers.AccessMask; + flags |= FLAG_MASK_INTERNAL; + } + } + else if (annot[1].Equals("Likvm/lang/DllExport;")) + { + string name = null; + int? ordinal = null; + for (int j = 2; j < annot.Length; j += 2) + { + if (annot[j].Equals("name") && annot[j + 1] is string) + name = (string)annot[j + 1]; + else if (annot[j].Equals("ordinal") && annot[j + 1] is int) + ordinal = (int)annot[j + 1]; + } + if (name != null && ordinal != null) + { + if (!IsStatic) + { + StaticCompiler.IssueMessage(Message.DllExportMustBeStaticMethod, classFile.Name, this.Name, this.Signature); + } + else + { + low ??= new LowFreqData(); + low.DllExportName = name; + low.DllExportOrdinal = ordinal.Value; + } + } + } + else if (annot[1].Equals("Likvm/internal/InterlockedCompareAndSet;")) + { + string field = null; + for (int j = 2; j < annot.Length; j += 2) + if (annot[j].Equals("value") && annot[j + 1] is string) + field = (string)annot[j + 1]; + + if (field != null) + { + low ??= new LowFreqData(); + low.InterlockedCompareAndSetField = field; + } + } + } + + break; #endif - case "MethodParameters": - { - if(classFile.MajorVersion < 52) - { - goto default; - } - if(parameters != null) - { - throw new ClassFormatError("{0} (Duplicate MethodParameters attribute)", classFile.Name); - } - parameters = ReadMethodParameters(br, utf8_cp); - break; - } - case "RuntimeVisibleTypeAnnotations": - if (classFile.MajorVersion < 52) - { - goto default; - } - classFile.CreateUtf8ConstantPoolItems(utf8_cp); - runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray(); - break; - default: - br.Skip(br.ReadUInt32()); - break; - } - } - if(IsAbstract || IsNative) - { - if(!code.IsEmpty) - { - throw new ClassFormatError("Code attribute in native or abstract methods in class file " + classFile.Name); - } - } - else - { - if(code.IsEmpty) - { - if(ReferenceEquals(this.Name, StringConstants.CLINIT)) - { - code.verifyError = string.Format("Class {0}, method {1} signature {2}: No Code attribute", classFile.Name, this.Name, this.Signature); - return; - } - throw new ClassFormatError("Absent Code attribute in method that is not native or abstract in class file " + classFile.Name); - } - } - } - - private static MethodParametersEntry[] ReadMethodParameters(BigEndianBinaryReader br, string[] utf8_cp) - { - uint length = br.ReadUInt32(); - if(length > 0) - { - BigEndianBinaryReader rdr = br.Section(length); - byte parameters_count = rdr.ReadByte(); - if(length == 1 + parameters_count * 4) - { - MethodParametersEntry[] parameters = new MethodParametersEntry[parameters_count]; - for(int j = 0; j < parameters_count; j++) - { - ushort name = rdr.ReadUInt16(); - if(name >= utf8_cp.Length || (name != 0 && utf8_cp[name] == null)) - { - return MethodParametersEntry.Malformed; - } - parameters[j].name = utf8_cp[name]; - parameters[j].flags = rdr.ReadUInt16(); - } - return parameters; - } - } - throw new ClassFormatError("Invalid MethodParameters method attribute length " + length + " in class file"); - } - - protected override void ValidateSig(ClassFile classFile, string descriptor) - { - if(!IsValidMethodSig(descriptor)) - { - throw new ClassFormatError("{0} (Method \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor); - } - } - - internal bool IsStrictfp - { - get - { - return (access_flags & Modifiers.Strictfp) != 0; - } - } - - internal bool IsVirtual - { - get - { - return (access_flags & (Modifiers.Static | Modifiers.Private)) == 0 - && !IsConstructor; - } - } - - // Is this the ()V method? - internal bool IsClassInitializer - { - get - { - return ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && IsStatic; - } - } - - internal bool IsConstructor - { - get - { - return ReferenceEquals(Name, StringConstants.INIT); - } - } - -#if STATIC_COMPILER - internal bool IsCallerSensitive - { - get - { - return (flags & FLAG_CALLERSENSITIVE) != 0; - } - } + case "MethodParameters": + if (classFile.MajorVersion < 52) + goto default; + + if (attribute is not MethodParametersAttributeReader methodParametersAttribute) + throw new ClassFormatError("Invalid attribute reader type."); + + if (parameters != null) + throw new ClassFormatError("{0} (Duplicate MethodParameters attribute)", classFile.Name); + + parameters = ReadMethodParameters(methodParametersAttribute.Parameters, utf8_cp); + + break; + case "RuntimeVisibleTypeAnnotations": + if (classFile.MajorVersion < 52) + goto default; + + if (attribute is not RuntimeVisibleTypeAnnotationsAttributeReader runtimeVisibleTypeAnnotationsAttribute) + throw new ClassFormatError("Invalid attribute reader type."); + + classFile.CreateUtf8ConstantPoolItems(utf8_cp); + runtimeVisibleTypeAnnotations = runtimeVisibleTypeAnnotationsAttribute.Annotations; + break; + default: + break; + } + } + if (IsAbstract || IsNative) + { + if (!code.IsEmpty) + { + throw new ClassFormatError("Code attribute in native or abstract methods in class file " + classFile.Name); + } + } + else + { + if (code.IsEmpty) + { + if (ReferenceEquals(this.Name, StringConstants.CLINIT)) + { + code.verifyError = string.Format("Class {0}, method {1} signature {2}: No Code attribute", classFile.Name, this.Name, this.Signature); + return; + } + throw new ClassFormatError("Absent Code attribute in method that is not native or abstract in class file " + classFile.Name); + } + } + } + + private static MethodParametersEntry[] ReadMethodParameters(IReadOnlyList parameters, string[] utf8_cp) + { + var l = new MethodParametersEntry[parameters.Count]; + + for (int i = 0; i < parameters.Count; i++) + { + var name = parameters[i].Record.NameIndex; + if (name >= utf8_cp.Length || (name != 0 && utf8_cp[name] == null)) + return MethodParametersEntry.Malformed; + + l[i].name = utf8_cp[name]; + l[i].accessFlags = parameters[i].AccessFlags; + } + + return l; + } + + protected override void ValidateSig(ClassFile classFile, string descriptor) + { + if (!IsValidMethodSig(descriptor)) + { + throw new ClassFormatError("{0} (Method \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor); + } + } + + internal bool IsStrictfp => (accessFlags & Modifiers.Strictfp) != 0; + + internal bool IsVirtual => (accessFlags & (Modifiers.Static | Modifiers.Private)) == 0 && !IsConstructor; + + // Is this the ()V method? + internal bool IsClassInitializer => ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID) && IsStatic; + + internal bool IsConstructor => ReferenceEquals(Name, StringConstants.INIT); + +#if IMPORTER + + internal bool IsCallerSensitive => (flags & FLAG_CALLERSENSITIVE) != 0; + #endif - internal bool IsLambdaFormCompiled - { - get - { - return (flags & FLAG_LAMBDAFORM_COMPILED) != 0; - } - } - - internal bool IsLambdaFormHidden - { - get - { - return (flags & FLAG_LAMBDAFORM_HIDDEN) != 0; - } - } - - internal bool IsForceInline - { - get - { - return (flags & FLAG_FORCEINLINE) != 0; - } - } - - internal string[] ExceptionsAttribute - { - get - { - return exceptions; - } - } - - internal object[][] ParameterAnnotations - { - get - { - return low == null ? null : low.parameterAnnotations; - } - } - - internal object AnnotationDefault - { - get - { - return low == null ? null : low.annotationDefault; - } - } - -#if STATIC_COMPILER - internal string DllExportName - { - get - { - return low == null ? null : low.DllExportName; - } - } - - internal int DllExportOrdinal - { - get - { - return low == null ? -1 : low.DllExportOrdinal; - } - } - - internal string InterlockedCompareAndSetField - { - get - { - return low == null ? null : low.InterlockedCompareAndSetField; - } - } + internal bool IsLambdaFormCompiled => (flags & FLAG_LAMBDAFORM_COMPILED) != 0; + + internal bool IsLambdaFormHidden => (flags & FLAG_LAMBDAFORM_HIDDEN) != 0; + + internal bool IsForceInline => (flags & FLAG_FORCEINLINE) != 0; + + internal string[] ExceptionsAttribute => exceptions; + + internal object[][] ParameterAnnotations => low == null ? null : low.parameterAnnotations; + + internal object AnnotationDefault => low == null ? null : low.annotationDefault; + +#if IMPORTER + + internal string DllExportName => low == null ? null : low.DllExportName; + + internal int DllExportOrdinal => low == null ? -1 : low.DllExportOrdinal; + + internal string InterlockedCompareAndSetField => low == null ? null : low.InterlockedCompareAndSetField; + #endif - internal string VerifyError - { - get - { - return code.verifyError; - } - } - - // maps argument 'slot' (as encoded in the xload/xstore instructions) into the ordinal - internal int[] ArgMap - { - get - { - return code.argmap; - } - } - - internal int MaxStack - { - get - { - return code.max_stack; - } - } - - internal int MaxLocals - { - get - { - return code.max_locals; - } - } - - internal Instruction[] Instructions - { - get - { - return code.instructions; - } - set - { - code.instructions = value; - } - } - - internal ExceptionTableEntry[] ExceptionTable - { - get - { - return code.exception_table; - } - set - { - code.exception_table = value; - } - } - - internal LineNumberTableEntry[] LineNumberTableAttribute - { - get - { - return code.lineNumberTable; - } - } - - internal LocalVariableTableEntry[] LocalVariableTableAttribute - { - get - { - return code.localVariableTable; - } - } - - internal MethodParametersEntry[] MethodParameters - { - get - { - return parameters; - } - } - - internal bool MalformedMethodParameters - { - get - { - return parameters == MethodParametersEntry.Malformed; - } - } - - internal bool HasJsr - { - get - { - return code.hasJsr; - } - } - } - } + internal string VerifyError => code.verifyError; + + // maps argument 'slot' (as encoded in the xload/xstore instructions) into the ordinal + internal int[] ArgMap => code.argmap; + + internal int MaxStack => code.max_stack; + + internal int MaxLocals => code.max_locals; + + internal Instruction[] Instructions + { + get => code.instructions; + set => code.instructions = value; + } + + internal ExceptionTableEntry[] ExceptionTable + { + get => code.exception_table; + set => code.exception_table = value; + } + + internal LineNumberTableEntry[] LineNumberTableAttribute => code.lineNumberTable; + + internal LocalVariableTableEntry[] LocalVariableTableAttribute => code.localVariableTable; + + internal MethodParametersEntry[] MethodParameters => parameters; + + internal bool MalformedMethodParameters => parameters == MethodParametersEntry.Malformed; + + internal bool HasJsr => code.hasJsr; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.SupportedVersions.cs b/src/IKVM.Runtime/ClassFile/ClassFile.SupportedVersions.cs index d67bbdcedc..ec19979d2d 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.SupportedVersions.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.SupportedVersions.cs @@ -26,12 +26,16 @@ namespace IKVM.Internal { sealed partial class ClassFile - { - private static class SupportedVersions - { - internal static readonly int Minimum = 45; - internal static readonly int Maximum = Experimental.JDK_9 ? 53 : 52; - } - } + { + + static class SupportedVersions + { + + internal static readonly int Minimum = 45; + internal static readonly int Maximum = 52; + + } + + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.cs b/src/IKVM.Runtime/ClassFile/ClassFile.cs index 0d4e7b2872..d573c7ae7e 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFile.cs @@ -22,1322 +22,1078 @@ Jeroen Frijters */ using System; +using System.Buffers.Binary; using System.Collections.Generic; +using System.Linq; using IKVM.Attributes; +using IKVM.ByteCode; +using IKVM.ByteCode.Reading; namespace IKVM.Internal { sealed partial class ClassFile - { - - private ConstantPoolItem[] constantpool; - // Modifiers is a ushort, so the next four fields combine into two 32 bit slots - private Modifiers access_flags; - private ushort this_class; - private ushort super_class; - private ushort flags; - private const ushort FLAG_MASK_MAJORVERSION = 0xFF; - private const ushort FLAG_MASK_DEPRECATED = 0x100; - private const ushort FLAG_MASK_INTERNAL = 0x200; - private const ushort FLAG_CALLERSENSITIVE = 0x400; - private const ushort FLAG_LAMBDAFORM_COMPILED = 0x800; - private const ushort FLAG_LAMBDAFORM_HIDDEN = 0x1000; - private const ushort FLAG_FORCEINLINE = 0x2000; - private const ushort FLAG_HAS_ASSERTIONS = 0x4000; - private ConstantPoolItemClass[] interfaces; - private Field[] fields; - private Method[] methods; - private string sourceFile; -#if STATIC_COMPILER - private string sourcePath; + { + + const ushort FLAG_MASK_DEPRECATED = 0x100; + const ushort FLAG_MASK_INTERNAL = 0x200; + const ushort FLAG_CALLERSENSITIVE = 0x400; + const ushort FLAG_LAMBDAFORM_COMPILED = 0x800; + const ushort FLAG_LAMBDAFORM_HIDDEN = 0x1000; + const ushort FLAG_FORCEINLINE = 0x2000; + const ushort FLAG_HAS_ASSERTIONS = 0x4000; + + readonly ClassReader reader; + + ConstantPoolItem[] constantpool; + string[] utf8_cp; + + // Modifiers is a ushort, so the next four fields combine into two 32 bit slots + Modifiers access_flags; + ushort flags; + ConstantPoolItemClass[] interfaces; + Field[] fields; + Method[] methods; + string sourceFile; +#if IMPORTER + string sourcePath; #endif - private string ikvmAssembly; - private InnerClass[] innerClasses; - private object[] annotations; - private string signature; - private string[] enclosingMethod; - private BootstrapMethod[] bootstrapMethods; - private byte[] runtimeVisibleTypeAnnotations; - -#if STATIC_COMPILER - // This method parses just enough of the class file to obtain its name and - // determine if the class is a possible ikvmstub generated stub, it doesn't - // validate the class file structure, but it may throw a ClassFormatError if it - // encounters bogus data - internal static string GetClassName(byte[] buf, int offset, int length, out bool isstub) - { - isstub = false; - BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length); - if(br.ReadUInt32() != 0xCAFEBABE) - { - throw new ClassFormatError("Bad magic number"); - } - int minorVersion = br.ReadUInt16(); - int majorVersion = br.ReadUInt16(); - if((majorVersion & FLAG_MASK_MAJORVERSION) != majorVersion - || majorVersion < SupportedVersions.Minimum - || majorVersion > SupportedVersions.Maximum - || (majorVersion == SupportedVersions.Minimum && minorVersion < 3) - || (majorVersion == SupportedVersions.Maximum && minorVersion != 0)) - { - throw new UnsupportedClassVersionError(majorVersion + "." + minorVersion); - } - int constantpoolcount = br.ReadUInt16(); - int[] cpclass = new int[constantpoolcount]; - string[] utf8_cp = new string[constantpoolcount]; - for(int i = 1; i < constantpoolcount; i++) - { - Constant tag = (Constant)br.ReadByte(); - switch(tag) - { - case Constant.Class: - cpclass[i] = br.ReadUInt16(); - break; - case Constant.Double: - case Constant.Long: - br.Skip(8); - i++; - break; - case Constant.Fieldref: - case Constant.InterfaceMethodref: - case Constant.Methodref: - case Constant.InvokeDynamic: - case Constant.NameAndType: - case Constant.Float: - case Constant.Integer: - br.Skip(4); - break; - case Constant.MethodHandle: - br.Skip(3); - break; - case Constant.String: - case Constant.MethodType: - br.Skip(2); - break; - case Constant.Utf8: - isstub |= (utf8_cp[i] = br.ReadString(null, majorVersion)) == "IKVM.NET.Assembly"; - break; - default: - throw new ClassFormatError("Illegal constant pool type 0x{0:X}", tag); - } - } - br.ReadUInt16(); // access_flags - try - { - return String.Intern(utf8_cp[cpclass[br.ReadUInt16()]].Replace('/', '.')); - } - catch(Exception x) - { - throw new ClassFormatError("{0}: {1}", x.GetType().Name, x.Message); - } - } -#endif // STATIC_COMPILER - - internal ClassFile(byte[] buf, int offset, int length, string inputClassName, ClassFileParseOptions options, object[] constantPoolPatches) - { - try - { - BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length); - if(br.ReadUInt32() != 0xCAFEBABE) - { - throw new ClassFormatError("{0} (Bad magic number)", inputClassName); - } - ushort minorVersion = br.ReadUInt16(); - ushort majorVersion = br.ReadUInt16(); - if((majorVersion & FLAG_MASK_MAJORVERSION) != majorVersion - || majorVersion < SupportedVersions.Minimum - || majorVersion > SupportedVersions.Maximum - || (majorVersion == SupportedVersions.Minimum && minorVersion < 3) - || (majorVersion == SupportedVersions.Maximum && minorVersion != 0)) - { - throw new UnsupportedClassVersionError(inputClassName + " (" + majorVersion + "." + minorVersion + ")"); - } - flags = majorVersion; - int constantpoolcount = br.ReadUInt16(); - constantpool = new ConstantPoolItem[constantpoolcount]; - string[] utf8_cp = new string[constantpoolcount]; - for(int i = 1; i < constantpoolcount; i++) - { - Constant tag = (Constant)br.ReadByte(); - switch(tag) - { - case Constant.Class: - constantpool[i] = new ConstantPoolItemClass(br); - break; - case Constant.Double: - constantpool[i] = new ConstantPoolItemDouble(br); - i++; - break; - case Constant.Fieldref: - constantpool[i] = new ConstantPoolItemFieldref(br); - break; - case Constant.Float: - constantpool[i] = new ConstantPoolItemFloat(br); - break; - case Constant.Integer: - constantpool[i] = new ConstantPoolItemInteger(br); - break; - case Constant.InterfaceMethodref: - constantpool[i] = new ConstantPoolItemInterfaceMethodref(br); - break; - case Constant.Long: - constantpool[i] = new ConstantPoolItemLong(br); - i++; - break; - case Constant.Methodref: - constantpool[i] = new ConstantPoolItemMethodref(br); - break; - case Constant.NameAndType: - constantpool[i] = new ConstantPoolItemNameAndType(br); - break; - case Constant.MethodHandle: - if (majorVersion < 51) - goto default; - constantpool[i] = new ConstantPoolItemMethodHandle(br); - break; - case Constant.MethodType: - if (majorVersion < 51) - goto default; - constantpool[i] = new ConstantPoolItemMethodType(br); - break; - case Constant.InvokeDynamic: - if (majorVersion < 51) - goto default; - constantpool[i] = new ConstantPoolItemInvokeDynamic(br); - break; - case Constant.String: - constantpool[i] = new ConstantPoolItemString(br); - break; - case Constant.Utf8: - utf8_cp[i] = br.ReadString(inputClassName, majorVersion); - break; - default: - throw new ClassFormatError("{0} (Illegal constant pool type 0x{1:X})", inputClassName, tag); - } - } - if (constantPoolPatches != null) - { - PatchConstantPool(constantPoolPatches, utf8_cp, inputClassName); - } - for(int i = 1; i < constantpoolcount; i++) - { - if(constantpool[i] != null) - { - try - { - constantpool[i].Resolve(this, utf8_cp, options); - } - catch(ClassFormatError x) - { - // HACK at this point we don't yet have the class name, so any exceptions throw - // are missing the class name - throw new ClassFormatError("{0} ({1})", inputClassName, x.Message); - } - catch(IndexOutOfRangeException) - { - throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); - } - catch(InvalidCastException) - { - throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); - } - } - } - access_flags = (Modifiers)br.ReadUInt16(); - // NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of - // javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule for older class files. - // NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this - // for older class files. - // (See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320322) - if((IsInterface && IsFinal) - || (IsAbstract && IsFinal) - || (majorVersion >= 49 && IsAnnotation && !IsInterface) - || (majorVersion >= 49 && IsInterface && (!IsAbstract || IsSuper || IsEnum))) - { - throw new ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags); - } - this_class = br.ReadUInt16(); - ValidateConstantPoolItemClass(inputClassName, this_class); - super_class = br.ReadUInt16(); - ValidateConstantPoolItemClass(inputClassName, super_class); - if(IsInterface && (super_class == 0 || this.SuperClass.Name != "java.lang.Object")) - { - throw new ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); - } - // most checks are already done by ConstantPoolItemClass.Resolve, but since it allows - // array types, we do need to check for that - if(this.Name[0] == '[') - { - throw new ClassFormatError("Bad name"); - } - int interfaces_count = br.ReadUInt16(); - interfaces = new ConstantPoolItemClass[interfaces_count]; - for(int i = 0; i < interfaces.Length; i++) - { - int index = br.ReadUInt16(); - if(index == 0 || index >= constantpool.Length) - { - throw new ClassFormatError("{0} (Illegal constant pool index)", Name); - } - ConstantPoolItemClass cpi = constantpool[index] as ConstantPoolItemClass; - if(cpi == null) - { - throw new ClassFormatError("{0} (Interface name has bad constant type)", Name); - } - interfaces[i] = cpi; - } - CheckDuplicates(interfaces, "Repetitive interface name"); - int fields_count = br.ReadUInt16(); - fields = new Field[fields_count]; - for(int i = 0; i < fields_count; i++) - { - fields[i] = new Field(this, utf8_cp, br); - string name = fields[i].Name; - if(!IsValidFieldName(name, majorVersion)) - { - throw new ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name); - } - } - CheckDuplicates(fields, "Repetitive field name/signature"); - int methods_count = br.ReadUInt16(); - methods = new Method[methods_count]; - for(int i = 0; i < methods_count; i++) - { - methods[i] = new Method(this, utf8_cp, options, br); - string name = methods[i].Name; - string sig = methods[i].Signature; - if(!IsValidMethodName(name, majorVersion)) - { - if(!ReferenceEquals(name, StringConstants.INIT) && !ReferenceEquals(name, StringConstants.CLINIT)) - { - throw new ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); - } - if(!sig.EndsWith("V")) - { - throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); - } - if((options & ClassFileParseOptions.RemoveAssertions) != 0 && methods[i].IsClassInitializer) - { - RemoveAssertionInit(methods[i]); - } - } - } - CheckDuplicates(methods, "Repetitive method name/signature"); - int attributes_count = br.ReadUInt16(); - for(int i = 0; i < attributes_count; i++) - { - switch(GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())) - { - case "Deprecated": - if(br.ReadUInt32() != 0) - { - throw new ClassFormatError("Invalid Deprecated attribute length"); - } - flags |= FLAG_MASK_DEPRECATED; - break; - case "SourceFile": - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("SourceFile attribute has incorrect length"); - } - sourceFile = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - break; - case "InnerClasses": - { - BigEndianBinaryReader rdr = br; - uint attribute_length = br.ReadUInt32(); - ushort count = rdr.ReadUInt16(); - if(this.MajorVersion >= 49 && attribute_length != 2 + count * (2 + 2 + 2 + 2)) - { - throw new ClassFormatError("{0} (InnerClasses attribute has incorrect length)", this.Name); - } - innerClasses = new InnerClass[count]; - for(int j = 0; j < innerClasses.Length; j++) - { - innerClasses[j].innerClass = rdr.ReadUInt16(); - innerClasses[j].outerClass = rdr.ReadUInt16(); - innerClasses[j].name = rdr.ReadUInt16(); - innerClasses[j].accessFlags = (Modifiers)rdr.ReadUInt16(); - if(innerClasses[j].innerClass != 0 && !(GetConstantPoolItem(innerClasses[j].innerClass) is ConstantPoolItemClass)) - { - throw new ClassFormatError("{0} (inner_class_info_index has bad constant pool index)", this.Name); - } - if(innerClasses[j].outerClass != 0 && !(GetConstantPoolItem(innerClasses[j].outerClass) is ConstantPoolItemClass)) - { - throw new ClassFormatError("{0} (outer_class_info_index has bad constant pool index)", this.Name); - } - if(innerClasses[j].name != 0 && utf8_cp[innerClasses[j].name] == null) - { - throw new ClassFormatError("{0} (inner class name has bad constant pool index)", this.Name); - } - if(innerClasses[j].innerClass == innerClasses[j].outerClass) - { - throw new ClassFormatError("{0} (Class is both inner and outer class)", this.Name); - } - if(innerClasses[j].innerClass != 0 && innerClasses[j].outerClass != 0) - { - MarkLinkRequiredConstantPoolItem(innerClasses[j].innerClass); - MarkLinkRequiredConstantPoolItem(innerClasses[j].outerClass); - } - } - break; - } - case "Signature": - if(majorVersion < 49) - { - goto default; - } - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("Signature attribute has incorrect length"); - } - signature = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - break; - case "EnclosingMethod": - if(majorVersion < 49) - { - goto default; - } - if(br.ReadUInt32() != 4) - { - throw new ClassFormatError("EnclosingMethod attribute has incorrect length"); - } - else - { - ushort class_index = br.ReadUInt16(); - ushort method_index = br.ReadUInt16(); - ValidateConstantPoolItemClass(inputClassName, class_index); - if(method_index == 0) - { - enclosingMethod = new string[] { - GetConstantPoolClass(class_index), - null, - null - }; - } - else - { - ConstantPoolItemNameAndType m = GetConstantPoolItem(method_index) as ConstantPoolItemNameAndType; - if(m == null) - { - throw new ClassFormatError("{0} (Bad constant pool index #{1})", inputClassName, method_index); - } - enclosingMethod = new string[] { - GetConstantPoolClass(class_index), - GetConstantPoolUtf8String(utf8_cp, m.name_index), - GetConstantPoolUtf8String(utf8_cp, m.descriptor_index).Replace('/', '.') - }; - } - } - break; - case "RuntimeVisibleAnnotations": - if(majorVersion < 49) - { - goto default; - } - annotations = ReadAnnotations(br, this, utf8_cp); - break; -#if STATIC_COMPILER - case "RuntimeInvisibleAnnotations": - if(majorVersion < 49) - { - goto default; - } - foreach(object[] annot in ReadAnnotations(br, this, utf8_cp)) - { - if(annot[1].Equals("Likvm/lang/Internal;")) - { - this.access_flags &= ~Modifiers.AccessMask; - flags |= FLAG_MASK_INTERNAL; - } - } - break; + string ikvmAssembly; + InnerClass[] innerClasses; + object[] annotations; + string signature; + string[] enclosingMethod; + BootstrapMethod[] bootstrapMethods; + TypeAnnotationReaderCollection runtimeVisibleTypeAnnotations; + +#if IMPORTER + + /// + /// This method returns the class name, and whether or not the class is an IKVM stub. + /// + /// + /// + /// + /// + /// + internal static string GetClassName(byte[] bytes, int offset, int length, out bool isstub) + { + return GetClassName(ClassReader.Read(bytes.AsMemory(offset, length)), out isstub); + } + + /// + /// This method returns the class name, and whether or not the class is an IKVM stub. + /// + /// + /// + /// + /// + /// + static string GetClassName(ClassReader reader, out bool isstub) + { + try + { + if (reader.Version < new ClassFormatVersion(45, 3) || reader.Version > 52) + throw new UnsupportedClassVersionError(reader.Version); + + // this is a terrible way to go about encoding this information + isstub = reader.Constants.OfType().Any(i => i.Value == "IKVM.NET.Assembly"); + + return string.Intern(reader.This.Name.Value.Replace('/', '.')); + } + catch (ByteCodeException e) + { + throw new ClassFormatError(e.Message); + } + } + +#endif + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + internal ClassFile(ClassReader reader, string inputClassName, ClassFileParseOptions options, object[] constantPoolPatches) + { + this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); + + try + { + if (reader.Version < new ClassFormatVersion(45, 3) || reader.Version > 52) + throw new UnsupportedClassVersionError(reader.Version); + + constantpool = new ConstantPoolItem[reader.Constants.Count]; + utf8_cp = new string[reader.Constants.Count]; + for (int i = 1; i < reader.Constants.Count; i++) + { + switch (reader.Constants[i]) + { + case null: + // longs and doubles can leave holes in the constant pool + break; + case ClassConstantReader clazzConstant: + constantpool[i] = new ConstantPoolItemClass(clazzConstant); + break; + case DoubleConstantReader doubleConstant: + constantpool[i] = new ConstantPoolItemDouble(doubleConstant); + break; + case FieldrefConstantReader fieldRefConstant: + constantpool[i] = new ConstantPoolItemFieldref(fieldRefConstant); + break; + case FloatConstantReader floatConstant: + constantpool[i] = new ConstantPoolItemFloat(floatConstant); + break; + case IntegerConstantReader integerConstant: + constantpool[i] = new ConstantPoolItemInteger(integerConstant); + break; + case InterfaceMethodrefConstantReader interfaceMethodrefConstant: + constantpool[i] = new ConstantPoolItemInterfaceMethodref(interfaceMethodrefConstant); + break; + case LongConstantReader longConstant: + constantpool[i] = new ConstantPoolItemLong(longConstant); + break; + case MethodrefConstantReader methodrefConstantReader: + constantpool[i] = new ConstantPoolItemMethodref(methodrefConstantReader); + break; + case NameAndTypeConstantReader nameAndType: + constantpool[i] = new ConstantPoolItemNameAndType(nameAndType); + break; + case MethodHandleConstantReader methodHandle: + if (reader.Version < 51) + goto default; + constantpool[i] = new ConstantPoolItemMethodHandle(methodHandle); + break; + case MethodTypeConstantReader methodType: + if (reader.Version < 51) + goto default; + constantpool[i] = new ConstantPoolItemMethodType(methodType); + break; + case InvokeDynamicConstantReader invokeDynamic: + if (reader.Version < 51) + goto default; + constantpool[i] = new ConstantPoolItemInvokeDynamic(invokeDynamic); + break; + case StringConstantReader stringConstant: + constantpool[i] = new ConstantPoolItemString(stringConstant); + break; + case Utf8ConstantReader utf8ConstantReader: + utf8_cp[i] = utf8ConstantReader.Value; + break; + default: + throw new ClassFormatError("Unknown constant type."); + } + } + + if (constantPoolPatches != null) + PatchConstantPool(constantPoolPatches, utf8_cp, inputClassName); + + for (int i = 1; i < reader.Constants.Count; i++) + { + if (constantpool[i] != null) + { + try + { + constantpool[i].Resolve(this, utf8_cp, options); + } + catch (ClassFormatError x) + { + // HACK at this point we don't yet have the class name, so any exceptions throw + // are missing the class name + throw new ClassFormatError("{0} ({1})", inputClassName, x.Message); + } + catch (IndexOutOfRangeException) + { + throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); + } + catch (InvalidCastException) + { + throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); + } + } + } + + access_flags = (Modifiers)reader.AccessFlags; + + // NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of + // javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule for older class files. + // NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this + // for older class files. + // (See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320322) + if ((IsInterface && IsFinal) || (IsAbstract && IsFinal) || (reader.Version >= 49 && IsAnnotation && !IsInterface) || (reader.Version >= 49 && IsInterface && (!IsAbstract || IsSuper || IsEnum))) + throw new ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags); + + ValidateConstantPoolItemClass(inputClassName, reader.Record.ThisClassIndex); + ValidateConstantPoolItemClass(inputClassName, reader.Record.SuperClassIndex); + + if (IsInterface && (reader.Record.SuperClassIndex == 0 || SuperClass.Name != "java.lang.Object")) + throw new ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); + + // most checks are already done by ConstantPoolItemClass.Resolve, but since it allows + // array types, we do need to check for that + if (Name[0] == '[') + throw new ClassFormatError("Bad name"); + + interfaces = new ConstantPoolItemClass[reader.Interfaces.Count]; + for (int i = 0; i < interfaces.Length; i++) + { + int index = reader.Interfaces[i].Record.ClassIndex; + if (index == 0 || index >= constantpool.Length) + throw new ClassFormatError("{0} (Illegal constant pool index)", Name); + + var cpi = constantpool[index] as ConstantPoolItemClass; + if (cpi == null) + throw new ClassFormatError("{0} (Interface name has bad constant type)", Name); + + interfaces[i] = cpi; + } + + CheckDuplicates(interfaces, "Repetitive interface name"); + + fields = new Field[reader.Fields.Count]; + for (int i = 0; i < reader.Fields.Count; i++) + { + fields[i] = new Field(this, utf8_cp, reader.Fields[i]); + var name = fields[i].Name; + + if (!IsValidFieldName(name, reader.Version)) + throw new ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name); + } + + CheckDuplicates(fields, "Repetitive field name/signature"); + + methods = new Method[reader.Methods.Count]; + for (int i = 0; i < reader.Methods.Count; i++) + { + methods[i] = new Method(this, utf8_cp, options, reader.Methods[i]); + string name = methods[i].Name; + string sig = methods[i].Signature; + if (!IsValidMethodName(name, reader.Version)) + { + if (!ReferenceEquals(name, StringConstants.INIT) && !ReferenceEquals(name, StringConstants.CLINIT)) + throw new ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); + if (!sig.EndsWith("V")) + throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); + if ((options & ClassFileParseOptions.RemoveAssertions) != 0 && methods[i].IsClassInitializer) + RemoveAssertionInit(methods[i]); + } + } + + CheckDuplicates(methods, "Repetitive method name/signature"); + + for (int i = 0; i < reader.Attributes.Count; i++) + { + var attribute = reader.Attributes[i]; + + switch (GetConstantPoolUtf8String(utf8_cp, attribute.Info.Record.NameIndex)) + { + case "Deprecated": + if (attribute is not DeprecatedAttributeReader deprecatedAttribute) + throw new ClassFormatError("Invalid Deprecated attribute type."); + + flags |= FLAG_MASK_DEPRECATED; + break; + case "SourceFile": + if (attribute is not SourceFileAttributeReader sourceFileAttribute) + throw new ClassFormatError("Invalid SourceFile attribute type."); + + sourceFile = GetConstantPoolUtf8String(utf8_cp, sourceFileAttribute.Record.SourceFileIndex); + break; + case "InnerClasses": + if (MajorVersion < 49) + goto default; + + if (attribute is not InnerClassesAttributeReader innerClassesAttribute) + throw new ClassFormatError("Invalid InnerClasses attribute type."); + + innerClasses = new InnerClass[innerClassesAttribute.Items.Count]; + for (int j = 0; j < innerClasses.Length; j++) + { + var item = innerClassesAttribute.Items[j]; + + innerClasses[j].innerClass = item.InnerClass?.Index ?? 0; + innerClasses[j].outerClass = item.OuterClass?.Index ?? 0; + innerClasses[j].name = item.InnerName?.Index ?? 0; + innerClasses[j].accessFlags = (Modifiers)item.InnerClassAccessFlags; + + if (innerClasses[j].innerClass != 0 && !(GetConstantPoolItem(innerClasses[j].innerClass) is ConstantPoolItemClass)) + throw new ClassFormatError("{0} (inner_class_info_index has bad constant pool index)", this.Name); + + if (innerClasses[j].outerClass != 0 && !(GetConstantPoolItem(innerClasses[j].outerClass) is ConstantPoolItemClass)) + throw new ClassFormatError("{0} (outer_class_info_index has bad constant pool index)", this.Name); + + if (innerClasses[j].name != 0 && utf8_cp[innerClasses[j].name] == null) + throw new ClassFormatError("{0} (inner class name has bad constant pool index)", this.Name); + + if (innerClasses[j].innerClass == innerClasses[j].outerClass) + throw new ClassFormatError("{0} (Class is both inner and outer class)", this.Name); + + if (innerClasses[j].innerClass != 0 && innerClasses[j].outerClass != 0) + { + MarkLinkRequiredConstantPoolItem(innerClasses[j].innerClass); + MarkLinkRequiredConstantPoolItem(innerClasses[j].outerClass); + } + } + + break; + case "Signature": + if (reader.Version < 49) + goto default; + + if (attribute is not SignatureAttributeReader signatureAttribute) + throw new ClassFormatError("Invalid Signature attribute type."); + + signature = GetConstantPoolUtf8String(utf8_cp, signatureAttribute.Record.SignatureIndex); + break; + case "EnclosingMethod": + if (reader.Version < 49) + goto default; + + if (attribute is not EnclosingMethodAttributeReader enclosingMethodAttribute) + throw new ClassFormatError("Invalid EnclosingMethod attribute type."); + + var classIndex = enclosingMethodAttribute.Record.ClassIndex; + var methodIndex = enclosingMethodAttribute.Record.MethodIndex; + ValidateConstantPoolItemClass(inputClassName, classIndex); + + if (methodIndex == 0) + { + enclosingMethod = new string[] + { + GetConstantPoolClass(classIndex), + null, + null + }; + } + else + { + if (GetConstantPoolItem(methodIndex) is not ConstantPoolItemNameAndType m) + throw new ClassFormatError("{0} (Bad constant pool index #{1})", inputClassName, methodIndex); + + enclosingMethod = new string[] + { + GetConstantPoolClass(classIndex), + GetConstantPoolUtf8String(utf8_cp, m.nameIndex), + GetConstantPoolUtf8String(utf8_cp, m.descriptorIndex).Replace('/', '.') + }; + } + + break; + case "RuntimeVisibleAnnotations": + if (reader.Version < 49) + goto default; + + if (attribute is not RuntimeVisibleAnnotationsAttributeReader runtimeVisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleAnnotations attribute type."); + + annotations = ReadAnnotations(runtimeVisibleAnnotationsAttribute.Annotations, this, utf8_cp); + break; +#if IMPORTER + case "RuntimeInvisibleAnnotations": + if (reader.Version < 49) + goto default; + + if (attribute is not RuntimeInvisibleAnnotationsAttributeReader runtimeInvisibleAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeInvisibleAnnotations attribute type."); + + foreach (object[] annot in ReadAnnotations(runtimeInvisibleAnnotationsAttribute.Annotations, this, utf8_cp)) + { + if (annot[1].Equals("Likvm/lang/Internal;")) + { + access_flags &= ~Modifiers.AccessMask; + flags |= FLAG_MASK_INTERNAL; + } + } + + break; #endif - case "BootstrapMethods": - if(majorVersion < 51) - { - goto default; - } - bootstrapMethods = ReadBootstrapMethods(br, this); - break; - case "RuntimeVisibleTypeAnnotations": - if(majorVersion < 52) - { - goto default; - } - CreateUtf8ConstantPoolItems(utf8_cp); - runtimeVisibleTypeAnnotations = br.Section(br.ReadUInt32()).ToArray(); - break; - case "IKVM.NET.Assembly": - if(br.ReadUInt32() != 2) - { - throw new ClassFormatError("IKVM.NET.Assembly attribute has incorrect length"); - } - ikvmAssembly = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); - break; - default: - br.Skip(br.ReadUInt32()); - break; - } - } - // validate the invokedynamic entries to point into the bootstrapMethods array - for(int i = 1; i < constantpoolcount; i++) - { - ConstantPoolItemInvokeDynamic cpi; - if(constantpool[i] != null - && (cpi = constantpool[i] as ConstantPoolItemInvokeDynamic) != null) - { - if(bootstrapMethods == null || cpi.BootstrapMethod >= bootstrapMethods.Length) - { - throw new ClassFormatError("Short length on BootstrapMethods in class file"); - } - } - } - if(br.Position != offset + length) - { - throw new ClassFormatError("Extra bytes at the end of the class file"); - } - } - catch(OverflowException) - { - throw new ClassFormatError("Truncated class file (or section)"); - } - catch(IndexOutOfRangeException) - { - // TODO we should throw more specific errors - throw new ClassFormatError("Unspecified class file format error"); - } - // catch(Exception x) - // { - // Console.WriteLine(x); - // FileStream fs = File.Create(inputClassName + ".broken"); - // fs.Write(buf, offset, length); - // fs.Close(); - // throw; - // } - } - - private void CreateUtf8ConstantPoolItems(string[] utf8_cp) - { - for (int i = 0; i < constantpool.Length; i++) - { - if (constantpool[i] == null && utf8_cp[i] != null) - { - constantpool[i] = new ConstantPoolItemUtf8(utf8_cp[i]); - } - } - } - - private void CheckDuplicates(T[] members, string msg) - where T : IEquatable - { - if (members.Length < 100) - { - for (int i = 0; i < members.Length; i++) - { - for (int j = 0; j < i; j++) - { - if (members[i].Equals(members[j])) - { - throw new ClassFormatError("{0} ({1})", Name, msg); - } - } - } - } - else - { - Dictionary dict = new Dictionary(); - for (int i = 0; i < members.Length; i++) - { - if (dict.ContainsKey(members[i])) - { - throw new ClassFormatError("{0} ({1})", Name, msg); - } - dict.Add(members[i], null); - } - } - } - - private void PatchConstantPool(object[] constantPoolPatches, string[] utf8_cp, string inputClassName) - { -#if !STATIC_COMPILER && !FIRST_PASS - for (int i = 0; i < constantPoolPatches.Length; i++) - { - if (constantPoolPatches[i] != null) - { - if (utf8_cp[i] != null) - { - if (!(constantPoolPatches[i] is string)) - { - throw new ClassFormatError("Illegal utf8 patch at {0} in class file {1}", i, inputClassName); - } - utf8_cp[i] = (string)constantPoolPatches[i]; - } - else if (constantpool[i] != null) - { - switch (constantpool[i].GetConstantType()) - { - case ConstantType.String: - constantpool[i] = new ConstantPoolItemLiveObject(constantPoolPatches[i]); - break; - case ConstantType.Class: - java.lang.Class clazz; - string name; - if ((clazz = constantPoolPatches[i] as java.lang.Class) != null) - { - TypeWrapper tw = TypeWrapper.FromClass(clazz); - constantpool[i] = new ConstantPoolItemClass(tw.Name, tw); - } - else if ((name = constantPoolPatches[i] as string) != null) - { - constantpool[i] = new ConstantPoolItemClass(String.Intern(name.Replace('/', '.')), null); - } - else - { - throw new ClassFormatError("Illegal class patch at {0} in class file {1}", i, inputClassName); - } - break; - case ConstantType.Integer: - ((ConstantPoolItemInteger)constantpool[i]).v = ((java.lang.Integer)constantPoolPatches[i]).intValue(); - break; - case ConstantType.Long: - ((ConstantPoolItemLong)constantpool[i]).l = ((java.lang.Long)constantPoolPatches[i]).longValue(); - break; - case ConstantType.Float: - ((ConstantPoolItemFloat)constantpool[i]).v = ((java.lang.Float)constantPoolPatches[i]).floatValue(); - break; - case ConstantType.Double: - ((ConstantPoolItemDouble)constantpool[i]).d = ((java.lang.Double)constantPoolPatches[i]).doubleValue(); - break; - default: - throw new NotImplementedException("ConstantPoolPatch: " + constantPoolPatches[i]); - } - } - } - } + case "BootstrapMethods": + if (reader.Version < 51) + goto default; + + if (attribute is not BootstrapMethodsAttributeReader bootstrapMethodsAttribute) + throw new ClassFormatError("Invalid BootstrapMethods attribute type."); + + bootstrapMethods = ReadBootstrapMethods(bootstrapMethodsAttribute.Methods, this); + break; + case "RuntimeVisibleTypeAnnotations": + if (reader.Version < 52) + goto default; + + if (attribute is not RuntimeVisibleTypeAnnotationsAttributeReader runtimeVisibleTypeAnnotationsAttribute) + throw new ClassFormatError("Invalid RuntimeVisibleTypeAnnotations attribute type."); + + CreateUtf8ConstantPoolItems(utf8_cp); + runtimeVisibleTypeAnnotations = runtimeVisibleTypeAnnotationsAttribute.Annotations; + break; + case "IKVM.NET.Assembly": + if (attribute is not UnknownAttributeReader unknownAttributeReader) + throw new ClassFormatError("Invalid IKVM.NET.Assembly attribute type."); + + if (unknownAttributeReader.Record.Data.Length != 2) + throw new ClassFormatError("IKVM.NET.Assembly attribute has incorrect length"); + + ikvmAssembly = GetConstantPoolUtf8String(utf8_cp, BinaryPrimitives.ReadInt16BigEndian(unknownAttributeReader.Record.Data)); + break; + default: + break; + } + } + + // validate the invokedynamic entries to point into the bootstrapMethods array + for (int i = 1; i < constantpool.Length; i++) + if (constantpool[i] != null && constantpool[i] is ConstantPoolItemInvokeDynamic cpi) + if (bootstrapMethods == null || cpi.BootstrapMethod >= bootstrapMethods.Length) + throw new ClassFormatError("Short length on BootstrapMethods in class file"); + } + catch (OverflowException) + { + throw new ClassFormatError("Truncated class file (or section)"); + } + catch (IndexOutOfRangeException) + { + throw new ClassFormatError("Unspecified class file format error"); + } + catch (ByteCodeException) + { + throw new ClassFormatError("Unspecified class file format error"); + } + } + + void CreateUtf8ConstantPoolItems(string[] utf8_cp) + { + for (int i = 0; i < constantpool.Length; i++) + if (constantpool[i] == null && utf8_cp[i] != null) + constantpool[i] = new ConstantPoolItemUtf8(utf8_cp[i]); + } + + void CheckDuplicates(T[] members, string msg) + where T : IEquatable + { + if (members.Length < 100) + { + for (int i = 0; i < members.Length; i++) + for (int j = 0; j < i; j++) + if (members[i].Equals(members[j])) + throw new ClassFormatError("{0} ({1})", Name, msg); + } + else + { + var hs = new HashSet(); + for (int i = 0; i < members.Length; i++) + if (hs.Add(members[i]) == false) + throw new ClassFormatError("{0} ({1})", Name, msg); + } + } + + void PatchConstantPool(object[] constantPoolPatches, string[] utf8_cp, string inputClassName) + { +#if !IMPORTER && !FIRST_PASS + for (int i = 0; i < constantPoolPatches.Length; i++) + { + if (constantPoolPatches[i] != null) + { + if (utf8_cp[i] != null) + { + if (!(constantPoolPatches[i] is string)) + { + throw new ClassFormatError("Illegal utf8 patch at {0} in class file {1}", i, inputClassName); + } + utf8_cp[i] = (string)constantPoolPatches[i]; + } + else if (constantpool[i] != null) + { + switch (constantpool[i].GetConstantType()) + { + case ConstantType.String: + constantpool[i] = new ConstantPoolItemLiveObject(constantPoolPatches[i]); + break; + case ConstantType.Class: + java.lang.Class clazz; + string name; + if ((clazz = constantPoolPatches[i] as java.lang.Class) != null) + { + TypeWrapper tw = TypeWrapper.FromClass(clazz); + constantpool[i] = new ConstantPoolItemClass(tw.Name, tw); + } + else if ((name = constantPoolPatches[i] as string) != null) + { + constantpool[i] = new ConstantPoolItemClass(String.Intern(name.Replace('/', '.')), null); + } + else + { + throw new ClassFormatError("Illegal class patch at {0} in class file {1}", i, inputClassName); + } + break; + case ConstantType.Integer: + ((ConstantPoolItemInteger)constantpool[i]).v = ((java.lang.Integer)constantPoolPatches[i]).intValue(); + break; + case ConstantType.Long: + ((ConstantPoolItemLong)constantpool[i]).l = ((java.lang.Long)constantPoolPatches[i]).longValue(); + break; + case ConstantType.Float: + ((ConstantPoolItemFloat)constantpool[i]).v = ((java.lang.Float)constantPoolPatches[i]).floatValue(); + break; + case ConstantType.Double: + ((ConstantPoolItemDouble)constantpool[i]).d = ((java.lang.Double)constantPoolPatches[i]).doubleValue(); + break; + default: + throw new NotImplementedException("ConstantPoolPatch: " + constantPoolPatches[i]); + } + } + } + } #endif - } - - private void MarkLinkRequiredConstantPoolItem(int index) - { - if (index > 0 && index < constantpool.Length && constantpool[index] != null) - { - constantpool[index].MarkLinkRequired(); - } - } - - private static BootstrapMethod[] ReadBootstrapMethods(BigEndianBinaryReader br, ClassFile classFile) - { - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - ushort count = rdr.ReadUInt16(); - BootstrapMethod[] bsm = new BootstrapMethod[count]; - for(int i = 0; i < bsm.Length; i++) - { - ushort bsm_index = rdr.ReadUInt16(); - if(bsm_index >= classFile.constantpool.Length || !(classFile.constantpool[bsm_index] is ConstantPoolItemMethodHandle)) - { - throw new ClassFormatError("bootstrap_method_index {0} has bad constant type in class file {1}", bsm_index, classFile.Name); - } - classFile.MarkLinkRequiredConstantPoolItem(bsm_index); - ushort argument_count = rdr.ReadUInt16(); - ushort[] args = new ushort[argument_count]; - for(int j = 0; j < args.Length; j++) - { - ushort argument_index = rdr.ReadUInt16(); - if(!classFile.IsValidConstant(argument_index)) - { - throw new ClassFormatError("argument_index {0} has bad constant type in class file {1}", argument_index, classFile.Name); - } - classFile.MarkLinkRequiredConstantPoolItem(argument_index); - args[j] = argument_index; - } - bsm[i] = new BootstrapMethod(bsm_index, args); - } - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("Bad length on BootstrapMethods in class file {0}", classFile.Name); - } - return bsm; - } - - private bool IsValidConstant(ushort index) - { - if(index < constantpool.Length && constantpool[index] != null) - { - try - { - constantpool[index].GetConstantType(); - return true; - } - catch (InvalidOperationException) { } - } - return false; - } - - private static object[] ReadAnnotations(BigEndianBinaryReader br, ClassFile classFile, string[] utf8_cp) - { - BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); - ushort num_annotations = rdr.ReadUInt16(); - object[] annotations = new object[num_annotations]; - for(int i = 0; i < annotations.Length; i++) - { - annotations[i] = ReadAnnotation(rdr, classFile, utf8_cp); - } - if(!rdr.IsAtEnd) - { - throw new ClassFormatError("{0} (RuntimeVisibleAnnotations attribute has wrong length)", classFile.Name); - } - return annotations; - } - - private static object ReadAnnotation(BigEndianBinaryReader rdr, ClassFile classFile, string[] utf8_cp) - { - string type = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()); - ushort num_element_value_pairs = rdr.ReadUInt16(); - object[] annot = new object[2 + num_element_value_pairs * 2]; - annot[0] = AnnotationDefaultAttribute.TAG_ANNOTATION; - annot[1] = type; - for(int i = 0; i < num_element_value_pairs; i++) - { - annot[2 + i * 2 + 0] = classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()); - annot[2 + i * 2 + 1] = ReadAnnotationElementValue(rdr, classFile, utf8_cp); - } - return annot; - } - - private static object ReadAnnotationElementValue(BigEndianBinaryReader rdr, ClassFile classFile, string[] utf8_cp) - { - try - { - byte tag = rdr.ReadByte(); - switch (tag) - { - case (byte)'Z': - return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()) != 0; - case (byte)'B': - return (byte)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()); - case (byte)'C': - return (char)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()); - case (byte)'S': - return (short)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()); - case (byte)'I': - return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()); - case (byte)'F': - return classFile.GetConstantPoolConstantFloat(rdr.ReadUInt16()); - case (byte)'J': - return classFile.GetConstantPoolConstantLong(rdr.ReadUInt16()); - case (byte)'D': - return classFile.GetConstantPoolConstantDouble(rdr.ReadUInt16()); - case (byte)'s': - return classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()); - case (byte)'e': - { - ushort type_name_index = rdr.ReadUInt16(); - ushort const_name_index = rdr.ReadUInt16(); - return new object[] { - AnnotationDefaultAttribute.TAG_ENUM, - classFile.GetConstantPoolUtf8String(utf8_cp, type_name_index), - classFile.GetConstantPoolUtf8String(utf8_cp, const_name_index) - }; - } - case (byte)'c': - return new object[] { - AnnotationDefaultAttribute.TAG_CLASS, - classFile.GetConstantPoolUtf8String(utf8_cp, rdr.ReadUInt16()) - }; - case (byte)'@': - return ReadAnnotation(rdr, classFile, utf8_cp); - case (byte)'[': - { - ushort num_values = rdr.ReadUInt16(); - object[] array = new object[num_values + 1]; - array[0] = AnnotationDefaultAttribute.TAG_ARRAY; - for (int i = 0; i < num_values; i++) - { - array[i + 1] = ReadAnnotationElementValue(rdr, classFile, utf8_cp); - } - return array; - } - default: - throw new ClassFormatError("Invalid tag {0} in annotation element_value", tag); - } - } - catch (NullReferenceException) - { - } - catch (InvalidCastException) - { - } - catch (IndexOutOfRangeException) - { - } - return new object[] { AnnotationDefaultAttribute.TAG_ERROR, "java.lang.IllegalArgumentException", "Wrong type at constant pool index" }; - } - - private void ValidateConstantPoolItemClass(string classFile, ushort index) - { - if(index >= constantpool.Length || !(constantpool[index] is ConstantPoolItemClass)) - { - throw new ClassFormatError("{0} (Bad constant pool index #{1})", classFile, index); - } - } - - private static bool IsValidMethodName(string name, int majorVersion) - { - if(name.Length == 0) - { - return false; - } - for(int i = 0; i < name.Length; i++) - { - if(".;[/<>".IndexOf(name[i]) != -1) - { - return false; - } - } - return majorVersion >= 49 || IsValidPre49Identifier(name); - } - - private static bool IsValidFieldName(string name, int majorVersion) - { - if(name.Length == 0) - { - return false; - } - for(int i = 0; i < name.Length; i++) - { - if(".;[/".IndexOf(name[i]) != -1) - { - return false; - } - } - return majorVersion >= 49 || IsValidPre49Identifier(name); - } - - private static bool IsValidPre49Identifier(string name) - { - if(!Char.IsLetter(name[0]) && "$_".IndexOf(name[0]) == -1) - { - return false; - } - for(int i = 1; i < name.Length; i++) - { - if(!Char.IsLetterOrDigit(name[i]) && "$_".IndexOf(name[i]) == -1) - { - return false; - } - } - return true; - } - - internal static bool IsValidFieldSig(string sig) - { - return IsValidFieldSigImpl(sig, 0, sig.Length); - } - - private static bool IsValidFieldSigImpl(string sig, int start, int end) - { - if(start >= end) - { - return false; - } - switch(sig[start]) - { - case 'L': - return sig.IndexOf(';', start + 1) == end - 1; - case '[': - while(sig[start] == '[') - { - start++; - if(start == end) - { - return false; - } - } - return IsValidFieldSigImpl(sig, start, end); - case 'B': - case 'Z': - case 'C': - case 'S': - case 'I': - case 'J': - case 'F': - case 'D': - return start == end - 1; - default: - return false; - } - } - - internal static bool IsValidMethodSig(string sig) - { - if(sig.Length < 3 || sig[0] != '(') - { - return false; - } - int end = sig.IndexOf(')'); - if(end == -1) - { - return false; - } - if(!sig.EndsWith(")V") && !IsValidFieldSigImpl(sig, end + 1, sig.Length)) - { - return false; - } - for(int i = 1; i < end; i++) - { - switch(sig[i]) - { - case 'B': - case 'Z': - case 'C': - case 'S': - case 'I': - case 'J': - case 'F': - case 'D': - break; - case 'L': - i = sig.IndexOf(';', i); - break; - case '[': - while(sig[i] == '[') - { - i++; - } - if("BZCSIJFDL".IndexOf(sig[i]) == -1) - { - return false; - } - if(sig[i] == 'L') - { - i = sig.IndexOf(';', i); - } - break; - default: - return false; - } - if(i == -1 || i >= end) - { - return false; - } - } - return true; - } - - internal int MajorVersion - { - get - { - return flags & FLAG_MASK_MAJORVERSION; - } - } - - internal void Link(TypeWrapper thisType, LoadMode mode) - { - // this is not just an optimization, it's required for anonymous classes to be able to refer to themselves - ((ConstantPoolItemClass)constantpool[this_class]).LinkSelf(thisType); - for(int i = 1; i < constantpool.Length; i++) - { - if(constantpool[i] != null) - { - constantpool[i].Link(thisType, mode); - } - } - } - - internal Modifiers Modifiers - { - get - { - return access_flags; - } - } - - internal bool IsAbstract - { - get - { - // interfaces are implicitly abstract - return (access_flags & (Modifiers.Abstract | Modifiers.Interface)) != 0; - } - } - - internal bool IsFinal - { - get - { - return (access_flags & Modifiers.Final) != 0; - } - } - - internal bool IsPublic - { - get - { - return (access_flags & Modifiers.Public) != 0; - } - } - - internal bool IsInterface - { - get - { - return (access_flags & Modifiers.Interface) != 0; - } - } - - internal bool IsEnum - { - get - { - return (access_flags & Modifiers.Enum) != 0; - } - } - - internal bool IsAnnotation - { - get - { - return (access_flags & Modifiers.Annotation) != 0; - } - } - - internal bool IsSuper - { - get - { - return (access_flags & Modifiers.Super) != 0; - } - } - - internal bool IsReferenced(Field fld) - { - foreach(ConstantPoolItem cpi in constantpool) - { - ConstantPoolItemFieldref fieldref = cpi as ConstantPoolItemFieldref; - if(fieldref != null && - fieldref.Class == this.Name && - fieldref.Name == fld.Name && - fieldref.Signature == fld.Signature) - { - return true; - } - } - return false; - } - - internal ConstantPoolItemFieldref GetFieldref(int index) - { - return (ConstantPoolItemFieldref)constantpool[index]; - } - - // this won't throw an exception if index is invalid - // (used by IsSideEffectFreeStaticInitializer) - internal ConstantPoolItemFieldref SafeGetFieldref(int index) - { - if(index > 0 && index < constantpool.Length) - { - return constantpool[index] as ConstantPoolItemFieldref; - } - return null; - } - - // NOTE this returns an MI, because it used for both normal methods and interface methods - internal ConstantPoolItemMI GetMethodref(int index) - { - return (ConstantPoolItemMI)constantpool[index]; - } - - // this won't throw an exception if index is invalid - // (used by IsAccessBridge) - internal ConstantPoolItemMI SafeGetMethodref(int index) - { - if (index > 0 && index < constantpool.Length) - { - return constantpool[index] as ConstantPoolItemMI; - } - return null; - } - - internal ConstantPoolItemInvokeDynamic GetInvokeDynamic(int index) - { - return (ConstantPoolItemInvokeDynamic)constantpool[index]; - } - - private ConstantPoolItem GetConstantPoolItem(int index) - { - return constantpool[index]; - } - - internal string GetConstantPoolClass(int index) - { - return ((ConstantPoolItemClass)constantpool[index]).Name; - } - - private bool SafeIsConstantPoolClass(int index) - { - if(index > 0 && index < constantpool.Length) - { - return constantpool[index] as ConstantPoolItemClass != null; - } - return false; - } - - internal TypeWrapper GetConstantPoolClassType(int index) - { - return ((ConstantPoolItemClass)constantpool[index]).GetClassType(); - } - - private string GetConstantPoolUtf8String(string[] utf8_cp, int index) - { - string s = utf8_cp[index]; - if(s == null) - { - if(this_class == 0) - { - throw new ClassFormatError("Bad constant pool index #{0}", index); - } - else - { - throw new ClassFormatError("{0} (Bad constant pool index #{1})", this.Name, index); - } - } - return s; - } - - internal ConstantType GetConstantPoolConstantType(int index) - { - return constantpool[index].GetConstantType(); - } - - internal double GetConstantPoolConstantDouble(int index) - { - return ((ConstantPoolItemDouble)constantpool[index]).Value; - } - - internal float GetConstantPoolConstantFloat(int index) - { - return ((ConstantPoolItemFloat)constantpool[index]).Value; - } - - internal int GetConstantPoolConstantInteger(int index) - { - return ((ConstantPoolItemInteger)constantpool[index]).Value; - } - - internal long GetConstantPoolConstantLong(int index) - { - return ((ConstantPoolItemLong)constantpool[index]).Value; - } - - internal string GetConstantPoolConstantString(int index) - { - return ((ConstantPoolItemString)constantpool[index]).Value; - } - - internal ConstantPoolItemMethodHandle GetConstantPoolConstantMethodHandle(int index) - { - return (ConstantPoolItemMethodHandle)constantpool[index]; - } - - internal ConstantPoolItemMethodType GetConstantPoolConstantMethodType(int index) - { - return (ConstantPoolItemMethodType)constantpool[index]; - } - - internal object GetConstantPoolConstantLiveObject(int index) - { - return ((ConstantPoolItemLiveObject)constantpool[index]).Value; - } - - internal string Name - { - get - { - return GetConstantPoolClass(this_class); - } - } - - internal ConstantPoolItemClass SuperClass - { - get - { - return (ConstantPoolItemClass)constantpool[super_class]; - } - } - - internal Field[] Fields - { - get - { - return fields; - } - } - - internal Method[] Methods - { - get - { - return methods; - } - } - - internal ConstantPoolItemClass[] Interfaces - { - get - { - return interfaces; - } - } - - internal string SourceFileAttribute - { - get - { - return sourceFile; - } - } - - internal string SourcePath - { -#if STATIC_COMPILER - get { return sourcePath; } - set { sourcePath = value; } + } + + void MarkLinkRequiredConstantPoolItem(int index) + { + if (index > 0 && index < constantpool.Length && constantpool[index] != null) + { + constantpool[index].MarkLinkRequired(); + } + } + + static BootstrapMethod[] ReadBootstrapMethods(IReadOnlyList methods, ClassFile classFile) + { + var bsm = new BootstrapMethod[methods.Count]; + for (int i = 0; i < methods.Count; i++) + { + var method = methods[i]; + + var bsm_index = method.Record.MethodRefIndex; + if (bsm_index >= classFile.constantpool.Length || classFile.constantpool[bsm_index] is not ConstantPoolItemMethodHandle) + throw new ClassFormatError("bootstrap_method_index {0} has bad constant type in class file {1}", bsm_index, classFile.Name); + + classFile.MarkLinkRequiredConstantPoolItem(bsm_index); + + var argument_count = method.Arguments.Count; + var args = new ushort[argument_count]; + for (int j = 0; j < args.Length; j++) + { + var argument_index = method.Record.Arguments[j]; + if (classFile.IsValidConstant(argument_index) == false) + throw new ClassFormatError("argument_index {0} has bad constant type in class file {1}", argument_index, classFile.Name); + + classFile.MarkLinkRequiredConstantPoolItem(argument_index); + args[j] = argument_index; + } + + bsm[i] = new BootstrapMethod(bsm_index, args); + } + + return bsm; + } + + bool IsValidConstant(ushort index) + { + if (index < constantpool.Length && constantpool[index] != null) + { + try + { + constantpool[index].GetConstantType(); + return true; + } + catch (InvalidOperationException) + { + + } + } + + return false; + } + + static object[] ReadAnnotations(AnnotationReaderCollection reader, ClassFile classFile, string[] utf8_cp) + { + var annotations = new object[reader.Count]; + + for (int i = 0; i < annotations.Length; i++) + annotations[i] = ReadAnnotation(reader[i], classFile, utf8_cp); + + return annotations; + } + + static object ReadAnnotation(AnnotationReader reader, ClassFile classFile, string[] utf8_cp) + { + var l = new object[2 + reader.Elements.Count * 2]; + l[0] = AnnotationDefaultAttribute.TAG_ANNOTATION; + l[1] = classFile.GetConstantPoolUtf8String(utf8_cp, reader.Record.TypeIndex); + for (int i = 0; i < reader.Elements.Count; i++) + { + l[2 + i * 2 + 0] = classFile.GetConstantPoolUtf8String(utf8_cp, reader.Record.Elements[i].NameIndex); + l[2 + i * 2 + 1] = ReadAnnotationElementValue(reader.Elements[i], classFile, utf8_cp); + } + + return l; + } + + static object ReadAnnotationElementValue(ElementValueReader reader, ClassFile classFile, string[] utf8_cp) + { + try + { + switch (reader) + { + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Boolean: + return classFile.GetConstantPoolConstantInteger(r.Value.Index) != 0; + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Byte: + return (byte)classFile.GetConstantPoolConstantInteger(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Char: + return (char)classFile.GetConstantPoolConstantInteger(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Short: + return (short)classFile.GetConstantPoolConstantInteger(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Integer: + return classFile.GetConstantPoolConstantInteger(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Float: + return classFile.GetConstantPoolConstantFloat(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Long: + return classFile.GetConstantPoolConstantLong(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.Double: + return classFile.GetConstantPoolConstantDouble(r.Value.Index); + case ElementValueConstantReader r when r.Tag == ByteCode.Parsing.ElementValueTag.String: + return classFile.GetConstantPoolUtf8String(utf8_cp, r.Value.Index); + case ElementValueEnumConstantReader r: + return new object[] { + AnnotationDefaultAttribute.TAG_ENUM, + classFile.GetConstantPoolUtf8String(utf8_cp, r.TypeName.Index), + classFile.GetConstantPoolUtf8String(utf8_cp, r.ConstantName.Index) + }; + case ElementValueClassReader r: + return new object[] { + AnnotationDefaultAttribute.TAG_CLASS, + classFile.GetConstantPoolUtf8String(utf8_cp, r.Class.Index) + }; + case ElementValueAnnotationReader r: + return ReadAnnotation(r.Annotation, classFile, utf8_cp); + case ElementValueArrayReader r: + var array = new object[r.Values.Count + 1]; + array[0] = AnnotationDefaultAttribute.TAG_ARRAY; + for (int i = 0; i < r.Values.Count; i++) + array[i + 1] = ReadAnnotationElementValue(r.Values[i], classFile, utf8_cp); + return array; + default: + throw new ClassFormatError("Invalid tag {0} in annotation element_value", reader.Record.Tag); + } + } + catch (NullReferenceException) + { + + } + catch (InvalidCastException) + { + + } + catch (IndexOutOfRangeException) + { + + } + catch (ByteCodeException) + { + + } + + return new object[] { AnnotationDefaultAttribute.TAG_ERROR, "java.lang.IllegalArgumentException", "Wrong type at constant pool index" }; + } + + private void ValidateConstantPoolItemClass(string classFile, ushort index) + { + if (index >= constantpool.Length || constantpool[index] is not ConstantPoolItemClass) + throw new ClassFormatError("{0} (Bad constant pool index #{1})", classFile, index); + } + + private static bool IsValidMethodName(string name, ClassFormatVersion version) + { + if (name.Length == 0) + { + return false; + } + for (int i = 0; i < name.Length; i++) + { + if (".;[/<>".IndexOf(name[i]) != -1) + { + return false; + } + } + return version >= 49 || IsValidPre49Identifier(name); + } + + private static bool IsValidFieldName(string name, ClassFormatVersion version) + { + if (name.Length == 0) + { + return false; + } + for (int i = 0; i < name.Length; i++) + { + if (".;[/".IndexOf(name[i]) != -1) + { + return false; + } + } + return version >= 49 || IsValidPre49Identifier(name); + } + + private static bool IsValidPre49Identifier(string name) + { + if (!Char.IsLetter(name[0]) && "$_".IndexOf(name[0]) == -1) + { + return false; + } + for (int i = 1; i < name.Length; i++) + { + if (!Char.IsLetterOrDigit(name[i]) && "$_".IndexOf(name[i]) == -1) + { + return false; + } + } + return true; + } + + internal static bool IsValidFieldSig(string sig) + { + return IsValidFieldSigImpl(sig, 0, sig.Length); + } + + private static bool IsValidFieldSigImpl(string sig, int start, int end) + { + if (start >= end) + { + return false; + } + switch (sig[start]) + { + case 'L': + return sig.IndexOf(';', start + 1) == end - 1; + case '[': + while (sig[start] == '[') + { + start++; + if (start == end) + { + return false; + } + } + return IsValidFieldSigImpl(sig, start, end); + case 'B': + case 'Z': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + return start == end - 1; + default: + return false; + } + } + + internal static bool IsValidMethodSig(string sig) + { + if (sig.Length < 3 || sig[0] != '(') + { + return false; + } + int end = sig.IndexOf(')'); + if (end == -1) + { + return false; + } + if (!sig.EndsWith(")V") && !IsValidFieldSigImpl(sig, end + 1, sig.Length)) + { + return false; + } + for (int i = 1; i < end; i++) + { + switch (sig[i]) + { + case 'B': + case 'Z': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + break; + case 'L': + i = sig.IndexOf(';', i); + break; + case '[': + while (sig[i] == '[') + { + i++; + } + if ("BZCSIJFDL".IndexOf(sig[i]) == -1) + { + return false; + } + if (sig[i] == 'L') + { + i = sig.IndexOf(';', i); + } + break; + default: + return false; + } + if (i == -1 || i >= end) + { + return false; + } + } + return true; + } + + internal int MajorVersion => reader.Version.Major; + + internal void Link(TypeWrapper thisType, LoadMode mode) + { + // this is not just an optimization, it's required for anonymous classes to be able to refer to themselves + ((ConstantPoolItemClass)constantpool[reader.Record.ThisClassIndex]).LinkSelf(thisType); + + for (int i = 1; i < constantpool.Length; i++) + if (constantpool[i] != null) + constantpool[i].Link(thisType, mode); + } + + internal Modifiers Modifiers => access_flags; + + internal bool IsAbstract + { + get + { + // interfaces are implicitly abstract + return (access_flags & (Modifiers.Abstract | Modifiers.Interface)) != 0; + } + } + + internal bool IsFinal => (access_flags & Modifiers.Final) != 0; + + internal bool IsPublic => (access_flags & Modifiers.Public) != 0; + + internal bool IsInterface => (access_flags & Modifiers.Interface) != 0; + + internal bool IsEnum => (access_flags & Modifiers.Enum) != 0; + + internal bool IsAnnotation => (access_flags & Modifiers.Annotation) != 0; + + internal bool IsSuper => (access_flags & Modifiers.Super) != 0; + + internal bool IsReferenced(Field fld) => constantpool.OfType().Any(i => i.Class == Name && i.Name == fld.Name && i.Signature == fld.Signature); + + internal ConstantPoolItemFieldref GetFieldref(int index) => (ConstantPoolItemFieldref)constantpool[index]; + + // this won't throw an exception if index is invalid + // (used by IsSideEffectFreeStaticInitializer) + internal ConstantPoolItemFieldref SafeGetFieldref(int index) + { + if (index > 0 && index < constantpool.Length) + return constantpool[index] as ConstantPoolItemFieldref; + + return null; + } + + // NOTE this returns an MI, because it used for both normal methods and interface methods + internal ConstantPoolItemMI GetMethodref(int index) => (ConstantPoolItemMI)constantpool[index]; + + // this won't throw an exception if index is invalid + // (used by IsAccessBridge) + internal ConstantPoolItemMI SafeGetMethodref(int index) + { + if (index > 0 && index < constantpool.Length) + return constantpool[index] as ConstantPoolItemMI; + + return null; + } + + internal ConstantPoolItemInvokeDynamic GetInvokeDynamic(int index) + { + return (ConstantPoolItemInvokeDynamic)constantpool[index]; + } + + private ConstantPoolItem GetConstantPoolItem(int index) + { + return constantpool[index]; + } + + internal string GetConstantPoolClass(int index) + { + return ((ConstantPoolItemClass)constantpool[index]).Name; + } + + private bool SafeIsConstantPoolClass(int index) + { + if (index > 0 && index < constantpool.Length) + return constantpool[index] as ConstantPoolItemClass != null; + + return false; + } + + internal TypeWrapper GetConstantPoolClassType(int index) + { + return ((ConstantPoolItemClass)constantpool[index]).GetClassType(); + } + + private string GetConstantPoolUtf8String(string[] utf8_cp, int index) + { + var s = utf8_cp[index]; + if (s == null) + { + if (reader.This.Index == 0) + { + throw new ClassFormatError("Bad constant pool index #{0}", index); + } + else + { + throw new ClassFormatError("{0} (Bad constant pool index #{1})", this.Name, index); + } + } + + return s; + } + + internal ConstantType GetConstantPoolConstantType(int index) + { + return constantpool[index].GetConstantType(); + } + + internal double GetConstantPoolConstantDouble(int index) + { + return ((ConstantPoolItemDouble)constantpool[index]).Value; + } + + internal float GetConstantPoolConstantFloat(int index) + { + return ((ConstantPoolItemFloat)constantpool[index]).Value; + } + + internal int GetConstantPoolConstantInteger(int index) + { + return ((ConstantPoolItemInteger)constantpool[index]).Value; + } + + internal long GetConstantPoolConstantLong(int index) + { + return ((ConstantPoolItemLong)constantpool[index]).Value; + } + + internal string GetConstantPoolConstantString(int index) + { + return ((ConstantPoolItemString)constantpool[index]).Value; + } + + internal ConstantPoolItemMethodHandle GetConstantPoolConstantMethodHandle(int index) + { + return (ConstantPoolItemMethodHandle)constantpool[index]; + } + + internal ConstantPoolItemMethodType GetConstantPoolConstantMethodType(int index) + { + return (ConstantPoolItemMethodType)constantpool[index]; + } + + internal object GetConstantPoolConstantLiveObject(int index) + { + return ((ConstantPoolItemLiveObject)constantpool[index]).Value; + } + + internal string Name => GetConstantPoolClass(reader.Record.ThisClassIndex); + + internal ConstantPoolItemClass SuperClass => (ConstantPoolItemClass)constantpool[reader.Record.SuperClassIndex]; + + internal Field[] Fields => fields; + + internal Method[] Methods => methods; + + internal ConstantPoolItemClass[] Interfaces => interfaces; + + internal string SourceFileAttribute => sourceFile; + + internal string SourcePath + { +#if IMPORTER + get { return sourcePath; } + set { sourcePath = value; } #else - get { return sourceFile; } + get { return sourceFile; } #endif - } - - internal object[] Annotations - { - get - { - return annotations; - } - } - - internal string GenericSignature - { - get - { - return signature; - } - } - - internal string[] EnclosingMethod - { - get - { - return enclosingMethod; - } - } - - internal byte[] RuntimeVisibleTypeAnnotations - { - get - { - return runtimeVisibleTypeAnnotations; - } - } - - internal object[] GetConstantPool() - { - object[] cp = new object[constantpool.Length]; - for (int i = 1; i < cp.Length; i++) - { - if (constantpool[i] != null) - { - cp[i] = constantpool[i].GetRuntimeValue(); - } - } - return cp; - } - - internal string IKVMAssemblyAttribute - { - get - { - return ikvmAssembly; - } - } - - internal bool DeprecatedAttribute - { - get - { - return (flags & FLAG_MASK_DEPRECATED) != 0; - } - } - - internal bool IsInternal - { - get - { - return (flags & FLAG_MASK_INTERNAL) != 0; - } - } - - // for use by ikvmc (to implement the -privatepackage option) - internal void SetInternal() - { - access_flags &= ~Modifiers.AccessMask; - flags |= FLAG_MASK_INTERNAL; - } - - internal bool HasAssertions - { - get - { - return (flags & FLAG_HAS_ASSERTIONS) != 0; - } - } - - internal bool HasInitializedFields - { - get - { - foreach (Field f in fields) - { - if (f.IsStatic && !f.IsFinal && f.ConstantValue != null) - { - return true; - } - } - return false; - } - } - - internal BootstrapMethod GetBootstrapMethod(int index) - { - return bootstrapMethods[index]; - } - - internal InnerClass[] InnerClasses - { - get - { - return innerClasses; - } - } - - internal Field GetField(string name, string sig) - { - for (int i = 0; i < fields.Length; i++) - { - if (fields[i].Name == name && fields[i].Signature == sig) - { - return fields[i]; - } - } - return null; - } - - private void RemoveAssertionInit(Method m) - { - /* We match the following code sequence: + } + + internal object[] Annotations + { + get + { + return annotations; + } + } + + internal string GenericSignature => signature; + + internal string[] EnclosingMethod => enclosingMethod; + + internal IReadOnlyList RuntimeVisibleTypeAnnotations => runtimeVisibleTypeAnnotations; + + internal object[] GetConstantPool() + { + var cp = new object[constantpool.Length]; + for (int i = 1; i < cp.Length; i++) + if (constantpool[i] != null) + cp[i] = constantpool[i].GetRuntimeValue(); + + return cp; + } + + internal string IKVMAssemblyAttribute => ikvmAssembly; + + internal bool DeprecatedAttribute => (flags & FLAG_MASK_DEPRECATED) != 0; + + internal bool IsInternal => (flags & FLAG_MASK_INTERNAL) != 0; + + // for use by ikvmc (to implement the -privatepackage option) + internal void SetInternal() + { + access_flags &= ~Modifiers.AccessMask; + flags |= FLAG_MASK_INTERNAL; + } + + internal bool HasAssertions => (flags & FLAG_HAS_ASSERTIONS) != 0; + + internal bool HasInitializedFields + { + get + { + foreach (Field f in fields) + if (f.IsStatic && !f.IsFinal && f.ConstantValue != null) + return true; + + return false; + } + } + + internal BootstrapMethod GetBootstrapMethod(int index) + { + return bootstrapMethods[index]; + } + + internal InnerClass[] InnerClasses => innerClasses; + + internal Field GetField(string name, string sig) + { + for (int i = 0; i < fields.Length; i++) + if (fields[i].Name == name && fields[i].Signature == sig) + return fields[i]; + + return null; + } + + private void RemoveAssertionInit(Method m) + { + /* We match the following code sequence: * 0 ldc * 2 invokevirtual * 5 ifne 12 @@ -1346,112 +1102,112 @@ private void RemoveAssertionInit(Method m) * 12 iconst_0 * 13 putstatic boolean > */ - ConstantPoolItemFieldref fieldref; - Field field; - if (m.Instructions[0].NormalizedOpCode == NormalizedByteCode.__ldc && SafeIsConstantPoolClass(m.Instructions[0].Arg1) - && m.Instructions[1].NormalizedOpCode == NormalizedByteCode.__invokevirtual && IsDesiredAssertionStatusMethodref(m.Instructions[1].Arg1) - && m.Instructions[2].NormalizedOpCode == NormalizedByteCode.__ifne && m.Instructions[2].TargetIndex == 5 - && m.Instructions[3].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[3].Arg1 == 1 - && m.Instructions[4].NormalizedOpCode == NormalizedByteCode.__goto && m.Instructions[4].TargetIndex == 6 - && m.Instructions[5].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[5].Arg1 == 0 - && m.Instructions[6].NormalizedOpCode == NormalizedByteCode.__putstatic && (fieldref = SafeGetFieldref(m.Instructions[6].Arg1)) != null - && fieldref.Class == Name && fieldref.Signature == "Z" - && (field = GetField(fieldref.Name, fieldref.Signature)) != null - && field.IsStatic && field.IsFinal - && !HasBranchIntoRegion(m.Instructions, 7, m.Instructions.Length, 0, 7) - && !HasStaticFieldWrite(m.Instructions, 7, m.Instructions.Length, field) - && !HasExceptionHandlerInRegion(m.ExceptionTable, 0, 7)) - { - field.PatchConstantValue(true); - m.Instructions[0].PatchOpCode(NormalizedByteCode.__goto, 7); - flags |= FLAG_HAS_ASSERTIONS; - } - } - - private bool IsDesiredAssertionStatusMethodref(int cpi) - { - ConstantPoolItemMethodref method = SafeGetMethodref(cpi) as ConstantPoolItemMethodref; - return method != null - && method.Class == "java.lang.Class" - && method.Name == "desiredAssertionStatus" - && method.Signature == "()Z"; - } - - private static bool HasBranchIntoRegion(Method.Instruction[] instructions, int checkStart, int checkEnd, int regionStart, int regionEnd) - { - for (int i = checkStart; i < checkEnd; i++) - { - switch (instructions[i].NormalizedOpCode) - { - case NormalizedByteCode.__ifeq: - case NormalizedByteCode.__ifne: - case NormalizedByteCode.__iflt: - case NormalizedByteCode.__ifge: - case NormalizedByteCode.__ifgt: - case NormalizedByteCode.__ifle: - case NormalizedByteCode.__if_icmpeq: - case NormalizedByteCode.__if_icmpne: - case NormalizedByteCode.__if_icmplt: - case NormalizedByteCode.__if_icmpge: - case NormalizedByteCode.__if_icmpgt: - case NormalizedByteCode.__if_icmple: - case NormalizedByteCode.__if_acmpeq: - case NormalizedByteCode.__if_acmpne: - case NormalizedByteCode.__ifnull: - case NormalizedByteCode.__ifnonnull: - case NormalizedByteCode.__goto: - case NormalizedByteCode.__jsr: - if (instructions[i].TargetIndex > regionStart && instructions[i].TargetIndex < regionEnd) - { - return true; - } - break; - case NormalizedByteCode.__tableswitch: - case NormalizedByteCode.__lookupswitch: - if (instructions[i].DefaultTarget > regionStart && instructions[i].DefaultTarget < regionEnd) - { - return true; - } - for (int j = 0; j < instructions[i].SwitchEntryCount; j++) - { - if (instructions[i].GetSwitchTargetIndex(j) > regionStart && instructions[i].GetSwitchTargetIndex(j) < regionEnd) - { - return true; - } - } - break; - } - } - return false; - } - - private bool HasStaticFieldWrite(Method.Instruction[] instructions, int checkStart, int checkEnd, Field field) - { - for (int i = checkStart; i < checkEnd; i++) - { - if (instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic) - { - ConstantPoolItemFieldref fieldref = SafeGetFieldref(instructions[i].Arg1); - if (fieldref != null && fieldref.Class == Name && fieldref.Name == field.Name && fieldref.Signature == field.Signature) - { - return true; - } - } - } - return false; - } - - private static bool HasExceptionHandlerInRegion(Method.ExceptionTableEntry[] entries, int regionStart, int regionEnd) - { - for (int i = 0; i < entries.Length; i++) - { - if (entries[i].handlerIndex > regionStart && entries[i].handlerIndex < regionEnd) - { - return true; - } - } - return false; - } - } + ConstantPoolItemFieldref fieldref; + Field field; + if (m.Instructions[0].NormalizedOpCode == NormalizedByteCode.__ldc && SafeIsConstantPoolClass(m.Instructions[0].Arg1) + && m.Instructions[1].NormalizedOpCode == NormalizedByteCode.__invokevirtual && IsDesiredAssertionStatusMethodref(m.Instructions[1].Arg1) + && m.Instructions[2].NormalizedOpCode == NormalizedByteCode.__ifne && m.Instructions[2].TargetIndex == 5 + && m.Instructions[3].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[3].Arg1 == 1 + && m.Instructions[4].NormalizedOpCode == NormalizedByteCode.__goto && m.Instructions[4].TargetIndex == 6 + && m.Instructions[5].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[5].Arg1 == 0 + && m.Instructions[6].NormalizedOpCode == NormalizedByteCode.__putstatic && (fieldref = SafeGetFieldref(m.Instructions[6].Arg1)) != null + && fieldref.Class == Name && fieldref.Signature == "Z" + && (field = GetField(fieldref.Name, fieldref.Signature)) != null + && field.IsStatic && field.IsFinal + && !HasBranchIntoRegion(m.Instructions, 7, m.Instructions.Length, 0, 7) + && !HasStaticFieldWrite(m.Instructions, 7, m.Instructions.Length, field) + && !HasExceptionHandlerInRegion(m.ExceptionTable, 0, 7)) + { + field.PatchConstantValue(true); + m.Instructions[0].PatchOpCode(NormalizedByteCode.__goto, 7); + flags |= FLAG_HAS_ASSERTIONS; + } + } + + private bool IsDesiredAssertionStatusMethodref(int cpi) + { + ConstantPoolItemMethodref method = SafeGetMethodref(cpi) as ConstantPoolItemMethodref; + return method != null + && method.Class == "java.lang.Class" + && method.Name == "desiredAssertionStatus" + && method.Signature == "()Z"; + } + + private static bool HasBranchIntoRegion(Method.Instruction[] instructions, int checkStart, int checkEnd, int regionStart, int regionEnd) + { + for (int i = checkStart; i < checkEnd; i++) + { + switch (instructions[i].NormalizedOpCode) + { + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + case NormalizedByteCode.__ifnull: + case NormalizedByteCode.__ifnonnull: + case NormalizedByteCode.__goto: + case NormalizedByteCode.__jsr: + if (instructions[i].TargetIndex > regionStart && instructions[i].TargetIndex < regionEnd) + { + return true; + } + break; + case NormalizedByteCode.__tableswitch: + case NormalizedByteCode.__lookupswitch: + if (instructions[i].DefaultTarget > regionStart && instructions[i].DefaultTarget < regionEnd) + { + return true; + } + for (int j = 0; j < instructions[i].SwitchEntryCount; j++) + { + if (instructions[i].GetSwitchTargetIndex(j) > regionStart && instructions[i].GetSwitchTargetIndex(j) < regionEnd) + { + return true; + } + } + break; + } + } + return false; + } + + private bool HasStaticFieldWrite(Method.Instruction[] instructions, int checkStart, int checkEnd, Field field) + { + for (int i = checkStart; i < checkEnd; i++) + { + if (instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic) + { + ConstantPoolItemFieldref fieldref = SafeGetFieldref(instructions[i].Arg1); + if (fieldref != null && fieldref.Class == Name && fieldref.Name == field.Name && fieldref.Signature == field.Signature) + { + return true; + } + } + } + return false; + } + + private static bool HasExceptionHandlerInRegion(Method.ExceptionTableEntry[] entries, int regionStart, int regionEnd) + { + for (int i = 0; i < entries.Length; i++) + { + if (entries[i].handlerIndex > regionStart && entries[i].handlerIndex < regionEnd) + { + return true; + } + } + return false; + } + } } diff --git a/src/IKVM.Runtime/ClassFile/ClassFileParseOptions.cs b/src/IKVM.Runtime/ClassFile/ClassFileParseOptions.cs index 0d1e75d49d..f4491e3d7f 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFileParseOptions.cs +++ b/src/IKVM.Runtime/ClassFile/ClassFileParseOptions.cs @@ -27,16 +27,16 @@ namespace IKVM.Internal { [Flags] - enum ClassFileParseOptions - { + enum ClassFileParseOptions + { - None = 0, - LocalVariableTable = 1, - LineNumberTable = 2, - RelaxedClassNameValidation = 4, - TrustedAnnotations = 8, - RemoveAssertions = 16, + None = 0, + LocalVariableTable = 1, + LineNumberTable = 2, + RelaxedClassNameValidation = 4, + TrustedAnnotations = 8, + RemoveAssertions = 16, - } + } } diff --git a/src/IKVM.Runtime/ClassLoaderWrapper.cs b/src/IKVM.Runtime/ClassLoaderWrapper.cs index adb54acd4d..a880d38be8 100644 --- a/src/IKVM.Runtime/ClassLoaderWrapper.cs +++ b/src/IKVM.Runtime/ClassLoaderWrapper.cs @@ -28,13 +28,16 @@ Jeroen Frijters using System.Threading; using System.Runtime.CompilerServices; +using IKVM.Runtime; + #if NETCOREAPP using System.Runtime.Loader; #endif -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; + using Type = IKVM.Reflection.Type; using ProtectionDomain = System.Object; #else @@ -42,6 +45,12 @@ Jeroen Frijters using System.Reflection.Emit; using ProtectionDomain = java.security.ProtectionDomain; + +using IKVM.Runtime.Accessors.Java.Lang; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; #endif namespace IKVM.Internal @@ -52,20 +61,25 @@ class ClassLoaderWrapper private static readonly object wrapperLock = new object(); private static readonly Dictionary globalTypeToTypeWrapper = new Dictionary(); -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER private static ClassLoaderWrapper bootstrapClassLoader; #else private static AssemblyClassLoader bootstrapClassLoader; #endif private static List genericClassLoaders; -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER + + ClassLoaderAccessor classLoaderAccessor; + ClassLoaderAccessor ClassLoaderAccessor => JVM.BaseAccessors.Get(ref classLoaderAccessor); + protected java.lang.ClassLoader javaClassLoader; + #endif -#if !STUB_GENERATOR +#if !EXPORTER private TypeWrapperFactory factory; -#endif // !STUB_GENERATOR +#endif // !EXPORTER private readonly Dictionary types = new Dictionary(); private readonly Dictionary defineClassInProgress = new Dictionary(); private List nativeLibraries; @@ -76,7 +90,7 @@ class ClassLoaderWrapper #endif private static readonly Dictionary remappedTypes = new Dictionary(); -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER // HACK this is used by the ahead-of-time compiler to overrule the bootstrap classloader // when we're compiling the core class libraries and by ikvmstub with the -bootstrap option internal static void SetBootstrapClassLoader(ClassLoaderWrapper bootstrapClassLoader) @@ -104,7 +118,7 @@ static ClassLoaderWrapper() internal static void LoadRemappedTypes() { // if we're compiling the core, coreAssembly will be null - var coreAssembly = JVM.CoreAssembly; + var coreAssembly = JVM.BaseAssembly; if (coreAssembly != null && remappedTypes.Count == 0) { var remapped = AttributeHelper.GetRemappedClasses(coreAssembly); @@ -115,29 +129,29 @@ internal static void LoadRemappedTypes() } else { -#if STATIC_COMPILER +#if IMPORTER throw new FatalCompilerErrorException(Message.CoreClassesMissing); #else - JVM.CriticalFailure("Failed to find core classes in core library", null); + throw new InternalException("Failed to find core classes in core library."); #endif } } } + internal static bool IsRemappedType(Type type) + { + return remappedTypes.ContainsKey(type); + } + internal ClassLoaderWrapper(CodeGenOptions codegenoptions, object javaClassLoader) { this.codegenoptions = codegenoptions; -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER this.javaClassLoader = (java.lang.ClassLoader)javaClassLoader; #endif } - internal static bool IsRemappedType(Type type) - { - return remappedTypes.ContainsKey(type); - } - -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER internal void SetRemappedType(Type type, TypeWrapper tw) { @@ -323,7 +337,32 @@ internal bool WorkaroundInterfaceStaticMethods } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER +#if FIRST_PASS == false + + /// + /// Returns true if the class loader is considered trusted. + /// + /// + /// Implementation of Hotspot's java_lang_ClassLoader::is_trusted_loader(). + /// + internal bool IsTrusted + { + get + { + var scl = ClassLoaderAccessor.GetScl(); + + // are we within the parent hierarchy of the system class loader? + for (var cl = scl; cl != null; cl = ClassLoaderAccessor.GetParent(cl)) + if (javaClassLoader == cl) + return true; + + return false; + } + } + +#endif + internal bool RelaxedClassNameValidation { get @@ -331,11 +370,12 @@ internal bool RelaxedClassNameValidation #if FIRST_PASS return true; #else - return JVM.relaxedVerification && (javaClassLoader == null || java.lang.ClassLoader.isTrustedLoader(javaClassLoader)); + return JVM.relaxedVerification && (javaClassLoader == null || IsTrusted); #endif } } -#endif + +#endif protected virtual void CheckProhibitedPackage(string className) { @@ -343,10 +383,10 @@ protected virtual void CheckProhibitedPackage(string className) throw new JavaSecurityException("Prohibited package name: " + className.Substring(0, className.LastIndexOf('.'))); } -#if !STUB_GENERATOR +#if !EXPORTER internal TypeWrapper DefineClass(ClassFile f, ProtectionDomain protectionDomain) { -#if !STATIC_COMPILER +#if !IMPORTER var dotnetAssembly = f.IKVMAssemblyAttribute; if (dotnetAssembly != null) { @@ -453,7 +493,7 @@ internal TypeWrapperFactory GetTypeWrapperFactory() return factory; } -#endif // !STUB_GENERATOR +#endif // !EXPORTER internal TypeWrapper LoadClassByDottedName(string name) { @@ -483,7 +523,7 @@ internal TypeWrapper LoadClass(string name, LoadMode mode) if (tw != null) return RegisterInitiatingLoader(tw); -#if STATIC_COMPILER +#if IMPORTER if (!(name.Length > 1 && name[0] == '[') && ((mode & LoadMode.WarnClassNotFound) != 0) || WarningLevelHigh) IssueMessage(Message.ClassNotFound, name); @@ -606,18 +646,13 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) // (note that other types with manufactured inner classes such as Attribute and Enum can't be generic) if (name.EndsWith(DotNetTypeWrapper.DelegateInterfaceSuffix)) { - TypeWrapper outer = FindOrLoadGenericClass(name.Substring(0, name.Length - DotNetTypeWrapper.DelegateInterfaceSuffix.Length), mode); + var outer = FindOrLoadGenericClass(name.Substring(0, name.Length - DotNetTypeWrapper.DelegateInterfaceSuffix.Length), mode); if (outer != null && outer.IsFakeTypeContainer) - { - foreach (TypeWrapper tw in outer.InnerClasses) - { + foreach (var tw in outer.InnerClasses) if (tw.Name == name) - { return tw; - } - } - } } + // generic class name grammar: // // mangled(open_generic_type_name) "_$$$_" M(parameter_class_name) ( "_$$_" M(parameter_class_name) )* "_$$$$_" @@ -625,18 +660,16 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) // mangled() is the normal name mangling algorithm // M() is a replacement of "__" with "$$005F$$005F" followed by a replace of "." with "__" // - int pos = name.IndexOf("_$$$_"); + var pos = name.IndexOf("_$$$_"); if (pos <= 0 || !name.EndsWith("_$$$$_")) - { return null; - } - TypeWrapper def = LoadClass(name.Substring(0, pos), mode); + + var def = LoadClass(name.Substring(0, pos), mode); if (def == null || !def.TypeAsTBD.IsGenericTypeDefinition) - { return null; - } - Type type = def.TypeAsTBD; - List typeParamNames = new List(); + + var type = def.TypeAsTBD; + var typeParamNames = new List(); pos += 5; int start = pos; int nest = 0; @@ -644,9 +677,8 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) { pos = name.IndexOf("_$$", pos); if (pos == -1) - { return null; - } + if (name.IndexOf("_$$_", pos, 4) == pos) { if (nest == 0) @@ -654,6 +686,7 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) typeParamNames.Add(name.Substring(start, pos - start)); start = pos + 4; } + pos += 4; } else if (name.IndexOf("_$$$_", pos, 5) == pos) @@ -680,10 +713,11 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) pos += 3; } } - Type[] typeArguments = new Type[typeParamNames.Count]; + + var typeArguments = new Type[typeParamNames.Count]; for (int i = 0; i < typeArguments.Length; i++) { - string s = (string)typeParamNames[i]; + var s = typeParamNames[i]; // only do the unmangling for non-generic types (because we don't want to convert // the double underscores in two adjacent _$$$_ or _$$$$_ markers) if (s.IndexOf("_$$$_") == -1) @@ -691,15 +725,14 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) s = s.Replace("__", "."); s = s.Replace("$$005F$$005F", "__"); } + int dims = 0; while (s.Length > dims && s[dims] == 'A') - { dims++; - } + if (s.Length == dims) - { return null; - } + TypeWrapper tw; switch (s[dims]) { @@ -738,12 +771,13 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) default: return null; } + if (dims > 0) - { tw = tw.MakeArrayType(dims); - } + typeArguments[i] = tw.TypeAsSignatureType; } + try { type = type.MakeGenericType(typeArguments); @@ -753,49 +787,46 @@ internal TypeWrapper FindOrLoadGenericClass(string name, LoadMode mode) // one of the typeArguments failed to meet the constraints return null; } - TypeWrapper wrapper = GetWrapperFromType(type); + + var wrapper = GetWrapperFromType(type); if (wrapper != null && wrapper.Name != name) { // the name specified was not in canonical form return null; } + return wrapper; } protected virtual TypeWrapper LoadClassImpl(string name, LoadMode mode) { - TypeWrapper tw = FindOrLoadGenericClass(name, mode); + var tw = FindOrLoadGenericClass(name, mode); if (tw != null) - { return tw; - } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR + +#if !FIRST_PASS && !IMPORTER && !EXPORTER + if ((mode & LoadMode.Load) == 0) - { return null; - } + Profiler.Enter("ClassLoader.loadClass"); try { - java.lang.Class c = GetJavaClassLoader().loadClassInternal(name); + var c = (java.lang.Class)ClassLoaderAccessor.InvokeLoadClassInternal(GetJavaClassLoader(), name); if (c == null) - { return null; - } - TypeWrapper type = TypeWrapper.FromClass(c); + + var type = TypeWrapper.FromClass(c); if (type.Name != name) - { - // the class loader is trying to trick us return null; - } + return type; } catch (java.lang.ClassNotFoundException x) { if ((mode & LoadMode.MaskReturn) == LoadMode.ThrowClassNotFound) - { throw new ClassLoadingException(ikvm.runtime.Util.mapException(x), name); - } + return null; } catch (java.lang.ThreadDeath) @@ -805,27 +836,29 @@ protected virtual TypeWrapper LoadClassImpl(string name, LoadMode mode) catch (Exception x) { if ((mode & LoadMode.SuppressExceptions) == 0) - { throw new ClassLoadingException(ikvm.runtime.Util.mapException(x), name); - } + if (Tracer.ClassLoading.TraceError) { - java.lang.ClassLoader cl = GetJavaClassLoader(); + var cl = GetJavaClassLoader(); if (cl != null) { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - string sep = ""; + var sb = new System.Text.StringBuilder(); + var sep = ""; while (cl != null) { sb.Append(sep).Append(cl); sep = " -> "; cl = cl.getParent(); } + Tracer.Error(Tracer.ClassLoading, "ClassLoader chain: {0}", sb); } - Exception m = ikvm.runtime.Util.mapException(x); + + var m = ikvm.runtime.Util.mapException(x); Tracer.Error(Tracer.ClassLoading, m.ToString() + Environment.NewLine + m.StackTrace); } + return null; } finally @@ -845,7 +878,7 @@ private static TypeWrapper CreateArrayType(string name, TypeWrapper elementTypeW return elementTypeWrapper.GetClassLoader().RegisterInitiatingLoader(new ArrayTypeWrapper(elementTypeWrapper, name)); } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal virtual java.lang.ClassLoader GetJavaClassLoader() { #if FIRST_PASS @@ -962,7 +995,7 @@ internal TypeWrapper[] ArgTypeWrapperListFromSig(string sig, LoadMode mode) return list.ToArray(); } -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER internal static ClassLoaderWrapper GetBootstrapClassLoader() #else internal static AssemblyClassLoader GetBootstrapClassLoader() @@ -977,7 +1010,7 @@ internal static AssemblyClassLoader GetBootstrapClassLoader() } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal static ClassLoaderWrapper GetClassLoaderWrapper(java.lang.ClassLoader javaClassLoader) { @@ -1020,14 +1053,14 @@ internal static ClassLoaderWrapper GetClassLoaderForDynamicJavaAssembly(Assembly internal static TypeWrapper GetWrapperFromType(Type type) { -#if STATIC_COMPILER +#if IMPORTER if (type.__ContainsMissingType) { return new UnloadableTypeWrapper(type); } #endif //Tracer.Info(Tracer.Runtime, "GetWrapperFromType: {0}", type.AssemblyQualifiedName); -#if !STATIC_COMPILER +#if !IMPORTER TypeWrapper.AssertFinished(type); #endif Debug.Assert(!type.IsPointer); @@ -1043,13 +1076,13 @@ internal static TypeWrapper GetWrapperFromType(Type type) return wrapper; } -#if STUB_GENERATOR - if(type.__IsMissing || type.__ContainsMissingType) - { - wrapper = new UnloadableTypeWrapper("Missing/" + type.Assembly.FullName); - globalTypeToTypeWrapper.Add(type, wrapper); - return wrapper; - } +#if EXPORTER + if (type.__IsMissing || type.__ContainsMissingType) + { + wrapper = new UnloadableTypeWrapper("Missing/" + type.Assembly.FullName); + globalTypeToTypeWrapper.Add(type, wrapper); + return wrapper; + } #endif string remapped; if (remappedTypes.TryGetValue(type, out remapped)) @@ -1087,7 +1120,7 @@ internal static TypeWrapper GetWrapperFromType(Type type) } } #endif -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER if (AnonymousTypeWrapper.IsAnonymous(type)) { Dictionary typeToTypeWrapper; @@ -1161,7 +1194,7 @@ internal static ClassLoaderWrapper GetGenericClassLoader(TypeWrapper wrapper) return matchingLoader; } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal static object DoPrivileged(java.security.PrivilegedAction action) { @@ -1181,7 +1214,7 @@ static ClassLoaderWrapper GetGenericClassLoaderByKey(ClassLoaderWrapper[] key) if (loader.Matches(key)) return loader; -#if STATIC_COMPILER || STUB_GENERATOR || FIRST_PASS +#if IMPORTER || EXPORTER || FIRST_PASS var newLoader = new GenericClassLoaderWrapper(key, null); #else var javaClassLoader = new ikvm.runtime.GenericClassLoader(); @@ -1193,7 +1226,7 @@ static ClassLoaderWrapper GetGenericClassLoaderByKey(ClassLoaderWrapper[] key) } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER protected internal static void SetWrapperForClassLoader(java.lang.ClassLoader javaClassLoader, ClassLoaderWrapper wrapper) { @@ -1206,7 +1239,7 @@ protected internal static void SetWrapperForClassLoader(java.lang.ClassLoader ja #endif -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal static ClassLoaderWrapper GetGenericClassLoaderByName(string name) { @@ -1281,7 +1314,7 @@ internal static ClassLoaderWrapper GetGenericClassLoaderById(int id) internal void SetWrapperForType(Type type, TypeWrapper wrapper) { -#if !STATIC_COMPILER +#if !IMPORTER TypeWrapper.AssertFinished(type); #endif @@ -1306,7 +1339,7 @@ internal void SetWrapperForType(Type type, TypeWrapper wrapper) internal static TypeWrapper LoadClassCritical(string name) { -#if STATIC_COMPILER +#if IMPORTER var wrapper = GetBootstrapClassLoader().LoadClassByDottedNameFast(name); if (wrapper == null) throw new FatalCompilerErrorException(Message.CriticalClassNotFound, name); @@ -1317,10 +1350,9 @@ internal static TypeWrapper LoadClassCritical(string name) { return GetBootstrapClassLoader().LoadClassByDottedName(name); } - catch (Exception x) + catch (Exception e) { - JVM.CriticalFailure("Loading of critical class failed", x); - return null; + throw new InternalException("Loading of critical class failed.", e); } #endif } @@ -1364,7 +1396,7 @@ internal nint[] GetNativeLibraries() return nativeLibraries == null ? Array.Empty() : nativeLibraries.ToArray(); } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER public override string ToString() { object javaClassLoader = GetJavaClassLoader(); @@ -1382,7 +1414,7 @@ internal virtual bool InternalsVisibleToImpl(TypeWrapper wrapper, TypeWrapper fr return this == friend.GetClassLoader(); } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER // this method is used by IKVM.Runtime.JNI internal static ClassLoaderWrapper FromCallerID(ikvm.@internal.CallerID callerID) { @@ -1394,7 +1426,7 @@ internal static ClassLoaderWrapper FromCallerID(ikvm.@internal.CallerID callerID } #endif -#if STATIC_COMPILER +#if IMPORTER internal virtual void IssueMessage(Message msgId, params string[] values) { // it's not ideal when we end up here (because it means we're emitting a warning that is not associated with a specific output target), @@ -1405,18 +1437,18 @@ internal virtual void IssueMessage(Message msgId, params string[] values) internal void CheckPackageAccess(TypeWrapper tw, ProtectionDomain pd) { -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER if (javaClassLoader != null) - javaClassLoader.checkPackageAccess(tw.ClassObject, pd); + ClassLoaderAccessor.InvokeCheckPackageAccess(javaClassLoader, tw.ClassObject, pd); #endif } -#if !STUB_GENERATOR +#if !EXPORTER internal ClassFileParseOptions ClassFileParseOptions { get { -#if STATIC_COMPILER +#if IMPORTER var cfp = ClassFileParseOptions.LocalVariableTable; if (EmitStackTraceInfo) cfp |= ClassFileParseOptions.LineNumberTable; @@ -1440,7 +1472,7 @@ internal ClassFileParseOptions ClassFileParseOptions } #endif -#if STATIC_COMPILER +#if IMPORTER internal virtual bool WarningLevelHigh { get { return false; } diff --git a/src/IKVM.Runtime/CodeEmitter.cs b/src/IKVM.Runtime/CodeEmitter.cs index d50a77b90a..a476672f0c 100644 --- a/src/IKVM.Runtime/CodeEmitter.cs +++ b/src/IKVM.Runtime/CodeEmitter.cs @@ -24,7 +24,13 @@ Jeroen Frijters #define CHECK_INVARIANTS using System; using System.Collections.Generic; -#if STATIC_COMPILER +using System.Runtime.InteropServices; +using System.Diagnostics.SymbolStore; +using System.Diagnostics; + +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -32,2032 +38,2020 @@ Jeroen Frijters using System.Reflection; using System.Reflection.Emit; #endif -using System.Runtime.InteropServices; -using System.Diagnostics.SymbolStore; -using System.Diagnostics; namespace IKVM.Internal { sealed class CodeEmitter - { - - private static readonly MethodInfo objectToString = Types.Object.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); - private static readonly MethodInfo verboseCastFailure = JVM.SafeGetEnvironmentVariable("IKVM_VERBOSE_CAST") == null ? null : ByteCodeHelperMethods.VerboseCastFailure; - private static readonly MethodInfo monitorEnter = JVM.Import(typeof(System.Threading.Monitor)).GetMethod("Enter", BindingFlags.Public | BindingFlags.Static, null, new Type[] { Types.Object }, null); - private static readonly MethodInfo monitorExit = JVM.Import(typeof(System.Threading.Monitor)).GetMethod("Exit", BindingFlags.Public | BindingFlags.Static, null, new Type[] { Types.Object }, null); - private static readonly bool experimentalOptimizations = JVM.SafeGetEnvironmentVariable("IKVM_EXPERIMENTAL_OPTIMIZATIONS") != null; - private static MethodInfo memoryBarrier; - private ILGenerator ilgen_real; -#if !STATIC_COMPILER - private bool inFinally; - private Stack exceptionStack = new Stack(); + { + + static readonly MethodInfo objectToString = Types.Object.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); + static readonly MethodInfo verboseCastFailure = JVM.SafeGetEnvironmentVariable("IKVM_VERBOSE_CAST") == null ? null : ByteCodeHelperMethods.VerboseCastFailure; + static readonly MethodInfo monitorEnter = JVM.Import(typeof(System.Threading.Monitor)).GetMethod("Enter", BindingFlags.Public | BindingFlags.Static, null, new Type[] { Types.Object }, null); + static readonly MethodInfo monitorExit = JVM.Import(typeof(System.Threading.Monitor)).GetMethod("Exit", BindingFlags.Public | BindingFlags.Static, null, new Type[] { Types.Object }, null); + static readonly bool experimentalOptimizations = JVM.SafeGetEnvironmentVariable("IKVM_EXPERIMENTAL_OPTIMIZATIONS") != null; + static MethodInfo memoryBarrier; + ILGenerator ilgen_real; +#if !IMPORTER + bool inFinally; + Stack exceptionStack = new Stack(); #endif - private IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter linenums; - private CodeEmitterLocal[] tempLocals = new CodeEmitterLocal[32]; + IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter linenums; + CodeEmitterLocal[] tempLocals = new CodeEmitterLocal[32]; #if NETFRAMEWORK - private ISymbolDocumentWriter symbols; + ISymbolDocumentWriter symbols; #endif - private List code = new List(10); - private readonly Type declaringType; + List code = new List(10); + readonly Type declaringType; #if LABELCHECK - private Dictionary labels = new Dictionary(); + Dictionary labels = new Dictionary(); #endif - static CodeEmitter() - { - if (experimentalOptimizations) - { - Console.Error.WriteLine("IKVM.NET experimental optimizations enabled."); - } - } - - enum CodeType : short - { - Unreachable, - OpCode, - BeginScope, - EndScope, - DeclareLocal, - ReleaseTempLocal, - SequencePoint, - LineNumber, - Label, - BeginExceptionBlock, - BeginCatchBlock, - BeginFaultBlock, - BeginFinallyBlock, - EndExceptionBlock, - MemoryBarrier, - TailCallPrevention, - ClearStack, - MonitorEnter, - MonitorExit, - } - - enum CodeTypeFlags : short - { - None = 0, - EndFaultOrFinally = 1, - } - - struct OpCodeWrapper - { - internal readonly CodeType pseudo; - private readonly CodeTypeFlags flags; - internal readonly OpCode opcode; - private readonly object data; - - internal OpCodeWrapper(CodeType pseudo, object data) - { - this.pseudo = pseudo; - this.flags = CodeTypeFlags.None; - this.opcode = OpCodes.Nop; - this.data = data; - } - - internal OpCodeWrapper(CodeType pseudo, CodeTypeFlags flags) - { - this.pseudo = pseudo; - this.flags = flags; - this.opcode = OpCodes.Nop; - this.data = null; - } - - internal OpCodeWrapper(OpCode opcode, object data) - { - this.pseudo = CodeType.OpCode; - this.flags = CodeTypeFlags.None; - this.opcode = opcode; - this.data = data; - } - - internal bool Match(OpCodeWrapper other) - { - return other.pseudo == pseudo - && other.opcode == opcode - && (other.data == data || (data != null && data.Equals(other.data))); - } - - internal bool HasLabel - { - get { return data is CodeEmitterLabel; } - } - - internal CodeEmitterLabel Label - { - get { return (CodeEmitterLabel)data; } - } - - internal bool MatchLabel(OpCodeWrapper other) - { - return data == other.data; - } - - internal CodeEmitterLabel[] Labels - { - get { return (CodeEmitterLabel[])data; } - } - - internal bool HasLocal - { - get { return data is CodeEmitterLocal; } - } - - internal CodeEmitterLocal Local - { - get { return (CodeEmitterLocal)data; } - } - - internal bool MatchLocal(OpCodeWrapper other) - { - return data == other.data; - } - - internal bool HasValueByte - { - get { return data is byte; } - } - - internal byte ValueByte - { - get { return (byte)data; } - } - - internal short ValueInt16 - { - get { return (short)data; } - } - - internal int ValueInt32 - { - get { return (int)data; } - } - - internal long ValueInt64 - { - get { return (long)data; } - } - - internal Type Type - { - get { return (Type)data; } - } - - internal FieldInfo FieldInfo - { - get { return (FieldInfo)data; } - } - - internal MethodBase MethodBase - { - get { return (MethodBase)data; } - } - - internal int Size - { - get - { - switch (pseudo) - { - case CodeType.Unreachable: - case CodeType.BeginScope: - case CodeType.EndScope: - case CodeType.DeclareLocal: - case CodeType.ReleaseTempLocal: - case CodeType.LineNumber: - case CodeType.Label: - case CodeType.BeginExceptionBlock: - return 0; - case CodeType.SequencePoint: - return 1; - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - case CodeType.EndExceptionBlock: -#if STATIC_COMPILER + static CodeEmitter() + { + if (experimentalOptimizations) + { + Console.Error.WriteLine("IKVM.NET experimental optimizations enabled."); + } + } + + enum CodeType : short + { + Unreachable, + OpCode, + BeginScope, + EndScope, + DeclareLocal, + ReleaseTempLocal, + SequencePoint, + LineNumber, + Label, + BeginExceptionBlock, + BeginCatchBlock, + BeginFaultBlock, + BeginFinallyBlock, + EndExceptionBlock, + MemoryBarrier, + TailCallPrevention, + ClearStack, + MonitorEnter, + MonitorExit, + } + + enum CodeTypeFlags : short + { + None = 0, + EndFaultOrFinally = 1, + } + + struct OpCodeWrapper + { + + internal readonly CodeType pseudo; + readonly CodeTypeFlags flags; + internal readonly OpCode opcode; + readonly object data; + + /// + /// Initializes a new instance. + /// + /// + /// + internal OpCodeWrapper(CodeType pseudo, object data) + { + this.pseudo = pseudo; + this.flags = CodeTypeFlags.None; + this.opcode = OpCodes.Nop; + this.data = data; + } + + /// + /// Initializes a new instance. + /// + /// + /// + internal OpCodeWrapper(CodeType pseudo, CodeTypeFlags flags) + { + this.pseudo = pseudo; + this.flags = flags; + this.opcode = OpCodes.Nop; + this.data = null; + } + + /// + /// Initializes a new instance. + /// + /// + /// + internal OpCodeWrapper(OpCode opcode, object data) + { + this.pseudo = CodeType.OpCode; + this.flags = CodeTypeFlags.None; + this.opcode = opcode; + this.data = data; + } + + internal bool Match(OpCodeWrapper other) => other.pseudo == pseudo && other.opcode == opcode && (other.data == data || (data != null && data.Equals(other.data))); + + internal bool HasLabel => data is CodeEmitterLabel; + + internal CodeEmitterLabel Label => (CodeEmitterLabel)data; + + internal bool MatchLabel(OpCodeWrapper other) => data == other.data; + + internal CodeEmitterLabel[] Labels => (CodeEmitterLabel[])data; + + internal bool HasLocal => data is CodeEmitterLocal; + + internal CodeEmitterLocal Local => (CodeEmitterLocal)data; + + internal bool MatchLocal(OpCodeWrapper other) => data == other.data; + + internal bool HasValueByte => data is byte; + + internal byte ValueByte => (byte)data; + + internal short ValueInt16 => (short)data; + + internal int ValueInt32 => (int)data; + + internal long ValueInt64 => (long)data; + + internal Type Type => (Type)data; + + internal FieldInfo FieldInfo => (FieldInfo)data; + + internal MethodBase MethodBase => (MethodBase)data; + + internal int Size + { + get + { + switch (pseudo) + { + case CodeType.Unreachable: + case CodeType.BeginScope: + case CodeType.EndScope: + case CodeType.DeclareLocal: + case CodeType.ReleaseTempLocal: + case CodeType.LineNumber: + case CodeType.Label: + case CodeType.BeginExceptionBlock: + return 0; + case CodeType.SequencePoint: + return 1; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + case CodeType.EndExceptionBlock: +#if IMPORTER return 0; #else - if ((flags & CodeTypeFlags.EndFaultOrFinally) != 0) - { - return 1 + 2; - } - return 5 + 2; + if ((flags & CodeTypeFlags.EndFaultOrFinally) != 0) + return 1 + 2; + return 5 + 2; #endif - case CodeType.MemoryBarrier: - case CodeType.MonitorEnter: - case CodeType.MonitorExit: - return 5; - case CodeType.TailCallPrevention: - return 2; - case CodeType.ClearStack: - return 2; - case CodeType.OpCode: - if (data == null) - { - return opcode.Size; - } - else if (data is int) - { - return opcode.Size + 4;; - } - else if (data is long) - { - return opcode.Size + 8; - } - else if (data is MethodInfo) - { - return opcode.Size + 4; - } - else if (data is ConstructorInfo) - { - return opcode.Size + 4; - } - else if (data is FieldInfo) - { - return opcode.Size + 4; - } - else if (data is sbyte) - { - return opcode.Size + 1; - } - else if (data is byte) - { - return opcode.Size + 1; - } - else if (data is short) - { - return opcode.Size + 2; - } - else if (data is float) - { - return opcode.Size + 4; - } - else if (data is double) - { - return opcode.Size + 8; - } - else if (data is string) - { - return opcode.Size + 4; - } - else if (data is Type) - { - return opcode.Size + 4; - } - else if (data is CodeEmitterLocal) - { - int index = ((CodeEmitterLocal)data).__LocalIndex; - if(index < 4 && opcode.Value != OpCodes.Ldloca.Value && opcode.Value != OpCodes.Ldloca_S.Value) - { - return 1; - } - else if(index < 256) - { - return 2; - } - else - { - return 4; - } - } - else if (data is CodeEmitterLabel) - { - switch(opcode.OperandType) - { - case OperandType.InlineBrTarget: - return opcode.Size + 4; - case OperandType.ShortInlineBrTarget: - return opcode.Size + 1; - default: - throw new InvalidOperationException(); - } - } - else if (data is CodeEmitterLabel[]) - { - return 5 + ((CodeEmitterLabel[])data).Length * 4; - } - else if (data is CalliWrapper) - { - return 5; - } - else - { - throw new InvalidOperationException(); - } - default: - throw new InvalidOperationException(); - } - } - } - - internal void RealEmit(int ilOffset, CodeEmitter codeEmitter, ref int lineNumber) - { - if (pseudo == CodeType.OpCode) - { - if (lineNumber != -1) - { - if (codeEmitter.linenums == null) - { - codeEmitter.linenums = new IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter(32); - } - codeEmitter.linenums.AddMapping(ilOffset, lineNumber); - lineNumber = -1; - } - codeEmitter.RealEmitOpCode(opcode, data); - } - else if (pseudo == CodeType.LineNumber) - { - lineNumber = (int)data; - } - else - { - codeEmitter.RealEmitPseudoOpCode(ilOffset, pseudo, data); - } - } - - public override string ToString() - { - return pseudo.ToString() + " " + data; - } - } - - sealed class CalliWrapper - { - internal readonly CallingConvention unmanagedCallConv; - internal readonly Type returnType; - internal readonly Type[] parameterTypes; - - internal CalliWrapper(CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) - { - this.unmanagedCallConv = unmanagedCallConv; - this.returnType = returnType; - this.parameterTypes = parameterTypes == null ? null : (Type[])parameterTypes.Clone(); - } - } - - internal static CodeEmitter Create(MethodBuilder mb) - { - return new CodeEmitter(mb.GetILGenerator(), mb.DeclaringType); - } - -#if !STATIC_COMPILER - internal static CodeEmitter Create(DynamicMethod dm) - { - return new CodeEmitter(dm.GetILGenerator(), null); - } + case CodeType.MemoryBarrier: + case CodeType.MonitorEnter: + case CodeType.MonitorExit: + return 5; + case CodeType.TailCallPrevention: + return 2; + case CodeType.ClearStack: + return 2; + case CodeType.OpCode: + if (data == null) + { + return opcode.Size; + } + else if (data is int) + { + return opcode.Size + 4; ; + } + else if (data is long) + { + return opcode.Size + 8; + } + else if (data is MethodInfo) + { + return opcode.Size + 4; + } + else if (data is ConstructorInfo) + { + return opcode.Size + 4; + } + else if (data is FieldInfo) + { + return opcode.Size + 4; + } + else if (data is sbyte) + { + return opcode.Size + 1; + } + else if (data is byte) + { + return opcode.Size + 1; + } + else if (data is short) + { + return opcode.Size + 2; + } + else if (data is float) + { + return opcode.Size + 4; + } + else if (data is double) + { + return opcode.Size + 8; + } + else if (data is string) + { + return opcode.Size + 4; + } + else if (data is Type) + { + return opcode.Size + 4; + } + else if (data is CodeEmitterLocal cel) + { + int index = cel.__LocalIndex; + if (index < 4 && opcode.Value != OpCodes.Ldloca.Value && opcode.Value != OpCodes.Ldloca_S.Value) + { + return 1; + } + else if (index < 256) + { + return 2; + } + else + { + return 4; + } + } + else if (data is CodeEmitterLabel) + { + switch (opcode.OperandType) + { + case OperandType.InlineBrTarget: + return opcode.Size + 4; + case OperandType.ShortInlineBrTarget: + return opcode.Size + 1; + default: + throw new InvalidOperationException(); + } + } + else if (data is CodeEmitterLabel[] cela) + { + return 5 + cela.Length * 4; + } + else if (data is ManagedCalliWrapper) + { + return 6; + } + else if (data is CalliWrapper) + { + return 5; + } + else + { + throw new InvalidOperationException(); + } + default: + throw new InvalidOperationException(); + } + } + } + + internal void RealEmit(int ilOffset, CodeEmitter codeEmitter, ref int lineNumber) + { + if (pseudo == CodeType.OpCode) + { + if (lineNumber != -1) + { + codeEmitter.linenums ??= new IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter(32); + codeEmitter.linenums.AddMapping(ilOffset, lineNumber); + lineNumber = -1; + } + + codeEmitter.RealEmitOpCode(opcode, data); + } + else if (pseudo == CodeType.LineNumber) + { + lineNumber = (int)data; + } + else + { + codeEmitter.RealEmitPseudoOpCode(ilOffset, pseudo, data); + } + } + + public override string ToString() => pseudo.ToString() + " " + data; + + } + + sealed class CalliWrapper + { + + internal readonly CallingConvention unmanagedCallConv; + internal readonly Type returnType; + internal readonly Type[] parameterTypes; + + internal CalliWrapper(CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) + { + this.unmanagedCallConv = unmanagedCallConv; + this.returnType = returnType; + this.parameterTypes = parameterTypes == null ? null : (Type[])parameterTypes.Clone(); + } + + } + + sealed class ManagedCalliWrapper + { + + internal readonly CallingConventions callConv; + internal readonly Type returnType; + internal readonly Type[] parameterTypes; + internal readonly Type[] optionalParameterTypes; + + internal ManagedCalliWrapper(CallingConventions callConv, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) + { + this.callConv = callConv; + this.returnType = returnType; + this.parameterTypes = parameterTypes == null ? null : (Type[])parameterTypes.Clone(); + this.optionalParameterTypes = optionalParameterTypes == null ? null : (Type[])optionalParameterTypes.Clone(); + } + + } + + internal static CodeEmitter Create(MethodBuilder mb) + { + return new CodeEmitter(mb.GetILGenerator(), mb.DeclaringType); + } + +#if !IMPORTER + internal static CodeEmitter Create(DynamicMethod dm) + { + return new CodeEmitter(dm.GetILGenerator(), null); + } #endif - private CodeEmitter(ILGenerator ilgen, Type declaringType) - { -#if STATIC_COMPILER + private CodeEmitter(ILGenerator ilgen, Type declaringType) + { +#if IMPORTER ilgen.__DisableExceptionBlockAssistance(); #endif - this.ilgen_real = ilgen; - this.declaringType = declaringType; - } - - private void EmitPseudoOpCode(CodeType type, object data) - { - code.Add(new OpCodeWrapper(type, data)); - } - - private void EmitOpCode(OpCode opcode, object arg) - { - code.Add(new OpCodeWrapper(opcode, arg)); - } - - private void RealEmitPseudoOpCode(int ilOffset, CodeType type, object data) - { - switch (type) - { - case CodeType.Unreachable: - break; - case CodeType.BeginScope: - ilgen_real.BeginScope(); - break; - case CodeType.EndScope: - ilgen_real.EndScope(); - break; - case CodeType.DeclareLocal: - ((CodeEmitterLocal)data).Declare(ilgen_real); - break; - case CodeType.ReleaseTempLocal: - break; - case CodeType.SequencePoint: - // MarkSequencePoint does not exist in .net core + this.ilgen_real = ilgen; + this.declaringType = declaringType; + } + + private void EmitPseudoOpCode(CodeType type, object data) + { + code.Add(new OpCodeWrapper(type, data)); + } + + private void EmitOpCode(OpCode opcode, object arg) + { + code.Add(new OpCodeWrapper(opcode, arg)); + } + + private void RealEmitPseudoOpCode(int ilOffset, CodeType type, object data) + { + switch (type) + { + case CodeType.Unreachable: + break; + case CodeType.BeginScope: + ilgen_real.BeginScope(); + break; + case CodeType.EndScope: + ilgen_real.EndScope(); + break; + case CodeType.DeclareLocal: + ((CodeEmitterLocal)data).Declare(ilgen_real); + break; + case CodeType.ReleaseTempLocal: + break; + case CodeType.SequencePoint: + // MarkSequencePoint does not exist in .net core #if NETFRAMEWORK - ilgen_real.MarkSequencePoint(symbols, (int)data, 0, (int)data + 1, 0); + ilgen_real.MarkSequencePoint(symbols, (int)data, 0, (int)data + 1, 0); #endif - // we emit a nop to make sure we always have an instruction associated with the sequence point - ilgen_real.Emit(OpCodes.Nop); - break; - case CodeType.Label: - ilgen_real.MarkLabel(((CodeEmitterLabel)data).Label); - break; - case CodeType.BeginExceptionBlock: - ilgen_real.BeginExceptionBlock(); - break; - case CodeType.BeginCatchBlock: - ilgen_real.BeginCatchBlock((Type)data); - break; - case CodeType.BeginFaultBlock: - ilgen_real.BeginFaultBlock(); - break; - case CodeType.BeginFinallyBlock: - ilgen_real.BeginFinallyBlock(); - break; - case CodeType.EndExceptionBlock: - ilgen_real.EndExceptionBlock(); -#if !STATIC_COMPILER - // HACK to keep the verifier happy we need this bogus jump - // (because of the bogus Leave that Ref.Emit ends the try block with) - ilgen_real.Emit(OpCodes.Br_S, (sbyte)-2); + // we emit a nop to make sure we always have an instruction associated with the sequence point + ilgen_real.Emit(OpCodes.Nop); + break; + case CodeType.Label: + ilgen_real.MarkLabel(((CodeEmitterLabel)data).Label); + break; + case CodeType.BeginExceptionBlock: + ilgen_real.BeginExceptionBlock(); + break; + case CodeType.BeginCatchBlock: + ilgen_real.BeginCatchBlock((Type)data); + break; + case CodeType.BeginFaultBlock: + ilgen_real.BeginFaultBlock(); + break; + case CodeType.BeginFinallyBlock: + ilgen_real.BeginFinallyBlock(); + break; + case CodeType.EndExceptionBlock: + ilgen_real.EndExceptionBlock(); +#if !IMPORTER + // HACK to keep the verifier happy we need this bogus jump + // (because of the bogus Leave that Ref.Emit ends the try block with) + ilgen_real.Emit(OpCodes.Br_S, (sbyte)-2); #endif - break; - case CodeType.MemoryBarrier: - if (memoryBarrier == null) - { - memoryBarrier = JVM.Import(typeof(System.Threading.Thread)).GetMethod("MemoryBarrier", Type.EmptyTypes); - } - ilgen_real.Emit(OpCodes.Call, memoryBarrier); - break; - case CodeType.MonitorEnter: - ilgen_real.Emit(OpCodes.Call, monitorEnter); - break; - case CodeType.MonitorExit: - ilgen_real.Emit(OpCodes.Call, monitorExit); - break; - case CodeType.TailCallPrevention: - ilgen_real.Emit(OpCodes.Ldnull); - ilgen_real.Emit(OpCodes.Pop); - break; - case CodeType.ClearStack: - ilgen_real.Emit(OpCodes.Leave_S, (byte)0); - break; - default: - throw new InvalidOperationException(); - } - } - - private void RealEmitOpCode(OpCode opcode, object arg) - { - if (arg == null) - { - ilgen_real.Emit(opcode); - } - else if (arg is int) - { - ilgen_real.Emit(opcode, (int)arg); - } - else if (arg is long) - { - ilgen_real.Emit(opcode, (long)arg); - } - else if (arg is MethodInfo) - { - ilgen_real.Emit(opcode, (MethodInfo)arg); - } - else if (arg is ConstructorInfo) - { - ilgen_real.Emit(opcode, (ConstructorInfo)arg); - } - else if (arg is FieldInfo) - { - ilgen_real.Emit(opcode, (FieldInfo)arg); - } - else if (arg is sbyte) - { - ilgen_real.Emit(opcode, (sbyte)arg); - } - else if (arg is byte) - { - ilgen_real.Emit(opcode, (byte)arg); - } - else if (arg is short) - { - ilgen_real.Emit(opcode, (short)arg); - } - else if (arg is float) - { - ilgen_real.Emit(opcode, (float)arg); - } - else if (arg is double) - { - ilgen_real.Emit(opcode, (double)arg); - } - else if (arg is string) - { - ilgen_real.Emit(opcode, (string)arg); - } - else if (arg is Type) - { - ilgen_real.Emit(opcode, (Type)arg); - } - else if (arg is CodeEmitterLocal) - { - CodeEmitterLocal local = (CodeEmitterLocal)arg; - local.Emit(ilgen_real, opcode); - } - else if (arg is CodeEmitterLabel) - { - CodeEmitterLabel label = (CodeEmitterLabel)arg; - ilgen_real.Emit(opcode, label.Label); - } - else if (arg is CodeEmitterLabel[]) - { - CodeEmitterLabel[] labels = (CodeEmitterLabel[])arg; - Label[] real = new Label[labels.Length]; - for (int i = 0; i < labels.Length; i++) - { - real[i] = labels[i].Label; - } - ilgen_real.Emit(opcode, real); - } - else if (arg is CalliWrapper) - { - CalliWrapper args = (CalliWrapper)arg; - ilgen_real.EmitCalli(opcode, args.unmanagedCallConv, args.returnType, args.parameterTypes); - } - else - { - throw new InvalidOperationException(); - } - } - - private void RemoveJumpNext() - { - for (int i = 1; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label) - { - if (code[i - 1].opcode == OpCodes.Br - && code[i - 1].MatchLabel(code[i])) - { - code.RemoveAt(i - 1); - i--; - } - else if (i >= 2 - && code[i - 1].pseudo == CodeType.LineNumber - && code[i - 2].opcode == OpCodes.Br - && code[i - 2].MatchLabel(code[i])) - { - code.RemoveAt(i - 2); - i--; - } - } - } - } - - private void AnnihilateStoreReleaseTempLocals() - { - for (int i = 1; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Stloc) - { - if (code[i + 1].pseudo == CodeType.ReleaseTempLocal - && code[i].Local == code[i + 1].Local) - { - code[i] = new OpCodeWrapper(OpCodes.Pop, null); - } - else if (code[i + 1].opcode == OpCodes.Ldloc - && code[i + 1].Local == code[i].Local - && code[i + 2].pseudo == CodeType.ReleaseTempLocal - && code[i + 2].Local == code[i].Local) - { - code.RemoveRange(i, 2); - } - } - } - } - - private void AnnihilatePops() - { - for (int i = 1; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Pop) - { - // search backwards for a candidate push to annihilate - int stack = 0; - for (int j = i - 1; j >= 0; j--) - { - if (IsSideEffectFreePush(j)) - { - if (stack == 0) - { - code.RemoveAt(i); - code.RemoveAt(j); - i -= 2; - break; - } - stack++; - } - else if (code[j].opcode == OpCodes.Stloc) - { - stack--; - } - else if (code[j].opcode == OpCodes.Shl - || code[j].opcode == OpCodes.And - || code[j].opcode == OpCodes.Add - || code[j].opcode == OpCodes.Sub) - { - if (stack == 0) - { - break; - } - stack--; - } - else if (code[j].opcode == OpCodes.Conv_Ovf_I4 - || code[j].opcode == OpCodes.Conv_I8 - || code[j].opcode == OpCodes.Ldlen) - { - if (stack == 0) - { - break; - } - // no stack effect - } - else - { - break; - } - } - } - } - } - - private bool IsSideEffectFreePush(int index) - { - if (code[index].opcode == OpCodes.Ldstr) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldnull) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldsfld) - { - FieldInfo field = code[index].FieldInfo; - if (field != null) - { - // Here we are considering BeforeFieldInit to mean that we really don't care about - // when the type is initialized (which is what we mean in the rest of the IKVM code as well) - // but it is good to point it out here because strictly speaking we're violating the - // BeforeFieldInit contract here by considering dummy loads not to be field accesses. - if ((field.DeclaringType.Attributes & TypeAttributes.BeforeFieldInit) != 0) - { - return true; - } - // If we're accessing a field in the current type, it can't trigger the static initializer - // (unless beforefieldinit is set, but see above for that scenario) - if (field.DeclaringType == declaringType) - { - return true; - } - } - return false; - } - else if (code[index].opcode == OpCodes.Ldc_I4) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldc_I8) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldc_R4) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldc_R8) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldloc) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg_S) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg_0) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg_1) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg_2) - { - return true; - } - else if (code[index].opcode == OpCodes.Ldarg_3) - { - return true; - } - else - { - return false; - } - } - - private void OptimizeBranchSizes() - { - int offset = 0; - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label) - { - code[i].Label.Temp = offset; - } - offset += code[i].Size; - } - offset = 0; - for (int i = 0; i < code.Count; i++) - { - int prevOffset = offset; - offset += code[i].Size; - if (code[i].HasLabel && code[i].opcode.OperandType == OperandType.InlineBrTarget) - { - CodeEmitterLabel label = code[i].Label; - int diff = label.Temp - (prevOffset + code[i].opcode.Size + 1); - if (-128 <= diff && diff <= 127) - { - OpCode opcode = code[i].opcode; - if (opcode == OpCodes.Brtrue) - { - opcode = OpCodes.Brtrue_S; - } - else if (opcode == OpCodes.Brfalse) - { - opcode = OpCodes.Brfalse_S; - } - else if (opcode == OpCodes.Br) - { - opcode = OpCodes.Br_S; - } - else if (opcode == OpCodes.Beq) - { - opcode = OpCodes.Beq_S; - } - else if (opcode == OpCodes.Bne_Un) - { - opcode = OpCodes.Bne_Un_S; - } - else if (opcode == OpCodes.Ble) - { - opcode = OpCodes.Ble_S; - } - else if (opcode == OpCodes.Ble_Un) - { - opcode = OpCodes.Ble_Un_S; - } - else if (opcode == OpCodes.Blt) - { - opcode = OpCodes.Blt_S; - } - else if (opcode == OpCodes.Blt_Un) - { - opcode = OpCodes.Blt_Un_S; - } - else if (opcode == OpCodes.Bge) - { - opcode = OpCodes.Bge_S; - } - else if (opcode == OpCodes.Bge_Un) - { - opcode = OpCodes.Bge_Un_S; - } - else if (opcode == OpCodes.Bgt) - { - opcode = OpCodes.Bgt_S; - } - else if (opcode == OpCodes.Bgt_Un) - { - opcode = OpCodes.Bgt_Un_S; - } - else if (opcode == OpCodes.Leave) - { - opcode = OpCodes.Leave_S; - } - code[i] = new OpCodeWrapper(opcode, label); - } - } - } - } - - private void OptimizePatterns() - { - SetLabelRefCounts(); - for (int i = 1; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Isinst - && code[i + 1].opcode == OpCodes.Ldnull - && code[i + 2].opcode == OpCodes.Cgt_Un - && (code[i + 3].opcode == OpCodes.Brfalse || code[i + 3].opcode == OpCodes.Brtrue)) - { - code.RemoveRange(i + 1, 2); - } - else if (code[i].opcode == OpCodes.Ldelem_I1 - && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 255 - && code[i + 2].opcode == OpCodes.And) - { - code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); - code.RemoveRange(i + 1, 2); - } - else if (code[i].opcode == OpCodes.Ldelem_I1 - && code[i + 1].opcode == OpCodes.Conv_I8 - && code[i + 2].opcode == OpCodes.Ldc_I8 && code[i + 2].ValueInt64 == 255 - && code[i + 3].opcode == OpCodes.And) - { - code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); - code.RemoveRange(i + 2, 2); - } - else if (code[i].opcode == OpCodes.Ldc_I4 - && code[i + 1].opcode == OpCodes.Ldc_I4 - && code[i + 2].opcode == OpCodes.And) - { - code[i] = new OpCodeWrapper(OpCodes.Ldc_I4, code[i].ValueInt32 & code[i + 1].ValueInt32); - code.RemoveRange(i + 1, 2); - } - else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Double) // dcmpl - || MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Single)) // fcmpl - { - PatchCompare(i, OpCodes.Ble_Un, OpCodes.Blt_Un, OpCodes.Bge, OpCodes.Bgt); - } - else if (MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Double) // dcmpg - || MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Single)) // fcmpg - { - PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge_Un, OpCodes.Bgt_Un); - } - else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt, Types.Int64)) // lcmp - { - PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge, OpCodes.Bgt); - } - else if (i < code.Count - 10 - && code[i].opcode == OpCodes.Ldc_I4 - && code[i + 1].opcode == OpCodes.Dup - && code[i + 2].opcode == OpCodes.Ldc_I4_M1 - && code[i + 3].opcode == OpCodes.Bne_Un - && code[i + 4].opcode == OpCodes.Pop - && code[i + 5].opcode == OpCodes.Neg - && code[i + 6].opcode == OpCodes.Br - && code[i + 7].pseudo == CodeType.Label && code[i + 7].MatchLabel(code[i + 3]) && code[i + 7].Label.Temp == 1 - && code[i + 8].opcode == OpCodes.Div - && code[i + 9].pseudo == CodeType.Label && code[i + 9].Label == code[i + 6].Label && code[i + 9].Label.Temp == 1) - { - int divisor = code[i].ValueInt32; - if (divisor == -1) - { - code[i] = code[i + 5]; - code.RemoveRange(i + 1, 9); - } - else - { - code[i + 1] = code[i + 8]; - code.RemoveRange(i + 2, 8); - } - } - else if (i < code.Count - 11 - && code[i].opcode == OpCodes.Ldc_I8 - && code[i + 1].opcode == OpCodes.Dup - && code[i + 2].opcode == OpCodes.Ldc_I4_M1 - && code[i + 3].opcode == OpCodes.Conv_I8 - && code[i + 4].opcode == OpCodes.Bne_Un - && code[i + 5].opcode == OpCodes.Pop - && code[i + 6].opcode == OpCodes.Neg - && code[i + 7].opcode == OpCodes.Br - && code[i + 8].pseudo == CodeType.Label && code[i + 8].MatchLabel(code[i + 4]) && code[i + 8].Label.Temp == 1 - && code[i + 9].opcode == OpCodes.Div - && code[i + 10].pseudo == CodeType.Label && code[i + 10].MatchLabel(code[i + 7]) && code[i + 10].Label.Temp == 1) - { - long divisor = code[i].ValueInt64; - if (divisor == -1) - { - code[i] = code[i + 6]; - code.RemoveRange(i + 1, 10); - } - else - { - code[i + 1] = code[i + 9]; - code.RemoveRange(i + 2, 9); - } - } - else if (code[i].opcode == OpCodes.Box - && code[i + 1].opcode == OpCodes.Unbox && code[i + 1].Type == code[i].Type) - { - CodeEmitterLocal local = new CodeEmitterLocal(code[i].Type); - code[i] = new OpCodeWrapper(OpCodes.Stloc, local); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldloca, local); - } - else if (i < code.Count - 13 - && code[i + 0].opcode == OpCodes.Box - && code[i + 1].opcode == OpCodes.Dup - && code[i + 2].opcode == OpCodes.Brtrue - && code[i + 3].opcode == OpCodes.Pop - && code[i + 4].opcode == OpCodes.Ldloca && code[i + 4].Local.LocalType == code[i + 0].Type - && code[i + 5].opcode == OpCodes.Initobj && code[i + 5].Type == code[i + 0].Type - && code[i + 6].opcode == OpCodes.Ldloc && code[i + 6].Local == code[i + 4].Local - && code[i + 7].pseudo == CodeType.ReleaseTempLocal && code[i + 7].Local == code[i + 6].Local - && code[i + 8].opcode == OpCodes.Br - && code[i + 9].pseudo == CodeType.Label && code[i + 9].MatchLabel(code[i + 2]) && code[i + 9].Label.Temp == 1 - && code[i + 10].opcode == OpCodes.Unbox && code[i + 10].Type == code[i + 0].Type - && code[i + 11].opcode == OpCodes.Ldobj && code[i + 11].Type == code[i + 0].Type - && code[i + 12].pseudo == CodeType.Label && code[i + 12].MatchLabel(code[i + 8]) && code[i + 12].Label.Temp == 1) - { - code.RemoveRange(i, 13); - } - - // NOTE intentionally not an else, because we want to optimize the code generated by the earlier compare optimization - if (i < code.Count - 6 - && code[i].opcode.FlowControl == FlowControl.Cond_Branch - && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 1 - && code[i + 2].opcode == OpCodes.Br - && code[i + 3].pseudo == CodeType.Label && code[i + 3].MatchLabel(code[i]) && code[i + 3].Label.Temp == 1 - && code[i + 4].opcode == OpCodes.Ldc_I4 && code[i + 4].ValueInt32 == 0 - && code[i + 5].pseudo == CodeType.Label && code[i + 5].MatchLabel(code[i + 2]) && code[i + 5].Label.Temp == 1) - { - if (code[i].opcode == OpCodes.Bne_Un) - { - code[i] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 1, 5); - } - else if (code[i].opcode == OpCodes.Beq) - { - code[i + 0] = new OpCodeWrapper(OpCodes.Ceq, null); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); - code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 3, 3); - } - else if (code[i].opcode == OpCodes.Ble || code[i].opcode == OpCodes.Ble_Un) - { - code[i] = new OpCodeWrapper(OpCodes.Cgt, null); - code.RemoveRange(i + 1, 5); - } - else if (code[i].opcode == OpCodes.Blt) - { - code[i] = new OpCodeWrapper(OpCodes.Clt, null); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); - code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 3, 3); - } - else if (code[i].opcode == OpCodes.Blt_Un) - { - code[i] = new OpCodeWrapper(OpCodes.Clt_Un, null); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); - code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 3, 3); - } - else if (code[i].opcode == OpCodes.Bge || code[i].opcode == OpCodes.Bge_Un) - { - code[i] = new OpCodeWrapper(OpCodes.Clt, null); - code.RemoveRange(i + 1, 5); - } - else if (code[i].opcode == OpCodes.Bgt) - { - code[i] = new OpCodeWrapper(OpCodes.Cgt, null); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); - code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 3, 3); - } - else if (code[i].opcode == OpCodes.Bgt_Un) - { - code[i] = new OpCodeWrapper(OpCodes.Cgt_Un, null); - code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); - code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); - code.RemoveRange(i + 3, 3); - } - } - } - } - - private bool MatchCompare(int index, OpCode cmp1, OpCode cmp2, Type type) - { - return code[index].opcode == OpCodes.Stloc && code[index].Local.LocalType == type - && code[index + 1].opcode == OpCodes.Stloc && code[index + 1].Local.LocalType == type - && code[index + 2].opcode == OpCodes.Ldloc && code[index + 2].MatchLocal(code[index + 1]) - && code[index + 3].opcode == OpCodes.Ldloc && code[index + 3].MatchLocal(code[index]) - && code[index + 4].opcode == cmp1 - && code[index + 5].opcode == OpCodes.Ldloc && code[index + 5].MatchLocal(code[index + 1]) - && code[index + 6].opcode == OpCodes.Ldloc && code[index + 6].MatchLocal(code[index]) - && code[index + 7].opcode == cmp2 - && code[index + 8].opcode == OpCodes.Sub - && code[index + 9].pseudo == CodeType.ReleaseTempLocal && code[index + 9].Local == code[index].Local - && code[index + 10].pseudo == CodeType.ReleaseTempLocal && code[index + 10].Local == code[index + 1].Local - && ((code[index + 11].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 11].HasLabel) || - (code[index + 11].opcode == OpCodes.Ldc_I4_0 - && (code[index + 12].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 12].HasLabel))); - } - - private void PatchCompare(int index, OpCode ble, OpCode blt, OpCode bge, OpCode bgt) - { - if (code[index + 11].opcode == OpCodes.Brtrue) - { - code[index] = new OpCodeWrapper(OpCodes.Bne_Un, code[index + 11].Label); - code.RemoveRange(index + 1, 11); - } - else if (code[index + 11].opcode == OpCodes.Brfalse) - { - code[index] = new OpCodeWrapper(OpCodes.Beq, code[index + 11].Label); - code.RemoveRange(index + 1, 11); - } - else if (code[index + 11].opcode == OpCodes.Ldc_I4_0) - { - if (code[index + 12].opcode == OpCodes.Ble) - { - code[index] = new OpCodeWrapper(ble, code[index + 12].Label); - code.RemoveRange(index + 1, 12); - } - else if (code[index + 12].opcode == OpCodes.Blt) - { - code[index] = new OpCodeWrapper(blt, code[index + 12].Label); - code.RemoveRange(index + 1, 12); - } - else if (code[index + 12].opcode == OpCodes.Bge) - { - code[index] = new OpCodeWrapper(bge, code[index + 12].Label); - code.RemoveRange(index + 1, 12); - } - else if (code[index + 12].opcode == OpCodes.Bgt) - { - code[index] = new OpCodeWrapper(bgt, code[index + 12].Label); - code.RemoveRange(index + 1, 12); - } - } - } - - private void OptimizeEncodings() - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Ldc_I4) - { - code[i] = OptimizeLdcI4(code[i].ValueInt32); - } - else if (code[i].opcode == OpCodes.Ldc_I8) - { - OptimizeLdcI8(i); - } - } - } - - private OpCodeWrapper OptimizeLdcI4(int value) - { - switch (value) - { - case -1: - return new OpCodeWrapper(OpCodes.Ldc_I4_M1, null); - case 0: - return new OpCodeWrapper(OpCodes.Ldc_I4_0, null); - case 1: - return new OpCodeWrapper(OpCodes.Ldc_I4_1, null); - case 2: - return new OpCodeWrapper(OpCodes.Ldc_I4_2, null); - case 3: - return new OpCodeWrapper(OpCodes.Ldc_I4_3, null); - case 4: - return new OpCodeWrapper(OpCodes.Ldc_I4_4, null); - case 5: - return new OpCodeWrapper(OpCodes.Ldc_I4_5, null); - case 6: - return new OpCodeWrapper(OpCodes.Ldc_I4_6, null); - case 7: - return new OpCodeWrapper(OpCodes.Ldc_I4_7, null); - case 8: - return new OpCodeWrapper(OpCodes.Ldc_I4_8, null); - default: - if (value >= -128 && value <= 127) - { - return new OpCodeWrapper(OpCodes.Ldc_I4_S, (sbyte)value); - } - else - { - return new OpCodeWrapper(OpCodes.Ldc_I4, value); - } - } - } - - private void OptimizeLdcI8(int index) - { - long value = code[index].ValueInt64; - if (value >= int.MinValue && value <= uint.MaxValue) - { - code[index] = OptimizeLdcI4((int)value); - code.Insert(index + 1, new OpCodeWrapper(value < 0 ? OpCodes.Conv_I8 : OpCodes.Conv_U8, null)); - } - } - - private void ChaseBranches() - { - /* + break; + case CodeType.MemoryBarrier: + if (memoryBarrier == null) + { + memoryBarrier = JVM.Import(typeof(System.Threading.Thread)).GetMethod("MemoryBarrier", Type.EmptyTypes); + } + ilgen_real.Emit(OpCodes.Call, memoryBarrier); + break; + case CodeType.MonitorEnter: + ilgen_real.Emit(OpCodes.Call, monitorEnter); + break; + case CodeType.MonitorExit: + ilgen_real.Emit(OpCodes.Call, monitorExit); + break; + case CodeType.TailCallPrevention: + ilgen_real.Emit(OpCodes.Ldnull); + ilgen_real.Emit(OpCodes.Pop); + break; + case CodeType.ClearStack: + ilgen_real.Emit(OpCodes.Leave_S, (byte)0); + break; + default: + throw new InvalidOperationException(); + } + } + + /// + /// Emits the real OpCode into the underlying IL generator. + /// + /// + /// + /// + void RealEmitOpCode(OpCode opcode, object arg) + { + if (arg == null) + { + ilgen_real.Emit(opcode); + } + else if (arg is int ina) + { + ilgen_real.Emit(opcode, ina); + } + else if (arg is long l) + { + ilgen_real.Emit(opcode, l); + } + else if (arg is MethodInfo mi) + { + ilgen_real.Emit(opcode, mi); + } + else if (arg is ConstructorInfo ci) + { + ilgen_real.Emit(opcode, ci); + } + else if (arg is FieldInfo fi) + { + ilgen_real.Emit(opcode, fi); + } + else if (arg is sbyte sby) + { + ilgen_real.Emit(opcode, sby); + } + else if (arg is byte by) + { + ilgen_real.Emit(opcode, by); + } + else if (arg is short sh) + { + ilgen_real.Emit(opcode, sh); + } + else if (arg is float f) + { + ilgen_real.Emit(opcode, f); + } + else if (arg is double d) + { + ilgen_real.Emit(opcode, d); + } + else if (arg is string s) + { + ilgen_real.Emit(opcode, s); + } + else if (arg is Type type) + { + ilgen_real.Emit(opcode, type); + } + else if (arg is CodeEmitterLocal local) + { + local.Emit(ilgen_real, opcode); + } + else if (arg is CodeEmitterLabel label) + { + ilgen_real.Emit(opcode, label.Label); + } + else if (arg is CodeEmitterLabel[] labels) + { + var real = new Label[labels.Length]; + for (int i = 0; i < labels.Length; i++) + real[i] = labels[i].Label; + + ilgen_real.Emit(opcode, real); + } + else if (arg is ManagedCalliWrapper margs) + { + ilgen_real.EmitCalli(opcode, margs.callConv, margs.returnType, margs.parameterTypes, margs.optionalParameterTypes); + } + else if (arg is CalliWrapper args) + { + ilgen_real.EmitCalli(opcode, args.unmanagedCallConv, args.returnType, args.parameterTypes); + } + else + { + throw new InvalidOperationException(); + } + } + + void RemoveJumpNext() + { + for (int i = 1; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + { + if (code[i - 1].opcode == OpCodes.Br + && code[i - 1].MatchLabel(code[i])) + { + code.RemoveAt(i - 1); + i--; + } + else if (i >= 2 + && code[i - 1].pseudo == CodeType.LineNumber + && code[i - 2].opcode == OpCodes.Br + && code[i - 2].MatchLabel(code[i])) + { + code.RemoveAt(i - 2); + i--; + } + } + } + } + + void AnnihilateStoreReleaseTempLocals() + { + for (int i = 1; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Stloc) + { + if (code[i + 1].pseudo == CodeType.ReleaseTempLocal && code[i].Local == code[i + 1].Local) + { + code[i] = new OpCodeWrapper(OpCodes.Pop, null); + } + else if (code[i + 1].opcode == OpCodes.Ldloc && code[i + 1].Local == code[i].Local && code[i + 2].pseudo == CodeType.ReleaseTempLocal && code[i + 2].Local == code[i].Local) + { + code.RemoveRange(i, 2); + } + } + } + } + + void AnnihilatePops() + { + for (int i = 1; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Pop) + { + // search backwards for a candidate push to annihilate + int stack = 0; + for (int j = i - 1; j >= 0; j--) + { + if (IsSideEffectFreePush(j)) + { + if (stack == 0) + { + code.RemoveAt(i); + code.RemoveAt(j); + i -= 2; + break; + } + stack++; + } + else if (code[j].opcode == OpCodes.Stloc) + { + stack--; + } + else if (code[j].opcode == OpCodes.Shl || code[j].opcode == OpCodes.And || code[j].opcode == OpCodes.Add || code[j].opcode == OpCodes.Sub) + { + if (stack == 0) + break; + + stack--; + } + else if (code[j].opcode == OpCodes.Conv_Ovf_I4 + || code[j].opcode == OpCodes.Conv_I8 + || code[j].opcode == OpCodes.Ldlen) + { + if (stack == 0) + { + break; + } + // no stack effect + } + else + { + break; + } + } + } + } + } + + /// + /// Returns true if the instruction at the given index leaves no side-effects. + /// + /// + /// + bool IsSideEffectFreePush(int index) + { + if (code[index].opcode == OpCodes.Ldstr) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldnull) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldsfld) + { + var field = code[index].FieldInfo; + if (field != null) + { + // Here we are considering BeforeFieldInit to mean that we really don't care about + // when the type is initialized (which is what we mean in the rest of the IKVM code as well) + // but it is good to point it out here because strictly speaking we're violating the + // BeforeFieldInit contract here by considering dummy loads not to be field accesses. + if ((field.DeclaringType.Attributes & TypeAttributes.BeforeFieldInit) != 0) + { + return true; + } + // If we're accessing a field in the current type, it can't trigger the static initializer + // (unless beforefieldinit is set, but see above for that scenario) + if (field.DeclaringType == declaringType) + { + return true; + } + } + return false; + } + else if (code[index].opcode == OpCodes.Ldc_I4) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldc_I8) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldc_R4) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldc_R8) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldloc) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg_S) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg_0) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg_1) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg_2) + { + return true; + } + else if (code[index].opcode == OpCodes.Ldarg_3) + { + return true; + } + else + { + return false; + } + } + + void OptimizeBranchSizes() + { + var offset = 0; + for (var i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + code[i].Label.Temp = offset; + + offset += code[i].Size; + } + + offset = 0; + for (var i = 0; i < code.Count; i++) + { + var prevOffset = offset; + offset += code[i].Size; + if (code[i].HasLabel && code[i].opcode.OperandType == OperandType.InlineBrTarget) + { + var label = code[i].Label; + var diff = label.Temp - (prevOffset + code[i].opcode.Size + 1); + if (-128 <= diff && diff <= 127) + { + var opcode = code[i].opcode; + if (opcode == OpCodes.Brtrue) + { + opcode = OpCodes.Brtrue_S; + } + else if (opcode == OpCodes.Brfalse) + { + opcode = OpCodes.Brfalse_S; + } + else if (opcode == OpCodes.Br) + { + opcode = OpCodes.Br_S; + } + else if (opcode == OpCodes.Beq) + { + opcode = OpCodes.Beq_S; + } + else if (opcode == OpCodes.Bne_Un) + { + opcode = OpCodes.Bne_Un_S; + } + else if (opcode == OpCodes.Ble) + { + opcode = OpCodes.Ble_S; + } + else if (opcode == OpCodes.Ble_Un) + { + opcode = OpCodes.Ble_Un_S; + } + else if (opcode == OpCodes.Blt) + { + opcode = OpCodes.Blt_S; + } + else if (opcode == OpCodes.Blt_Un) + { + opcode = OpCodes.Blt_Un_S; + } + else if (opcode == OpCodes.Bge) + { + opcode = OpCodes.Bge_S; + } + else if (opcode == OpCodes.Bge_Un) + { + opcode = OpCodes.Bge_Un_S; + } + else if (opcode == OpCodes.Bgt) + { + opcode = OpCodes.Bgt_S; + } + else if (opcode == OpCodes.Bgt_Un) + { + opcode = OpCodes.Bgt_Un_S; + } + else if (opcode == OpCodes.Leave) + { + opcode = OpCodes.Leave_S; + } + + code[i] = new OpCodeWrapper(opcode, label); + } + } + } + } + + void OptimizePatterns() + { + SetLabelRefCounts(); + for (int i = 1; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Isinst + && code[i + 1].opcode == OpCodes.Ldnull + && code[i + 2].opcode == OpCodes.Cgt_Un + && (code[i + 3].opcode == OpCodes.Brfalse || code[i + 3].opcode == OpCodes.Brtrue)) + { + code.RemoveRange(i + 1, 2); + } + else if (code[i].opcode == OpCodes.Ldelem_I1 + && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 255 + && code[i + 2].opcode == OpCodes.And) + { + code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); + code.RemoveRange(i + 1, 2); + } + else if (code[i].opcode == OpCodes.Ldelem_I1 + && code[i + 1].opcode == OpCodes.Conv_I8 + && code[i + 2].opcode == OpCodes.Ldc_I8 && code[i + 2].ValueInt64 == 255 + && code[i + 3].opcode == OpCodes.And) + { + code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); + code.RemoveRange(i + 2, 2); + } + else if (code[i].opcode == OpCodes.Ldc_I4 + && code[i + 1].opcode == OpCodes.Ldc_I4 + && code[i + 2].opcode == OpCodes.And) + { + code[i] = new OpCodeWrapper(OpCodes.Ldc_I4, code[i].ValueInt32 & code[i + 1].ValueInt32); + code.RemoveRange(i + 1, 2); + } + else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Double) // dcmpl + || MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Single)) // fcmpl + { + PatchCompare(i, OpCodes.Ble_Un, OpCodes.Blt_Un, OpCodes.Bge, OpCodes.Bgt); + } + else if (MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Double) // dcmpg + || MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Single)) // fcmpg + { + PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge_Un, OpCodes.Bgt_Un); + } + else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt, Types.Int64)) // lcmp + { + PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge, OpCodes.Bgt); + } + else if (i < code.Count - 10 + && code[i].opcode == OpCodes.Ldc_I4 + && code[i + 1].opcode == OpCodes.Dup + && code[i + 2].opcode == OpCodes.Ldc_I4_M1 + && code[i + 3].opcode == OpCodes.Bne_Un + && code[i + 4].opcode == OpCodes.Pop + && code[i + 5].opcode == OpCodes.Neg + && code[i + 6].opcode == OpCodes.Br + && code[i + 7].pseudo == CodeType.Label && code[i + 7].MatchLabel(code[i + 3]) && code[i + 7].Label.Temp == 1 + && code[i + 8].opcode == OpCodes.Div + && code[i + 9].pseudo == CodeType.Label && code[i + 9].Label == code[i + 6].Label && code[i + 9].Label.Temp == 1) + { + int divisor = code[i].ValueInt32; + if (divisor == -1) + { + code[i] = code[i + 5]; + code.RemoveRange(i + 1, 9); + } + else + { + code[i + 1] = code[i + 8]; + code.RemoveRange(i + 2, 8); + } + } + else if (i < code.Count - 11 + && code[i].opcode == OpCodes.Ldc_I8 + && code[i + 1].opcode == OpCodes.Dup + && code[i + 2].opcode == OpCodes.Ldc_I4_M1 + && code[i + 3].opcode == OpCodes.Conv_I8 + && code[i + 4].opcode == OpCodes.Bne_Un + && code[i + 5].opcode == OpCodes.Pop + && code[i + 6].opcode == OpCodes.Neg + && code[i + 7].opcode == OpCodes.Br + && code[i + 8].pseudo == CodeType.Label && code[i + 8].MatchLabel(code[i + 4]) && code[i + 8].Label.Temp == 1 + && code[i + 9].opcode == OpCodes.Div + && code[i + 10].pseudo == CodeType.Label && code[i + 10].MatchLabel(code[i + 7]) && code[i + 10].Label.Temp == 1) + { + long divisor = code[i].ValueInt64; + if (divisor == -1) + { + code[i] = code[i + 6]; + code.RemoveRange(i + 1, 10); + } + else + { + code[i + 1] = code[i + 9]; + code.RemoveRange(i + 2, 9); + } + } + else if (code[i].opcode == OpCodes.Box + && code[i + 1].opcode == OpCodes.Unbox && code[i + 1].Type == code[i].Type) + { + CodeEmitterLocal local = new CodeEmitterLocal(code[i].Type); + code[i] = new OpCodeWrapper(OpCodes.Stloc, local); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldloca, local); + } + else if (i < code.Count - 13 + && code[i + 0].opcode == OpCodes.Box + && code[i + 1].opcode == OpCodes.Dup + && code[i + 2].opcode == OpCodes.Brtrue + && code[i + 3].opcode == OpCodes.Pop + && code[i + 4].opcode == OpCodes.Ldloca && code[i + 4].Local.LocalType == code[i + 0].Type + && code[i + 5].opcode == OpCodes.Initobj && code[i + 5].Type == code[i + 0].Type + && code[i + 6].opcode == OpCodes.Ldloc && code[i + 6].Local == code[i + 4].Local + && code[i + 7].pseudo == CodeType.ReleaseTempLocal && code[i + 7].Local == code[i + 6].Local + && code[i + 8].opcode == OpCodes.Br + && code[i + 9].pseudo == CodeType.Label && code[i + 9].MatchLabel(code[i + 2]) && code[i + 9].Label.Temp == 1 + && code[i + 10].opcode == OpCodes.Unbox && code[i + 10].Type == code[i + 0].Type + && code[i + 11].opcode == OpCodes.Ldobj && code[i + 11].Type == code[i + 0].Type + && code[i + 12].pseudo == CodeType.Label && code[i + 12].MatchLabel(code[i + 8]) && code[i + 12].Label.Temp == 1) + { + code.RemoveRange(i, 13); + } + + // NOTE intentionally not an else, because we want to optimize the code generated by the earlier compare optimization + if (i < code.Count - 6 + && code[i].opcode.FlowControl == FlowControl.Cond_Branch + && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 1 + && code[i + 2].opcode == OpCodes.Br + && code[i + 3].pseudo == CodeType.Label && code[i + 3].MatchLabel(code[i]) && code[i + 3].Label.Temp == 1 + && code[i + 4].opcode == OpCodes.Ldc_I4 && code[i + 4].ValueInt32 == 0 + && code[i + 5].pseudo == CodeType.Label && code[i + 5].MatchLabel(code[i + 2]) && code[i + 5].Label.Temp == 1) + { + if (code[i].opcode == OpCodes.Bne_Un) + { + code[i] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 1, 5); + } + else if (code[i].opcode == OpCodes.Beq) + { + code[i + 0] = new OpCodeWrapper(OpCodes.Ceq, null); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); + code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 3, 3); + } + else if (code[i].opcode == OpCodes.Ble || code[i].opcode == OpCodes.Ble_Un) + { + code[i] = new OpCodeWrapper(OpCodes.Cgt, null); + code.RemoveRange(i + 1, 5); + } + else if (code[i].opcode == OpCodes.Blt) + { + code[i] = new OpCodeWrapper(OpCodes.Clt, null); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); + code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 3, 3); + } + else if (code[i].opcode == OpCodes.Blt_Un) + { + code[i] = new OpCodeWrapper(OpCodes.Clt_Un, null); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); + code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 3, 3); + } + else if (code[i].opcode == OpCodes.Bge || code[i].opcode == OpCodes.Bge_Un) + { + code[i] = new OpCodeWrapper(OpCodes.Clt, null); + code.RemoveRange(i + 1, 5); + } + else if (code[i].opcode == OpCodes.Bgt) + { + code[i] = new OpCodeWrapper(OpCodes.Cgt, null); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); + code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 3, 3); + } + else if (code[i].opcode == OpCodes.Bgt_Un) + { + code[i] = new OpCodeWrapper(OpCodes.Cgt_Un, null); + code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); + code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); + code.RemoveRange(i + 3, 3); + } + } + } + } + + bool MatchCompare(int index, OpCode cmp1, OpCode cmp2, Type type) + { + return code[index].opcode == OpCodes.Stloc && code[index].Local.LocalType == type + && code[index + 1].opcode == OpCodes.Stloc && code[index + 1].Local.LocalType == type + && code[index + 2].opcode == OpCodes.Ldloc && code[index + 2].MatchLocal(code[index + 1]) + && code[index + 3].opcode == OpCodes.Ldloc && code[index + 3].MatchLocal(code[index]) + && code[index + 4].opcode == cmp1 + && code[index + 5].opcode == OpCodes.Ldloc && code[index + 5].MatchLocal(code[index + 1]) + && code[index + 6].opcode == OpCodes.Ldloc && code[index + 6].MatchLocal(code[index]) + && code[index + 7].opcode == cmp2 + && code[index + 8].opcode == OpCodes.Sub + && code[index + 9].pseudo == CodeType.ReleaseTempLocal && code[index + 9].Local == code[index].Local + && code[index + 10].pseudo == CodeType.ReleaseTempLocal && code[index + 10].Local == code[index + 1].Local + && ((code[index + 11].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 11].HasLabel) || + (code[index + 11].opcode == OpCodes.Ldc_I4_0 + && (code[index + 12].opcode.FlowControl == FlowControl.Cond_Branch && code[index + 12].HasLabel))); + } + + void PatchCompare(int index, OpCode ble, OpCode blt, OpCode bge, OpCode bgt) + { + if (code[index + 11].opcode == OpCodes.Brtrue) + { + code[index] = new OpCodeWrapper(OpCodes.Bne_Un, code[index + 11].Label); + code.RemoveRange(index + 1, 11); + } + else if (code[index + 11].opcode == OpCodes.Brfalse) + { + code[index] = new OpCodeWrapper(OpCodes.Beq, code[index + 11].Label); + code.RemoveRange(index + 1, 11); + } + else if (code[index + 11].opcode == OpCodes.Ldc_I4_0) + { + if (code[index + 12].opcode == OpCodes.Ble) + { + code[index] = new OpCodeWrapper(ble, code[index + 12].Label); + code.RemoveRange(index + 1, 12); + } + else if (code[index + 12].opcode == OpCodes.Blt) + { + code[index] = new OpCodeWrapper(blt, code[index + 12].Label); + code.RemoveRange(index + 1, 12); + } + else if (code[index + 12].opcode == OpCodes.Bge) + { + code[index] = new OpCodeWrapper(bge, code[index + 12].Label); + code.RemoveRange(index + 1, 12); + } + else if (code[index + 12].opcode == OpCodes.Bgt) + { + code[index] = new OpCodeWrapper(bgt, code[index + 12].Label); + code.RemoveRange(index + 1, 12); + } + } + } + + /// + /// Lowers various instructions to their constant forms. + /// + void OptimizeEncodings() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Ldc_I4) + code[i] = OptimizeLdcI4(code[i].ValueInt32); + else if (code[i].opcode == OpCodes.Ldc_I8) + OptimizeLdcI8(i); + } + } + + /// + /// Replaces instances of ldc.i4 with constant versions base on the data value. + /// + /// + /// + OpCodeWrapper OptimizeLdcI4(int value) + { + switch (value) + { + case -1: + return new OpCodeWrapper(OpCodes.Ldc_I4_M1, null); + case 0: + return new OpCodeWrapper(OpCodes.Ldc_I4_0, null); + case 1: + return new OpCodeWrapper(OpCodes.Ldc_I4_1, null); + case 2: + return new OpCodeWrapper(OpCodes.Ldc_I4_2, null); + case 3: + return new OpCodeWrapper(OpCodes.Ldc_I4_3, null); + case 4: + return new OpCodeWrapper(OpCodes.Ldc_I4_4, null); + case 5: + return new OpCodeWrapper(OpCodes.Ldc_I4_5, null); + case 6: + return new OpCodeWrapper(OpCodes.Ldc_I4_6, null); + case 7: + return new OpCodeWrapper(OpCodes.Ldc_I4_7, null); + case 8: + return new OpCodeWrapper(OpCodes.Ldc_I4_8, null); + default: + if (value >= -128 && value <= 127) + return new OpCodeWrapper(OpCodes.Ldc_I4_S, (sbyte)value); + else + return new OpCodeWrapper(OpCodes.Ldc_I4, value); + } + } + + /// + /// Replaces instances of ldc.i8 with ldc.i4 based on the range of the constant data. + /// + /// + void OptimizeLdcI8(int index) + { + var value = code[index].ValueInt64; + if (value >= int.MinValue && value <= uint.MaxValue) + { + code[index] = OptimizeLdcI4((int)value); + code.Insert(index + 1, new OpCodeWrapper(value < 0 ? OpCodes.Conv_I8 : OpCodes.Conv_U8, null)); + } + } + + private void ChaseBranches() + { + /* * Here we do a couple of different optimizations to unconditional branches: * - a branch to a ret or endfinally will be replaced * by the ret or endfinally instruction (because that is always at least as efficient) * - a branch to a branch will remove the indirection * - a leave to a branch or leave will remove the indirection */ - SetLabelIndexes(); - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Br) - { - int target = code[i].Label.Temp + 1; - if (code[target].pseudo == CodeType.LineNumber) - { - // line number info on endfinally or ret is probably useless anyway - target++; - } - if (code[target].opcode == OpCodes.Endfinally || code[target].opcode == OpCodes.Ret) - { - code[i] = code[target]; - } - else - { - CodeEmitterLabel label = null; - while (code[target].opcode == OpCodes.Br && target != i) - { - label = code[target].Label; - target = code[target].Label.Temp + 1; - } - if (label != null) - { - code[i] = new OpCodeWrapper(OpCodes.Br, label); - } - } - } - else if (code[i].opcode == OpCodes.Leave) - { - int target = code[i].Label.Temp + 1; - CodeEmitterLabel label = null; - while ((code[target].opcode == OpCodes.Br || code[target].opcode == OpCodes.Leave) && target != i) - { - label = code[target].Label; - target = code[target].Label.Temp + 1; - } - if (label != null) - { - code[i] = new OpCodeWrapper(OpCodes.Leave, label); - } - } - } - } - - private void RemoveSingletonBranches() - { - /* + SetLabelIndexes(); + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Br) + { + int target = code[i].Label.Temp + 1; + if (code[target].pseudo == CodeType.LineNumber) + { + // line number info on endfinally or ret is probably useless anyway + target++; + } + if (code[target].opcode == OpCodes.Endfinally || code[target].opcode == OpCodes.Ret) + { + code[i] = code[target]; + } + else + { + CodeEmitterLabel label = null; + while (code[target].opcode == OpCodes.Br && target != i) + { + label = code[target].Label; + target = code[target].Label.Temp + 1; + } + if (label != null) + { + code[i] = new OpCodeWrapper(OpCodes.Br, label); + } + } + } + else if (code[i].opcode == OpCodes.Leave) + { + int target = code[i].Label.Temp + 1; + CodeEmitterLabel label = null; + while ((code[target].opcode == OpCodes.Br || code[target].opcode == OpCodes.Leave) && target != i) + { + label = code[target].Label; + target = code[target].Label.Temp + 1; + } + if (label != null) + { + code[i] = new OpCodeWrapper(OpCodes.Leave, label); + } + } + } + } + + private void RemoveSingletonBranches() + { + /* * Here we try to remove unconditional branches that jump to a label with ref count of one * and where the code is not otherwise used. */ - SetLabelRefCounts(); - // now increment label refcounts for labels that are also reachable via the preceding instruction - bool reachable = true; - for (int i = 0; i < code.Count; i++) - { - if (reachable) - { - switch (code[i].pseudo) - { - case CodeType.Label: - code[i].Label.Temp++; - break; - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - case CodeType.EndExceptionBlock: - throw new InvalidOperationException(); - case CodeType.OpCode: - switch (code[i].opcode.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Return: - case FlowControl.Throw: - reachable = false; - break; - } - break; - } - } - else - { - switch (code[i].pseudo) - { - case CodeType.Label: - reachable = code[i].Label.Temp > 0; - break; - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - reachable = true; - break; - } - } - } - - // now remove the unconditional branches to labels with a refcount of one - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Br && code[i].Label.Temp == 1) - { - int target = FindLabel(code[i].Label) + 1; - for (int j = target; j < code.Count; j++) - { - switch (code[j].pseudo) - { - case CodeType.OpCode: - if (code[j].HasLocal && FindLocal(code[j].Local) > i) - { - // we cannot local variable usage before the declaration - goto breakOuter; - } - switch (code[j].opcode.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Return: - case FlowControl.Throw: - // we've found a viable sequence of opcode to move to the branch location - List range = code.GetRange(target, j - target + 1); - if (target < i) - { - code.RemoveAt(i); - code.InsertRange(i, range); - code.RemoveRange(target - 1, range.Count + 1); - i -= range.Count + 1; - } - else - { - code.RemoveRange(target - 1, range.Count + 1); - code.RemoveAt(i); - code.InsertRange(i, range); - } - goto breakOuter; - } - break; - case CodeType.Label: - case CodeType.BeginExceptionBlock: - case CodeType.DeclareLocal: - goto breakOuter; - } - } - breakOuter: ; - } - } - } - - private int FindLabel(CodeEmitterLabel label) - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label && code[i].Label == label) - { - return i; - } - } - throw new InvalidOperationException(); - } - - private int FindLocal(CodeEmitterLocal local) - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.DeclareLocal && code[i].Local == local) - { - return i; - } - } - // if the local variable isn't declared, it is a temporary that is allocated on demand - // (so we can move their usage freely) - return 0; - } - - private void SortPseudoOpCodes() - { - for (int i = 0; i < code.Count - 1; i++) - { - switch (code[i].pseudo) - { - case CodeType.ReleaseTempLocal: - for (int j = i - 1; ; j--) - { - if (j == -1) - { - code.RemoveAt(i); - break; - } - if (code[j].HasLocal && code[j].Local == code[i].Local) - { - MoveInstruction(i, j + 1); - break; - } - } - break; - } - } - } - - private void MoveInstruction(int i, int j) - { - if (i == j - 1 || i == j + 1) - { - OpCodeWrapper temp = code[i]; - code[i] = code[j]; - code[j] = temp; - } - else if (i < j) - { - code.Insert(j, code[i]); - code.RemoveAt(i); - } - else if (i > j) - { - OpCodeWrapper temp = code[i]; - code.RemoveAt(i); - code.Insert(j, temp); - } - } - - private void ClearLabelTemp() - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label) - { - code[i].Label.Temp = 0; - } - } - } - - private void SetLabelIndexes() - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label) - { - code[i].Label.Temp = i; - } - } - } - - private void SetLabelRefCounts() - { - ClearLabelTemp(); - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.OpCode) - { - if (code[i].HasLabel) - { - code[i].Label.Temp++; - } - else if (code[i].opcode == OpCodes.Switch) - { - foreach (CodeEmitterLabel label in code[i].Labels) - { - label.Temp++; - } - } - } - } - } - - private void RemoveUnusedLabels() - { - SetLabelRefCounts(); - for (int i = 0; i < code.Count; i++) - { - while (code[i].pseudo == CodeType.Label && code[i].Label.Temp == 0) - { - code.RemoveAt(i); - } - } - } - - private void RemoveDeadCode() - { - ClearLabelTemp(); - const int ReachableFlag = 1; - const int ProcessedFlag = 2; - bool reachable = true; - bool done = false; - while (!done) - { - done = true; - for (int i = 0; i < code.Count; i++) - { - if (reachable) - { - if (code[i].pseudo == CodeType.Label) - { - if (code[i].Label.Temp == ProcessedFlag) - { - done = false; - } - code[i].Label.Temp |= ReachableFlag; - } - else if (code[i].pseudo == CodeType.OpCode) - { - if (code[i].HasLabel) - { - if (code[i].Label.Temp == ProcessedFlag) - { - done = false; - } - code[i].Label.Temp |= ReachableFlag; - } - else if (code[i].opcode == OpCodes.Switch) - { - foreach (CodeEmitterLabel label in code[i].Labels) - { - if (label.Temp == ProcessedFlag) - { - done = false; - } - label.Temp |= ReachableFlag; - } - } - switch (code[i].opcode.FlowControl) - { - case FlowControl.Cond_Branch: - if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch) - { - throw new NotSupportedException(); - } - break; - case FlowControl.Branch: - case FlowControl.Return: - case FlowControl.Throw: - reachable = false; - break; - } - } - } - else if (code[i].pseudo == CodeType.BeginCatchBlock) - { - reachable = true; - } - else if (code[i].pseudo == CodeType.BeginFaultBlock) - { - reachable = true; - } - else if (code[i].pseudo == CodeType.BeginFinallyBlock) - { - reachable = true; - } - else if (code[i].pseudo == CodeType.Label && (code[i].Label.Temp & ReachableFlag) != 0) - { - reachable = true; - } - if (code[i].pseudo == CodeType.Label) - { - code[i].Label.Temp |= ProcessedFlag; - } - } - } - reachable = true; - int firstUnreachable = -1; - for (int i = 0; i < code.Count; i++) - { - if (reachable) - { - if (code[i].pseudo == CodeType.OpCode) - { - switch (code[i].opcode.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Return: - case FlowControl.Throw: - reachable = false; - firstUnreachable = i + 1; - break; - } - } - } - else - { - switch (code[i].pseudo) - { - case CodeType.OpCode: - break; - case CodeType.Label: - if ((code[i].Label.Temp & ReachableFlag) != 0) - { - goto case CodeType.BeginCatchBlock; - } - break; - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - code.RemoveRange(firstUnreachable, i - firstUnreachable); - i = firstUnreachable; - firstUnreachable = -1; - reachable = true; - break; - default: - code.RemoveRange(firstUnreachable, i - firstUnreachable); - i = firstUnreachable; - firstUnreachable++; - break; - } - } - } - if (!reachable) - { - code.RemoveRange(firstUnreachable, code.Count - firstUnreachable); - } - - // TODO can't we incorporate this in the above code? - // remove exception blocks with empty try blocks - // (which can happen if the try block is unreachable) - for (int i = 0; i < code.Count; i++) - { - restart: - if (code[i].pseudo == CodeType.BeginExceptionBlock) - { - for (int k = 0; ; k++) - { - switch (code[i + k].pseudo) - { - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - int depth = 0; - for (int j = i + 1; ; j++) - { - switch (code[j].pseudo) - { - case CodeType.BeginExceptionBlock: - depth++; - break; - case CodeType.EndExceptionBlock: - if (depth == 0) - { - code.RemoveRange(i, (j - i) + 1); - goto restart; - } - depth--; - break; - } - } - case CodeType.OpCode: - goto next; - } - } - } - next: ; - } - } - - private void DeduplicateBranchSourceTargetCode() - { - SetLabelIndexes(); - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Br && code[i].HasLabel) - { - int source = i - 1; - int target = code[i].Label.Temp - 1; - while (source >= 0 && target >= 0) - { - switch (code[source].pseudo) - { - case CodeType.LineNumber: - case CodeType.OpCode: - break; - default: - goto break_while; - } - if (!code[source].Match(code[target])) - { - break; - } - switch (code[source].opcode.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Cond_Branch: - goto break_while; - } - source--; - target--; - } - break_while: ; - source++; - target++; - if (source != i && target > 0 && source != target - 1) - { - // TODO for now we only do this optimization if there happens to be an appriopriate label - if (code[target - 1].pseudo == CodeType.Label) - { - code[source] = new OpCodeWrapper(OpCodes.Br, code[target - 1].Label); - for (int j = source + 1; j <= i; j++) - { - // We can't depend on DCE for code correctness (we have to maintain all MSIL invariants at all times), - // so we patch out the unused code. - code[j] = new OpCodeWrapper(CodeType.Unreachable, null); - } - } - } - } - } - } - - private void OptimizeStackTransfer() - { - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Ldloc - && code[i + 1].opcode == OpCodes.Stloc - && code[i + 2].pseudo == CodeType.BeginExceptionBlock - && code[i + 3].opcode == OpCodes.Ldloc && code[i + 3].MatchLocal(code[i + 1]) - && code[i + 4].pseudo == CodeType.ReleaseTempLocal && code[i + 4].MatchLocal(code[i + 3])) - { - code[i + 1] = code[i]; - code[i] = code[i + 2]; - code.RemoveRange(i + 2, 3); - } - } - } - - private void MergeExceptionBlocks() - { - // The first loop will convert all Begin[Exception|Catch|Fault|Finally]Block and EndExceptionBlock - // pseudo opcodes into a cyclic linked list (EndExceptionBlock links back to BeginExceptionBlock) - // to allow for easy traversal in the next loop. - int[] extra = new int[code.Count]; - Stack stack = new Stack(); - int currentBeginExceptionBlock = -1; - int currentLast = -1; - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.BeginExceptionBlock: - stack.Push(currentBeginExceptionBlock); - currentBeginExceptionBlock = i; - currentLast = i; - break; - case CodeType.EndExceptionBlock: - extra[currentLast] = i; - extra[i] = currentBeginExceptionBlock; - currentBeginExceptionBlock = stack.Pop(); - currentLast = currentBeginExceptionBlock; - if (currentLast != -1) - { - while (extra[currentLast] != 0) - { - currentLast = extra[currentLast]; - } - } - break; - case CodeType.BeginCatchBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginFinallyBlock: - extra[currentLast] = i; - currentLast = i; - break; - } - } - - // Now we look for consecutive exception blocks that have the same fault handler - for (int i = 0; i < code.Count - 1; i++) - { - if (code[i].pseudo == CodeType.EndExceptionBlock - && code[i + 1].pseudo == CodeType.BeginExceptionBlock) - { - if (IsFaultOnlyBlock(extra, extra[i]) && IsFaultOnlyBlock(extra, i + 1)) - { - int beginFault1 = extra[extra[i]]; - int beginFault2 = extra[i + 1]; - int length1 = extra[beginFault1] - beginFault1; - int length2 = extra[beginFault2] - beginFault2; - if (length1 == length2 && MatchHandlers(beginFault1, beginFault2, length1)) - { - // Check if the labels at the start of the handler are reachable from outside - // of the new combined block. - for (int j = i + 2; j < beginFault2; j++) - { - if (code[j].pseudo == CodeType.OpCode) - { - break; - } - else if (code[j].pseudo == CodeType.Label) - { - if (HasBranchTo(0, extra[i], code[j].Label) - || HasBranchTo(beginFault2 + length2, code.Count, code[j].Label)) - { - goto no_merge; - } - } - } - // Merge the two blocks by overwritting the first fault block and - // the BeginExceptionBlock of the second block. - for (int j = beginFault1; j < i + 2; j++) - { - code[j] = new OpCodeWrapper(OpCodes.Nop, null); - } - // Repair the linking structure. - extra[extra[i]] = beginFault2; - extra[extra[beginFault2]] = extra[i]; - } - } - no_merge: ; - } - } - } - - private bool HasBranchTo(int start, int end, CodeEmitterLabel label) - { - for (int i = start; i < end; i++) - { - if (code[i].HasLabel) - { - if (code[i].Label == label) - { - return true; - } - } - else if (code[i].opcode == OpCodes.Switch) - { - foreach (CodeEmitterLabel swlbl in code[i].Labels) - { - if (swlbl == label) - { - return true; - } - } - } - } - return false; - } - - private bool MatchHandlers(int beginFault1, int beginFault2, int length) - { - for (int i = 0; i < length; i++) - { - if (!code[beginFault1 + i].Match(code[beginFault2 + i])) - { - return false; - } - } - return true; - } - - private bool IsFaultOnlyBlock(int[] extra, int begin) - { - return code[extra[begin]].pseudo == CodeType.BeginFaultBlock - && code[extra[extra[begin]]].pseudo == CodeType.EndExceptionBlock; - } - - private void ConvertSynchronizedFaultToFinally() - { - bool labelIndexSet = false; - int start = -1; - int nest = 0; - int next = -1; - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.BeginExceptionBlock: - if (nest == 0) - { - start = i; - } - else if (nest == 1 && next <= start) - { - next = i; - } - nest++; - break; - case CodeType.BeginCatchBlock: - case CodeType.BeginFinallyBlock: - if (nest == 1) - { - nest = 0; - if (next > start) - { - // while we were processing the outer block, we encountered a nested BeginExceptionBlock - // so now that we've failed the outer, restart at the first nested block - i = start = next; - nest = 1; - } - } - else - { - next = -1; - } - break; - case CodeType.BeginFaultBlock: - if (nest == 1) - { - int beginFault = i; - if (code[i + 1].pseudo == CodeType.LineNumber) - { - i++; - } - // check if the fault handler is the synchronized block exit pattern - if (code[i + 1].opcode == OpCodes.Ldloc - && code[i + 2].pseudo == CodeType.MonitorExit - && code[i + 3].opcode == OpCodes.Endfinally) - { - if (!labelIndexSet) - { - labelIndexSet = true; - SetLabelIndexes(); - } - // now make two passes through the try block to 1) see if all leave - // opcodes that leave the try block do a synchronized block exit - // and 2) patch out the synchronized block exit - for (int pass = 0; pass < 2; pass++) - { - for (int j = start; j < i; j++) - { - if (code[j].opcode == OpCodes.Leave) - { - int target = code[j].Label.Temp; - if (target < start || target > i) - { - // check if the code preceding the leave matches the fault block - if ((code[j - 1].opcode == OpCodes.Pop || code[j - 1].opcode == OpCodes.Stloc) - && code[j - 2].pseudo == CodeType.MonitorExit - && code[j - 3].Match(code[i + 1])) - { - if (pass == 1) - { - // move the leave to the top of the sequence we're removing - code[j - 3] = code[j - 1]; - code[j - 2] = code[j - 0]; - code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); - code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); - } - } - else if (code[j - 1].pseudo == CodeType.MonitorExit - && code[j - 2].Match(code[i + 1])) - { - if (pass == 1) - { - // move the leave to the top of the sequence we're removing - code[j - 2] = code[j]; - code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); - code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); - } - } - else - { - goto fail; - } - } - } - } - } - // if we end up here, all leaves have been successfully patched, - // so now we turn the BeginFaultBlock into a BeginFinallyBlock - code[beginFault] = new OpCodeWrapper(CodeType.BeginFinallyBlock, CodeTypeFlags.None); - fail: ; - } - goto case CodeType.BeginFinallyBlock; - } - break; - case CodeType.EndExceptionBlock: - nest--; - break; - } - } - } - - private void RemoveRedundantMemoryBarriers() - { - int lastMemoryBarrier = -1; - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.MemoryBarrier: - if (lastMemoryBarrier != -1) - { - code.RemoveAt(lastMemoryBarrier); - i--; - } - lastMemoryBarrier = i; - break; - case CodeType.OpCode: - if (code[i].opcode == OpCodes.Volatile) - { - if (code[i + 1].opcode != OpCodes.Stfld && code[i + 1].opcode != OpCodes.Stsfld) - { - lastMemoryBarrier = -1; - } - } - else if (code[i].opcode.FlowControl != FlowControl.Next) - { - lastMemoryBarrier = -1; - } - break; - } - } - } - - private static bool MatchLdarg(OpCodeWrapper opc, out short arg) - { - if (opc.opcode == OpCodes.Ldarg) - { - arg = opc.ValueInt16; - return true; - } - else if (opc.opcode == OpCodes.Ldarg_S) - { - arg = opc.ValueByte; - return true; - } - else if (opc.opcode == OpCodes.Ldarg_0) - { - arg = 0; - return true; - } - else if (opc.opcode == OpCodes.Ldarg_1) - { - arg = 1; - return true; - } - else if (opc.opcode == OpCodes.Ldarg_2) - { - arg = 2; - return true; - } - else if (opc.opcode == OpCodes.Ldarg_3) - { - arg = 3; - return true; - } - else - { - arg = -1; - return false; - } - } - - private bool IsBranchEqNe(OpCode opcode) - { - return opcode == OpCodes.Beq - || opcode == OpCodes.Bne_Un; - } - - private void CLRv4_x64_JIT_Workaround() - { - for (int i = 0; i < code.Count - 2; i++) - { - // This is a workaround for https://connect.microsoft.com/VisualStudio/feedback/details/566946/x64-jit-optimization-bug - // - // Testing shows that the bug appears to be very specific and requires a comparison of a method argument with zero. - // For example, the problem goes away when the method argument is first assigned to a local variable and then - // the comparison (and subsequent use) is done against the local variable. - // - // This means we only have to detect these specific patterns: - // - // ldc.i8 0x0 ldarg - // ldarg ldc.i8 0x0 - // beq/bne beq/bne - // - // The workaround is to replace ldarg with ldarga/ldind.i8. Looking at the generated code by the x86 and x64 JITs - // this appears to be as efficient as the ldarg and it avoids the x64 bug. - if (code[i].opcode == OpCodes.Ldc_I8 && code[i].ValueInt64 == 0) - { - short arg; - int m; - if (i > 0 && MatchLdarg(code[i - 1], out arg) && IsBranchEqNe(code[i + 1].opcode)) - { - m = i - 1; - } - else if (MatchLdarg(code[i + 1], out arg) && IsBranchEqNe(code[i + 2].opcode)) - { - m = i + 1; - } - else - { - continue; - } - code[m] = new OpCodeWrapper(OpCodes.Ldarga, arg); - code.Insert(m + 1, new OpCodeWrapper(OpCodes.Ldind_I8, null)); - } - } - } - - [Conditional("CHECK_INVARIANTS")] - private void CheckInvariants() - { - CheckInvariantBranchInOrOutOfBlocks(); - CheckInvariantOpCodeUsage(); - CheckInvariantLocalVariables(); - } - - private void CheckInvariantBranchInOrOutOfBlocks() - { - /* + SetLabelRefCounts(); + // now increment label refcounts for labels that are also reachable via the preceding instruction + bool reachable = true; + for (int i = 0; i < code.Count; i++) + { + if (reachable) + { + switch (code[i].pseudo) + { + case CodeType.Label: + code[i].Label.Temp++; + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + case CodeType.EndExceptionBlock: + throw new InvalidOperationException(); + case CodeType.OpCode: + switch (code[i].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Return: + case FlowControl.Throw: + reachable = false; + break; + } + break; + } + } + else + { + switch (code[i].pseudo) + { + case CodeType.Label: + reachable = code[i].Label.Temp > 0; + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + reachable = true; + break; + } + } + } + + // now remove the unconditional branches to labels with a refcount of one + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Br && code[i].Label.Temp == 1) + { + int target = FindLabel(code[i].Label) + 1; + for (int j = target; j < code.Count; j++) + { + switch (code[j].pseudo) + { + case CodeType.OpCode: + if (code[j].HasLocal && FindLocal(code[j].Local) > i) + { + // we cannot local variable usage before the declaration + goto breakOuter; + } + switch (code[j].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Return: + case FlowControl.Throw: + // we've found a viable sequence of opcode to move to the branch location + List range = code.GetRange(target, j - target + 1); + if (target < i) + { + code.RemoveAt(i); + code.InsertRange(i, range); + code.RemoveRange(target - 1, range.Count + 1); + i -= range.Count + 1; + } + else + { + code.RemoveRange(target - 1, range.Count + 1); + code.RemoveAt(i); + code.InsertRange(i, range); + } + goto breakOuter; + } + break; + case CodeType.Label: + case CodeType.BeginExceptionBlock: + case CodeType.DeclareLocal: + goto breakOuter; + } + } + breakOuter:; + } + } + } + + private int FindLabel(CodeEmitterLabel label) + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label && code[i].Label == label) + { + return i; + } + } + throw new InvalidOperationException(); + } + + private int FindLocal(CodeEmitterLocal local) + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.DeclareLocal && code[i].Local == local) + { + return i; + } + } + // if the local variable isn't declared, it is a temporary that is allocated on demand + // (so we can move their usage freely) + return 0; + } + + private void SortPseudoOpCodes() + { + for (int i = 0; i < code.Count - 1; i++) + { + switch (code[i].pseudo) + { + case CodeType.ReleaseTempLocal: + for (int j = i - 1; ; j--) + { + if (j == -1) + { + code.RemoveAt(i); + break; + } + if (code[j].HasLocal && code[j].Local == code[i].Local) + { + MoveInstruction(i, j + 1); + break; + } + } + break; + } + } + } + + private void MoveInstruction(int i, int j) + { + if (i == j - 1 || i == j + 1) + { + OpCodeWrapper temp = code[i]; + code[i] = code[j]; + code[j] = temp; + } + else if (i < j) + { + code.Insert(j, code[i]); + code.RemoveAt(i); + } + else if (i > j) + { + OpCodeWrapper temp = code[i]; + code.RemoveAt(i); + code.Insert(j, temp); + } + } + + private void ClearLabelTemp() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + { + code[i].Label.Temp = 0; + } + } + } + + private void SetLabelIndexes() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + { + code[i].Label.Temp = i; + } + } + } + + private void SetLabelRefCounts() + { + ClearLabelTemp(); + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.OpCode) + { + if (code[i].HasLabel) + { + code[i].Label.Temp++; + } + else if (code[i].opcode == OpCodes.Switch) + { + foreach (CodeEmitterLabel label in code[i].Labels) + { + label.Temp++; + } + } + } + } + } + + private void RemoveUnusedLabels() + { + SetLabelRefCounts(); + for (int i = 0; i < code.Count; i++) + { + while (code[i].pseudo == CodeType.Label && code[i].Label.Temp == 0) + { + code.RemoveAt(i); + } + } + } + + private void RemoveDeadCode() + { + ClearLabelTemp(); + const int ReachableFlag = 1; + const int ProcessedFlag = 2; + bool reachable = true; + bool done = false; + while (!done) + { + done = true; + for (int i = 0; i < code.Count; i++) + { + if (reachable) + { + if (code[i].pseudo == CodeType.Label) + { + if (code[i].Label.Temp == ProcessedFlag) + { + done = false; + } + code[i].Label.Temp |= ReachableFlag; + } + else if (code[i].pseudo == CodeType.OpCode) + { + if (code[i].HasLabel) + { + if (code[i].Label.Temp == ProcessedFlag) + { + done = false; + } + code[i].Label.Temp |= ReachableFlag; + } + else if (code[i].opcode == OpCodes.Switch) + { + foreach (CodeEmitterLabel label in code[i].Labels) + { + if (label.Temp == ProcessedFlag) + { + done = false; + } + label.Temp |= ReachableFlag; + } + } + switch (code[i].opcode.FlowControl) + { + case FlowControl.Cond_Branch: + if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch) + { + throw new NotSupportedException(); + } + break; + case FlowControl.Branch: + case FlowControl.Return: + case FlowControl.Throw: + reachable = false; + break; + } + } + } + else if (code[i].pseudo == CodeType.BeginCatchBlock) + { + reachable = true; + } + else if (code[i].pseudo == CodeType.BeginFaultBlock) + { + reachable = true; + } + else if (code[i].pseudo == CodeType.BeginFinallyBlock) + { + reachable = true; + } + else if (code[i].pseudo == CodeType.Label && (code[i].Label.Temp & ReachableFlag) != 0) + { + reachable = true; + } + if (code[i].pseudo == CodeType.Label) + { + code[i].Label.Temp |= ProcessedFlag; + } + } + } + reachable = true; + int firstUnreachable = -1; + for (int i = 0; i < code.Count; i++) + { + if (reachable) + { + if (code[i].pseudo == CodeType.OpCode) + { + switch (code[i].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Return: + case FlowControl.Throw: + reachable = false; + firstUnreachable = i + 1; + break; + } + } + } + else + { + switch (code[i].pseudo) + { + case CodeType.OpCode: + break; + case CodeType.Label: + if ((code[i].Label.Temp & ReachableFlag) != 0) + { + goto case CodeType.BeginCatchBlock; + } + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + code.RemoveRange(firstUnreachable, i - firstUnreachable); + i = firstUnreachable; + firstUnreachable = -1; + reachable = true; + break; + default: + code.RemoveRange(firstUnreachable, i - firstUnreachable); + i = firstUnreachable; + firstUnreachable++; + break; + } + } + } + if (!reachable) + { + code.RemoveRange(firstUnreachable, code.Count - firstUnreachable); + } + + // TODO can't we incorporate this in the above code? + // remove exception blocks with empty try blocks + // (which can happen if the try block is unreachable) + for (int i = 0; i < code.Count; i++) + { + restart: + if (code[i].pseudo == CodeType.BeginExceptionBlock) + { + for (int k = 0; ; k++) + { + switch (code[i + k].pseudo) + { + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + int depth = 0; + for (int j = i + 1; ; j++) + { + switch (code[j].pseudo) + { + case CodeType.BeginExceptionBlock: + depth++; + break; + case CodeType.EndExceptionBlock: + if (depth == 0) + { + code.RemoveRange(i, (j - i) + 1); + goto restart; + } + depth--; + break; + } + } + case CodeType.OpCode: + goto next; + } + } + } + next:; + } + } + + private void DeduplicateBranchSourceTargetCode() + { + SetLabelIndexes(); + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Br && code[i].HasLabel) + { + int source = i - 1; + int target = code[i].Label.Temp - 1; + while (source >= 0 && target >= 0) + { + switch (code[source].pseudo) + { + case CodeType.LineNumber: + case CodeType.OpCode: + break; + default: + goto break_while; + } + if (!code[source].Match(code[target])) + { + break; + } + switch (code[source].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Cond_Branch: + goto break_while; + } + source--; + target--; + } + break_while:; + source++; + target++; + if (source != i && target > 0 && source != target - 1) + { + // TODO for now we only do this optimization if there happens to be an appriopriate label + if (code[target - 1].pseudo == CodeType.Label) + { + code[source] = new OpCodeWrapper(OpCodes.Br, code[target - 1].Label); + for (int j = source + 1; j <= i; j++) + { + // We can't depend on DCE for code correctness (we have to maintain all MSIL invariants at all times), + // so we patch out the unused code. + code[j] = new OpCodeWrapper(CodeType.Unreachable, null); + } + } + } + } + } + } + + private void OptimizeStackTransfer() + { + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Ldloc && + code[i + 1].opcode == OpCodes.Stloc && + code[i + 2].pseudo == CodeType.BeginExceptionBlock && + code[i + 3].opcode == OpCodes.Ldloc && + code[i + 3].MatchLocal(code[i + 1]) && + code[i + 4].pseudo == CodeType.ReleaseTempLocal && + code[i + 4].MatchLocal(code[i + 3])) + { + code[i + 1] = code[i]; + code[i] = code[i + 2]; + code.RemoveRange(i + 2, 3); + } + } + } + + private void MergeExceptionBlocks() + { + // The first loop will convert all Begin[Exception|Catch|Fault|Finally]Block and EndExceptionBlock + // pseudo opcodes into a cyclic linked list (EndExceptionBlock links back to BeginExceptionBlock) + // to allow for easy traversal in the next loop. + int[] extra = new int[code.Count]; + Stack stack = new Stack(); + int currentBeginExceptionBlock = -1; + int currentLast = -1; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.BeginExceptionBlock: + stack.Push(currentBeginExceptionBlock); + currentBeginExceptionBlock = i; + currentLast = i; + break; + case CodeType.EndExceptionBlock: + extra[currentLast] = i; + extra[i] = currentBeginExceptionBlock; + currentBeginExceptionBlock = stack.Pop(); + currentLast = currentBeginExceptionBlock; + if (currentLast != -1) + { + while (extra[currentLast] != 0) + { + currentLast = extra[currentLast]; + } + } + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginFinallyBlock: + extra[currentLast] = i; + currentLast = i; + break; + } + } + + // Now we look for consecutive exception blocks that have the same fault handler + for (int i = 0; i < code.Count - 1; i++) + { + if (code[i].pseudo == CodeType.EndExceptionBlock + && code[i + 1].pseudo == CodeType.BeginExceptionBlock) + { + if (IsFaultOnlyBlock(extra, extra[i]) && IsFaultOnlyBlock(extra, i + 1)) + { + int beginFault1 = extra[extra[i]]; + int beginFault2 = extra[i + 1]; + int length1 = extra[beginFault1] - beginFault1; + int length2 = extra[beginFault2] - beginFault2; + if (length1 == length2 && MatchHandlers(beginFault1, beginFault2, length1)) + { + // Check if the labels at the start of the handler are reachable from outside + // of the new combined block. + for (int j = i + 2; j < beginFault2; j++) + { + if (code[j].pseudo == CodeType.OpCode) + { + break; + } + else if (code[j].pseudo == CodeType.Label) + { + if (HasBranchTo(0, extra[i], code[j].Label) + || HasBranchTo(beginFault2 + length2, code.Count, code[j].Label)) + { + goto no_merge; + } + } + } + // Merge the two blocks by overwritting the first fault block and + // the BeginExceptionBlock of the second block. + for (int j = beginFault1; j < i + 2; j++) + { + code[j] = new OpCodeWrapper(OpCodes.Nop, null); + } + // Repair the linking structure. + extra[extra[i]] = beginFault2; + extra[extra[beginFault2]] = extra[i]; + } + } + no_merge:; + } + } + } + + /// + /// Returns true if the specified range of instructions contains a branch instruction to the specified label. + /// + /// + /// + /// + /// + bool HasBranchTo(int start, int end, CodeEmitterLabel label) + { + for (int i = start; i < end; i++) + { + if (code[i].HasLabel) + { + if (code[i].Label == label) + return true; + } + else if (code[i].opcode == OpCodes.Switch) + { + foreach (CodeEmitterLabel swlbl in code[i].Labels) + if (swlbl == label) + return true; + } + } + + return false; + } + + private bool MatchHandlers(int beginFault1, int beginFault2, int length) + { + for (int i = 0; i < length; i++) + if (!code[beginFault1 + i].Match(code[beginFault2 + i])) + return false; + + return true; + } + + private bool IsFaultOnlyBlock(int[] extra, int begin) + { + return code[extra[begin]].pseudo == CodeType.BeginFaultBlock && code[extra[extra[begin]]].pseudo == CodeType.EndExceptionBlock; + } + + private void ConvertSynchronizedFaultToFinally() + { + bool labelIndexSet = false; + int start = -1; + int nest = 0; + int next = -1; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.BeginExceptionBlock: + if (nest == 0) + { + start = i; + } + else if (nest == 1 && next <= start) + { + next = i; + } + nest++; + break; + case CodeType.BeginCatchBlock: + case CodeType.BeginFinallyBlock: + if (nest == 1) + { + nest = 0; + if (next > start) + { + // while we were processing the outer block, we encountered a nested BeginExceptionBlock + // so now that we've failed the outer, restart at the first nested block + i = start = next; + nest = 1; + } + } + else + { + next = -1; + } + break; + case CodeType.BeginFaultBlock: + if (nest == 1) + { + int beginFault = i; + if (code[i + 1].pseudo == CodeType.LineNumber) + { + i++; + } + // check if the fault handler is the synchronized block exit pattern + if (code[i + 1].opcode == OpCodes.Ldloc + && code[i + 2].pseudo == CodeType.MonitorExit + && code[i + 3].opcode == OpCodes.Endfinally) + { + if (!labelIndexSet) + { + labelIndexSet = true; + SetLabelIndexes(); + } + // now make two passes through the try block to 1) see if all leave + // opcodes that leave the try block do a synchronized block exit + // and 2) patch out the synchronized block exit + for (int pass = 0; pass < 2; pass++) + { + for (int j = start; j < i; j++) + { + if (code[j].opcode == OpCodes.Leave) + { + int target = code[j].Label.Temp; + if (target < start || target > i) + { + // check if the code preceding the leave matches the fault block + if ((code[j - 1].opcode == OpCodes.Pop || code[j - 1].opcode == OpCodes.Stloc) + && code[j - 2].pseudo == CodeType.MonitorExit + && code[j - 3].Match(code[i + 1])) + { + if (pass == 1) + { + // move the leave to the top of the sequence we're removing + code[j - 3] = code[j - 1]; + code[j - 2] = code[j - 0]; + code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); + code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); + } + } + else if (code[j - 1].pseudo == CodeType.MonitorExit + && code[j - 2].Match(code[i + 1])) + { + if (pass == 1) + { + // move the leave to the top of the sequence we're removing + code[j - 2] = code[j]; + code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); + code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); + } + } + else + { + goto fail; + } + } + } + } + } + // if we end up here, all leaves have been successfully patched, + // so now we turn the BeginFaultBlock into a BeginFinallyBlock + code[beginFault] = new OpCodeWrapper(CodeType.BeginFinallyBlock, CodeTypeFlags.None); + fail:; + } + goto case CodeType.BeginFinallyBlock; + } + break; + case CodeType.EndExceptionBlock: + nest--; + break; + } + } + } + + private void RemoveRedundantMemoryBarriers() + { + int lastMemoryBarrier = -1; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.MemoryBarrier: + if (lastMemoryBarrier != -1) + { + code.RemoveAt(lastMemoryBarrier); + i--; + } + lastMemoryBarrier = i; + break; + case CodeType.OpCode: + if (code[i].opcode == OpCodes.Volatile) + { + if (code[i + 1].opcode != OpCodes.Stfld && code[i + 1].opcode != OpCodes.Stsfld) + { + lastMemoryBarrier = -1; + } + } + else if (code[i].opcode.FlowControl != FlowControl.Next) + { + lastMemoryBarrier = -1; + } + break; + } + } + } + + private static bool MatchLdarg(OpCodeWrapper opc, out short arg) + { + if (opc.opcode == OpCodes.Ldarg) + { + arg = opc.ValueInt16; + return true; + } + else if (opc.opcode == OpCodes.Ldarg_S) + { + arg = opc.ValueByte; + return true; + } + else if (opc.opcode == OpCodes.Ldarg_0) + { + arg = 0; + return true; + } + else if (opc.opcode == OpCodes.Ldarg_1) + { + arg = 1; + return true; + } + else if (opc.opcode == OpCodes.Ldarg_2) + { + arg = 2; + return true; + } + else if (opc.opcode == OpCodes.Ldarg_3) + { + arg = 3; + return true; + } + else + { + arg = -1; + return false; + } + } + + private bool IsBranchEqNe(OpCode opcode) + { + return opcode == OpCodes.Beq + || opcode == OpCodes.Bne_Un; + } + + private void CLRv4_x64_JIT_Workaround() + { + for (int i = 0; i < code.Count - 2; i++) + { + // This is a workaround for https://connect.microsoft.com/VisualStudio/feedback/details/566946/x64-jit-optimization-bug + // + // Testing shows that the bug appears to be very specific and requires a comparison of a method argument with zero. + // For example, the problem goes away when the method argument is first assigned to a local variable and then + // the comparison (and subsequent use) is done against the local variable. + // + // This means we only have to detect these specific patterns: + // + // ldc.i8 0x0 ldarg + // ldarg ldc.i8 0x0 + // beq/bne beq/bne + // + // The workaround is to replace ldarg with ldarga/ldind.i8. Looking at the generated code by the x86 and x64 JITs + // this appears to be as efficient as the ldarg and it avoids the x64 bug. + if (code[i].opcode == OpCodes.Ldc_I8 && code[i].ValueInt64 == 0) + { + short arg; + int m; + if (i > 0 && MatchLdarg(code[i - 1], out arg) && IsBranchEqNe(code[i + 1].opcode)) + { + m = i - 1; + } + else if (MatchLdarg(code[i + 1], out arg) && IsBranchEqNe(code[i + 2].opcode)) + { + m = i + 1; + } + else + { + continue; + } + code[m] = new OpCodeWrapper(OpCodes.Ldarga, arg); + code.Insert(m + 1, new OpCodeWrapper(OpCodes.Ldind_I8, null)); + } + } + } + + [Conditional("CHECK_INVARIANTS")] + private void CheckInvariants() + { + CheckInvariantBranchInOrOutOfBlocks(); + CheckInvariantOpCodeUsage(); + CheckInvariantLocalVariables(); + } + + private void CheckInvariantBranchInOrOutOfBlocks() + { + /* * We maintain an invariant that a branch (other than an explicit leave) * can never branch out or into an exception block (try or handler). * This is a stronger invariant than requirement by MSIL, because @@ -2065,594 +2059,595 @@ private void CheckInvariantBranchInOrOutOfBlocks() * * Br Label0 * ... - * BeginExceptionBlock - * Label0: - * ... - * Br Label0 - * - * This should be rewritten as: - * - * Br Label0 - * ... - * Label0: - * BeginExceptionBlock - * Label1: - * ... - * Br Label1 - */ - int blockId = 0; - int nextBlockId = 1; - Stack blocks = new Stack(); - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.Label: - code[i].Label.Temp = blockId; - break; - case CodeType.BeginExceptionBlock: - blocks.Push(blockId); - goto case CodeType.BeginFinallyBlock; - case CodeType.BeginFinallyBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginCatchBlock: - blockId = nextBlockId++; - break; - case CodeType.EndExceptionBlock: - blockId = blocks.Pop(); - break; - } - } - if (blocks.Count != 0) - { - throw new InvalidOperationException("Unbalanced exception blocks"); - } - blockId = 0; - nextBlockId = 1; - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.OpCode: - if (code[i].HasLabel - && code[i].opcode != OpCodes.Leave - && code[i].Label.Temp != blockId) - { - DumpMethod(); - throw new InvalidOperationException("Invalid branch " + code[i].opcode.Name + " at offset " + i + " from block " + blockId + " to " + code[i].Label.Temp); - } - break; - case CodeType.BeginExceptionBlock: - blocks.Push(blockId); - goto case CodeType.BeginFinallyBlock; - case CodeType.BeginFinallyBlock: - case CodeType.BeginFaultBlock: - case CodeType.BeginCatchBlock: - blockId = nextBlockId++; - break; - case CodeType.EndExceptionBlock: - blockId = blocks.Pop(); - break; - } - } - } - - private void CheckInvariantOpCodeUsage() - { - for (int i = 0; i < code.Count; i++) - { - switch (code[i].opcode.FlowControl) - { - case FlowControl.Branch: - case FlowControl.Cond_Branch: - if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch) - { - throw new InvalidOperationException(); - } - break; - } - } - } - - private void CheckInvariantLocalVariables() - { - List locals = new List(); - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.DeclareLocal) - { - if (locals.Contains(code[i].Local)) - { - throw new InvalidOperationException("Local variable used before declaration"); - } - } - else if (code[i].HasLocal) - { - locals.Add(code[i].Local); - } - } - } - - private void MoveLocalDeclarationToBeginScope() - { - int pos = 0; - for (int i = 0; i < code.Count; i++) - { - switch (code[i].pseudo) - { - case CodeType.BeginScope: - pos = i + 1; - break; - case CodeType.DeclareLocal: - OpCodeWrapper decl = code[i]; - code.RemoveAt(i); - code.Insert(pos++, decl); - break; - } - } - } - - internal void DoEmit() - { - OptimizePatterns(); - CLRv4_x64_JIT_Workaround(); - RemoveRedundantMemoryBarriers(); - - if (experimentalOptimizations) - { - CheckInvariants(); - MoveLocalDeclarationToBeginScope(); - - for (int i = 0; i < 4; i++) - { - RemoveJumpNext(); - CheckInvariants(); - ChaseBranches(); - CheckInvariants(); - RemoveSingletonBranches(); - CheckInvariants(); - RemoveUnusedLabels(); - CheckInvariants(); - SortPseudoOpCodes(); - CheckInvariants(); - AnnihilatePops(); - CheckInvariants(); - AnnihilateStoreReleaseTempLocals(); - CheckInvariants(); - DeduplicateBranchSourceTargetCode(); - CheckInvariants(); - OptimizeStackTransfer(); - CheckInvariants(); - MergeExceptionBlocks(); - CheckInvariants(); - ConvertSynchronizedFaultToFinally(); - CheckInvariants(); - RemoveDeadCode(); - CheckInvariants(); - } - } - -#if STATIC_COMPILER + * BeginExceptionBlock + * Label0: + * ... + * Br Label0 + * + * This should be rewritten as: + * + * Br Label0 + * ... + * Label0: + * BeginExceptionBlock + * Label1: + * ... + * Br Label1 + */ + int blockId = 0; + int nextBlockId = 1; + Stack blocks = new Stack(); + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.Label: + code[i].Label.Temp = blockId; + break; + case CodeType.BeginExceptionBlock: + blocks.Push(blockId); + goto case CodeType.BeginFinallyBlock; + case CodeType.BeginFinallyBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginCatchBlock: + blockId = nextBlockId++; + break; + case CodeType.EndExceptionBlock: + blockId = blocks.Pop(); + break; + } + } + if (blocks.Count != 0) + { + throw new InvalidOperationException("Unbalanced exception blocks"); + } + blockId = 0; + nextBlockId = 1; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.OpCode: + if (code[i].HasLabel + && code[i].opcode != OpCodes.Leave + && code[i].Label.Temp != blockId) + { + DumpMethod(); + throw new InvalidOperationException("Invalid branch " + code[i].opcode.Name + " at offset " + i + " from block " + blockId + " to " + code[i].Label.Temp); + } + break; + case CodeType.BeginExceptionBlock: + blocks.Push(blockId); + goto case CodeType.BeginFinallyBlock; + case CodeType.BeginFinallyBlock: + case CodeType.BeginFaultBlock: + case CodeType.BeginCatchBlock: + blockId = nextBlockId++; + break; + case CodeType.EndExceptionBlock: + blockId = blocks.Pop(); + break; + } + } + } + + private void CheckInvariantOpCodeUsage() + { + for (int i = 0; i < code.Count; i++) + { + switch (code[i].opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Cond_Branch: + if (!code[i].HasLabel && code[i].opcode != OpCodes.Switch) + { + throw new InvalidOperationException(); + } + break; + } + } + } + + private void CheckInvariantLocalVariables() + { + List locals = new List(); + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.DeclareLocal) + { + if (locals.Contains(code[i].Local)) + { + throw new InvalidOperationException("Local variable used before declaration"); + } + } + else if (code[i].HasLocal) + { + locals.Add(code[i].Local); + } + } + } + + private void MoveLocalDeclarationToBeginScope() + { + int pos = 0; + for (int i = 0; i < code.Count; i++) + { + switch (code[i].pseudo) + { + case CodeType.BeginScope: + pos = i + 1; + break; + case CodeType.DeclareLocal: + OpCodeWrapper decl = code[i]; + code.RemoveAt(i); + code.Insert(pos++, decl); + break; + } + } + } + + internal void DoEmit() + { + OptimizePatterns(); + CLRv4_x64_JIT_Workaround(); + RemoveRedundantMemoryBarriers(); + + if (experimentalOptimizations) + { + CheckInvariants(); + MoveLocalDeclarationToBeginScope(); + + for (int i = 0; i < 4; i++) + { + RemoveJumpNext(); + CheckInvariants(); + ChaseBranches(); + CheckInvariants(); + RemoveSingletonBranches(); + CheckInvariants(); + RemoveUnusedLabels(); + CheckInvariants(); + SortPseudoOpCodes(); + CheckInvariants(); + AnnihilatePops(); + CheckInvariants(); + AnnihilateStoreReleaseTempLocals(); + CheckInvariants(); + DeduplicateBranchSourceTargetCode(); + CheckInvariants(); + OptimizeStackTransfer(); + CheckInvariants(); + MergeExceptionBlocks(); + CheckInvariants(); + ConvertSynchronizedFaultToFinally(); + CheckInvariants(); + RemoveDeadCode(); + CheckInvariants(); + } + } + +#if IMPORTER OptimizeEncodings(); OptimizeBranchSizes(); #endif - int ilOffset = 0; - int lineNumber = -1; - for (int i = 0; i < code.Count; i++) - { - code[i].RealEmit(ilOffset, this, ref lineNumber); -#if STATIC_COMPILER || NET_4_0 - ilOffset = ilgen_real.ILOffset; -#else - ilOffset += code[i].Size; -#endif - } - } - - internal void DumpMethod() - { - Dictionary labelIndexes = new Dictionary(); - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.Label) - { - labelIndexes.Add(code[i].Label, i); - } - } - Console.WriteLine("======================"); - for (int i = 0; i < code.Count; i++) - { - if (code[i].pseudo == CodeType.OpCode) - { - Console.Write(" " + code[i].opcode.Name); - if (code[i].HasLabel) - { - Console.Write(" label" + labelIndexes[code[i].Label]); - } - else if (code[i].opcode == OpCodes.Ldarg_S || code[i].opcode == OpCodes.Ldarga_S) - { - Console.Write(" " + code[i].ValueByte); - } - else if (code[i].opcode == OpCodes.Ldarg || code[i].opcode == OpCodes.Ldarga) - { - Console.Write(" " + code[i].ValueInt16); - } - else if (code[i].opcode == OpCodes.Isinst || code[i].opcode == OpCodes.Castclass || code[i].opcode == OpCodes.Box || code[i].opcode == OpCodes.Unbox || code[i].opcode == OpCodes.Ldobj || code[i].opcode == OpCodes.Newarr) - { - Console.Write(" " + code[i].Type); - } - else if (code[i].opcode == OpCodes.Call || code[i].opcode == OpCodes.Callvirt) - { - Console.Write(" " + code[i].MethodBase); - } - else if (code[i].opcode == OpCodes.Ldfld || code[i].opcode == OpCodes.Ldsfld || code[i].opcode == OpCodes.Stfld || code[i].opcode == OpCodes.Stsfld) - { - Console.Write(" " + code[i].FieldInfo); - } - else if (code[i].opcode == OpCodes.Ldc_I4) - { - Console.Write(" " + code[i].ValueInt32); - } - else if (code[i].opcode == OpCodes.Ldloc || code[i].opcode == OpCodes.Stloc) - { - Console.Write(" " + code[i].Local.__LocalIndex); - } - Console.WriteLine(); - } - else if (code[i].pseudo == CodeType.Label) - { - Console.WriteLine("label{0}: // temp = {1}", i, code[i].Label.Temp); - } - else if (code[i].pseudo == CodeType.DeclareLocal) - { - Console.WriteLine("local #{0} = {1}", code[i].Local.__LocalIndex, code[i].Local.LocalType); - } - else - { - Console.WriteLine(code[i]); - } - } - } - - internal void DefineSymbolDocument(ModuleBuilder module, string url, Guid language, Guid languageVendor, Guid documentType) - { + int ilOffset = 0; + int lineNumber = -1; + for (int i = 0; i < code.Count; i++) + { + code[i].RealEmit(ilOffset, this, ref lineNumber); + ilOffset = ilgen_real.ILOffset; + } + } + + internal void DumpMethod() + { + Dictionary labelIndexes = new Dictionary(); + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.Label) + { + labelIndexes.Add(code[i].Label, i); + } + } + Console.WriteLine("======================"); + for (int i = 0; i < code.Count; i++) + { + if (code[i].pseudo == CodeType.OpCode) + { + Console.Write(" " + code[i].opcode.Name); + if (code[i].HasLabel) + { + Console.Write(" label" + labelIndexes[code[i].Label]); + } + else if (code[i].opcode == OpCodes.Ldarg_S || code[i].opcode == OpCodes.Ldarga_S) + { + Console.Write(" " + code[i].ValueByte); + } + else if (code[i].opcode == OpCodes.Ldarg || code[i].opcode == OpCodes.Ldarga) + { + Console.Write(" " + code[i].ValueInt16); + } + else if (code[i].opcode == OpCodes.Isinst || code[i].opcode == OpCodes.Castclass || code[i].opcode == OpCodes.Box || code[i].opcode == OpCodes.Unbox || code[i].opcode == OpCodes.Ldobj || code[i].opcode == OpCodes.Newarr) + { + Console.Write(" " + code[i].Type); + } + else if (code[i].opcode == OpCodes.Call || code[i].opcode == OpCodes.Callvirt) + { + Console.Write(" " + code[i].MethodBase); + } + else if (code[i].opcode == OpCodes.Ldfld || code[i].opcode == OpCodes.Ldsfld || code[i].opcode == OpCodes.Stfld || code[i].opcode == OpCodes.Stsfld) + { + Console.Write(" " + code[i].FieldInfo); + } + else if (code[i].opcode == OpCodes.Ldc_I4) + { + Console.Write(" " + code[i].ValueInt32); + } + else if (code[i].opcode == OpCodes.Ldloc || code[i].opcode == OpCodes.Stloc) + { + Console.Write(" " + code[i].Local.__LocalIndex); + } + Console.WriteLine(); + } + else if (code[i].pseudo == CodeType.Label) + { + Console.WriteLine("label{0}: // temp = {1}", i, code[i].Label.Temp); + } + else if (code[i].pseudo == CodeType.DeclareLocal) + { + Console.WriteLine("local #{0} = {1}", code[i].Local.__LocalIndex, code[i].Local.LocalType); + } + else + { + Console.WriteLine(code[i]); + } + } + } + + internal void DefineSymbolDocument(ModuleBuilder module, string url, Guid language, Guid languageVendor, Guid documentType) + { #if NETFRAMEWORK - symbols = module.DefineDocument(url, language, languageVendor, documentType); + symbols = module.DefineDocument(url, language, languageVendor, documentType); #endif - } - - internal CodeEmitterLocal UnsafeAllocTempLocal(Type type) - { - int free = -1; - for (int i = 0; i < tempLocals.Length; i++) - { - CodeEmitterLocal lb = tempLocals[i]; - if (lb == null) - { - if (free == -1) - { - free = i; - } - } - else if (lb.LocalType == type) - { - return lb; - } - } - CodeEmitterLocal lb1 = DeclareLocal(type); - if (free != -1) - { - tempLocals[free] = lb1; - } - return lb1; - } - - internal CodeEmitterLocal AllocTempLocal(Type type) - { - for (int i = 0; i < tempLocals.Length; i++) - { - CodeEmitterLocal lb = tempLocals[i]; - if (lb != null && lb.LocalType == type) - { - tempLocals[i] = null; - return lb; - } - } - return new CodeEmitterLocal(type); - } - - internal void ReleaseTempLocal(CodeEmitterLocal lb) - { - EmitPseudoOpCode(CodeType.ReleaseTempLocal, lb); - for (int i = 0; i < tempLocals.Length; i++) - { - if (tempLocals[i] == null) - { - tempLocals[i] = lb; - break; - } - } - } - - internal void BeginCatchBlock(Type exceptionType) - { - EmitPseudoOpCode(CodeType.BeginCatchBlock, exceptionType); - } - - internal void BeginExceptionBlock() - { -#if !STATIC_COMPILER - exceptionStack.Push(inFinally); - inFinally = false; + } + + internal CodeEmitterLocal UnsafeAllocTempLocal(Type type) + { + int free = -1; + for (int i = 0; i < tempLocals.Length; i++) + { + CodeEmitterLocal lb = tempLocals[i]; + if (lb == null) + { + if (free == -1) + { + free = i; + } + } + else if (lb.LocalType == type) + { + return lb; + } + } + CodeEmitterLocal lb1 = DeclareLocal(type); + if (free != -1) + { + tempLocals[free] = lb1; + } + return lb1; + } + + internal CodeEmitterLocal AllocTempLocal(Type type) + { + for (int i = 0; i < tempLocals.Length; i++) + { + CodeEmitterLocal lb = tempLocals[i]; + if (lb != null && lb.LocalType == type) + { + tempLocals[i] = null; + return lb; + } + } + return new CodeEmitterLocal(type); + } + + internal void ReleaseTempLocal(CodeEmitterLocal lb) + { + EmitPseudoOpCode(CodeType.ReleaseTempLocal, lb); + for (int i = 0; i < tempLocals.Length; i++) + { + if (tempLocals[i] == null) + { + tempLocals[i] = lb; + break; + } + } + } + + internal void BeginCatchBlock(Type exceptionType) + { + EmitPseudoOpCode(CodeType.BeginCatchBlock, exceptionType); + } + + internal void BeginExceptionBlock() + { +#if !IMPORTER + exceptionStack.Push(inFinally); + inFinally = false; #endif - EmitPseudoOpCode(CodeType.BeginExceptionBlock, null); - } + EmitPseudoOpCode(CodeType.BeginExceptionBlock, null); + } - internal void BeginFaultBlock() - { -#if !STATIC_COMPILER - inFinally = true; + internal void BeginFaultBlock() + { +#if !IMPORTER + inFinally = true; #endif - EmitPseudoOpCode(CodeType.BeginFaultBlock, null); - } + EmitPseudoOpCode(CodeType.BeginFaultBlock, null); + } - internal void BeginFinallyBlock() - { -#if !STATIC_COMPILER - inFinally = true; + internal void BeginFinallyBlock() + { +#if !IMPORTER + inFinally = true; #endif - EmitPseudoOpCode(CodeType.BeginFinallyBlock, null); - } - - internal void BeginScope() - { - EmitPseudoOpCode(CodeType.BeginScope, null); - } - - internal CodeEmitterLocal DeclareLocal(Type localType) - { - CodeEmitterLocal local = new CodeEmitterLocal(localType); - EmitPseudoOpCode(CodeType.DeclareLocal, local); - return local; - } - - internal CodeEmitterLabel DefineLabel() - { - CodeEmitterLabel label = new CodeEmitterLabel(ilgen_real.DefineLabel()); + EmitPseudoOpCode(CodeType.BeginFinallyBlock, null); + } + + internal void BeginScope() + { + EmitPseudoOpCode(CodeType.BeginScope, null); + } + + internal CodeEmitterLocal DeclareLocal(Type localType) + { + CodeEmitterLocal local = new CodeEmitterLocal(localType); + EmitPseudoOpCode(CodeType.DeclareLocal, local); + return local; + } + + internal CodeEmitterLabel DefineLabel() + { + CodeEmitterLabel label = new CodeEmitterLabel(ilgen_real.DefineLabel()); #if LABELCHECK labels.Add(label, new System.Diagnostics.StackFrame(1, true)); #endif - return label; - } - - internal void Emit(OpCode opcode) - { - EmitOpCode(opcode, null); - } - - internal void EmitUnaligned(byte alignment) - { - EmitOpCode(OpCodes.Unaligned, alignment); - } - - internal void Emit(OpCode opcode, MethodBase mb) - { - EmitOpCode(opcode, mb); - } - - internal void EmitLdc_R8(double arg) - { - EmitOpCode(OpCodes.Ldc_R8, arg); - } - - internal void Emit(OpCode opcode, FieldInfo field) - { - EmitOpCode(opcode, field); - } - - internal void EmitLdarg(int arg) - { - Debug.Assert(0 <= arg && arg < 65536); - switch (arg) - { - case 0: - EmitOpCode(OpCodes.Ldarg_0, null); - break; - case 1: - EmitOpCode(OpCodes.Ldarg_1, null); - break; - case 2: - EmitOpCode(OpCodes.Ldarg_2, null); - break; - case 3: - EmitOpCode(OpCodes.Ldarg_3, null); - break; - default: - if (arg < 256) - { - EmitOpCode(OpCodes.Ldarg_S, (byte)arg); - } - else - { - EmitOpCode(OpCodes.Ldarg, (short)arg); - } - break; - } - } - - internal void EmitLdarga(int arg) - { - Debug.Assert(0 <= arg && arg < 65536); - if (arg < 256) - { - EmitOpCode(OpCodes.Ldarga_S, (byte)arg); - } - else - { - EmitOpCode(OpCodes.Ldarga, (short)arg); - } - } - - internal void EmitStarg(int arg) - { - Debug.Assert(0 <= arg && arg < 65536); - if (arg < 256) - { - EmitOpCode(OpCodes.Starg_S, (byte)arg); - } - else - { - EmitOpCode(OpCodes.Starg, (short)arg); - } - } - - internal void EmitLdc_I8(long arg) - { - EmitOpCode(OpCodes.Ldc_I8, arg); - } - - internal void EmitBr(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Br, label); - } - - internal void EmitBeq(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Beq, label); - } - - internal void EmitBne_Un(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Bne_Un, label); - } - - internal void EmitBle_Un(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Ble_Un, label); - } - - internal void EmitBlt_Un(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Blt_Un, label); - } - - internal void EmitBge_Un(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Bge_Un, label); - } - - internal void EmitBle(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Ble, label); - } - - internal void EmitBlt(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Blt, label); - } - - internal void EmitBge(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Bge, label); - } - - internal void EmitBgt(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Bgt, label); - } - - internal void EmitBrtrue(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Brtrue, label); - } - - internal void EmitBrfalse(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Brfalse, label); - } - - internal void EmitLeave(CodeEmitterLabel label) - { - EmitOpCode(OpCodes.Leave, label); - } - - internal void EmitSwitch(CodeEmitterLabel[] labels) - { - EmitOpCode(OpCodes.Switch, labels); - } - - internal void Emit(OpCode opcode, CodeEmitterLocal local) - { - EmitOpCode(opcode, local); - } - - internal void EmitLdc_R4(float arg) - { - EmitOpCode(OpCodes.Ldc_R4, arg); - } - - internal void Emit(OpCode opcode, string arg) - { - EmitOpCode(opcode, arg); - } - - internal void Emit(OpCode opcode, Type cls) - { - EmitOpCode(opcode, cls); - } - - internal void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) - { - EmitOpCode(opcode, new CalliWrapper(unmanagedCallConv, returnType, parameterTypes)); - } - - internal void EndExceptionBlock() - { -#if STATIC_COMPILER + return label; + } + + internal void Emit(OpCode opcode) + { + EmitOpCode(opcode, null); + } + + internal void EmitUnaligned(byte alignment) + { + EmitOpCode(OpCodes.Unaligned, alignment); + } + + internal void Emit(OpCode opcode, MethodBase mb) + { + EmitOpCode(opcode, mb); + } + + internal void EmitLdc_R8(double arg) + { + EmitOpCode(OpCodes.Ldc_R8, arg); + } + + internal void Emit(OpCode opcode, FieldInfo field) + { + EmitOpCode(opcode, field); + } + + internal void EmitLdarg(int arg) + { + Debug.Assert(0 <= arg && arg < 65536); + switch (arg) + { + case 0: + EmitOpCode(OpCodes.Ldarg_0, null); + break; + case 1: + EmitOpCode(OpCodes.Ldarg_1, null); + break; + case 2: + EmitOpCode(OpCodes.Ldarg_2, null); + break; + case 3: + EmitOpCode(OpCodes.Ldarg_3, null); + break; + default: + if (arg < 256) + { + EmitOpCode(OpCodes.Ldarg_S, (byte)arg); + } + else + { + EmitOpCode(OpCodes.Ldarg, (short)arg); + } + break; + } + } + + internal void EmitLdarga(int arg) + { + Debug.Assert(0 <= arg && arg < 65536); + if (arg < 256) + { + EmitOpCode(OpCodes.Ldarga_S, (byte)arg); + } + else + { + EmitOpCode(OpCodes.Ldarga, (short)arg); + } + } + + internal void EmitStarg(int arg) + { + Debug.Assert(0 <= arg && arg < 65536); + if (arg < 256) + { + EmitOpCode(OpCodes.Starg_S, (byte)arg); + } + else + { + EmitOpCode(OpCodes.Starg, (short)arg); + } + } + + internal void EmitLdc_I8(long arg) + { + EmitOpCode(OpCodes.Ldc_I8, arg); + } + + internal void EmitBr(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Br, label); + } + + internal void EmitBeq(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Beq, label); + } + + internal void EmitBne_Un(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Bne_Un, label); + } + + internal void EmitBle_Un(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Ble_Un, label); + } + + internal void EmitBlt_Un(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Blt_Un, label); + } + + internal void EmitBge_Un(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Bge_Un, label); + } + + internal void EmitBle(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Ble, label); + } + + internal void EmitBlt(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Blt, label); + } + + internal void EmitBge(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Bge, label); + } + + internal void EmitBgt(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Bgt, label); + } + + internal void EmitBrtrue(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Brtrue, label); + } + + internal void EmitBrfalse(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Brfalse, label); + } + + internal void EmitLeave(CodeEmitterLabel label) + { + EmitOpCode(OpCodes.Leave, label); + } + + internal void EmitSwitch(CodeEmitterLabel[] labels) + { + EmitOpCode(OpCodes.Switch, labels); + } + + internal void Emit(OpCode opcode, CodeEmitterLocal local) + { + EmitOpCode(opcode, local); + } + + internal void EmitLdc_R4(float arg) + { + EmitOpCode(OpCodes.Ldc_R4, arg); + } + + internal void Emit(OpCode opcode, string arg) + { + EmitOpCode(opcode, arg); + } + + internal void Emit(OpCode opcode, Type cls) + { + EmitOpCode(opcode, cls); + } + + internal void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) + { + EmitOpCode(opcode, new CalliWrapper(unmanagedCallConv, returnType, parameterTypes)); + } + + internal void EmitCalli(OpCode opcode, CallingConventions unmanagedCallConv, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) + { + EmitOpCode(opcode, new ManagedCalliWrapper(unmanagedCallConv, returnType, parameterTypes, optionalParameterTypes)); + } + + internal void EndExceptionBlock() + { +#if IMPORTER EmitPseudoOpCode(CodeType.EndExceptionBlock, null); #else - EmitPseudoOpCode(CodeType.EndExceptionBlock, inFinally ? CodeTypeFlags.EndFaultOrFinally : CodeTypeFlags.None); - inFinally = exceptionStack.Pop(); + EmitPseudoOpCode(CodeType.EndExceptionBlock, inFinally ? CodeTypeFlags.EndFaultOrFinally : CodeTypeFlags.None); + inFinally = exceptionStack.Pop(); #endif - } + } - internal void EndScope() - { - EmitPseudoOpCode(CodeType.EndScope, null); - } + internal void EndScope() + { + EmitPseudoOpCode(CodeType.EndScope, null); + } - internal void MarkLabel(CodeEmitterLabel loc) - { + internal void MarkLabel(CodeEmitterLabel loc) + { #if LABELCHECK labels.Remove(loc); #endif - EmitPseudoOpCode(CodeType.Label, loc); - } + EmitPseudoOpCode(CodeType.Label, loc); + } - internal void ThrowException(Type excType) - { - Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes)); - Emit(OpCodes.Throw); - } + internal void ThrowException(Type excType) + { + Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes)); + Emit(OpCodes.Throw); + } - internal void SetLineNumber(ushort line) - { + internal void SetLineNumber(ushort line) + { #if NETFRAMEWORK - if (symbols != null) - { - EmitPseudoOpCode(CodeType.SequencePoint, (int)line); - } + if (symbols != null) + { + EmitPseudoOpCode(CodeType.SequencePoint, (int)line); + } #endif - EmitPseudoOpCode(CodeType.LineNumber, (int)line); - } + EmitPseudoOpCode(CodeType.LineNumber, (int)line); + } - internal byte[] GetLineNumberTable() - { - return linenums == null ? null : linenums.ToArray(); - } + internal byte[] GetLineNumberTable() + { + return linenums == null ? null : linenums.ToArray(); + } -#if STATIC_COMPILER +#if IMPORTER internal void EmitLineNumberTable(MethodBuilder mb) { if(linenums != null) @@ -2660,225 +2655,225 @@ internal void EmitLineNumberTable(MethodBuilder mb) AttributeHelper.SetLineNumberTable(mb, linenums); } } -#endif // STATIC_COMPILER - - internal void EmitThrow(string dottedClassName) - { - TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(dottedClassName); - MethodWrapper mw = exception.GetMethodWrapper("", "()V", false); - mw.Link(); - mw.EmitNewobj(this); - Emit(OpCodes.Throw); - } - - internal void EmitThrow(string dottedClassName, string message) - { - TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(dottedClassName); - Emit(OpCodes.Ldstr, message); - MethodWrapper mw = exception.GetMethodWrapper("", "(Ljava.lang.String;)V", false); - mw.Link(); - mw.EmitNewobj(this); - Emit(OpCodes.Throw); - } - - internal void EmitNullCheck() - { - // I think this is the most efficient way to generate a NullReferenceException if the reference is null - Emit(OpCodes.Ldvirtftn, objectToString); - Emit(OpCodes.Pop); - } - - internal void EmitCastclass(Type type) - { - if (verboseCastFailure != null) - { - CodeEmitterLocal lb = DeclareLocal(Types.Object); - Emit(OpCodes.Stloc, lb); - Emit(OpCodes.Ldloc, lb); - Emit(OpCodes.Isinst, type); - Emit(OpCodes.Dup); - CodeEmitterLabel ok = DefineLabel(); - EmitBrtrue(ok); - Emit(OpCodes.Ldloc, lb); - EmitBrfalse(ok); // handle null - Emit(OpCodes.Ldtoken, type); - Emit(OpCodes.Ldloc, lb); - Emit(OpCodes.Call, verboseCastFailure); - MarkLabel(ok); - } - else - { - Emit(OpCodes.Castclass, type); - } - } - - // This is basically the same as Castclass, except that it - // throws an IncompatibleClassChangeError on failure. - internal void EmitAssertType(Type type) - { - CodeEmitterLabel isnull = DefineLabel(); - Emit(OpCodes.Dup); - EmitBrfalse(isnull); - Emit(OpCodes.Isinst, type); - Emit(OpCodes.Dup); - CodeEmitterLabel ok = DefineLabel(); - EmitBrtrue(ok); - EmitThrow("java.lang.IncompatibleClassChangeError"); - MarkLabel(isnull); - Emit(OpCodes.Pop); - Emit(OpCodes.Ldnull); - MarkLabel(ok); - } - - internal void EmitUnboxSpecial(Type type) - { - // NOTE if the reference is null, we treat it as a default instance of the value type. - Emit(OpCodes.Dup); - CodeEmitterLabel label1 = DefineLabel(); - EmitBrtrue(label1); - Emit(OpCodes.Pop); - CodeEmitterLocal local = AllocTempLocal(type); - Emit(OpCodes.Ldloca, local); - Emit(OpCodes.Initobj, type); - Emit(OpCodes.Ldloc, local); - ReleaseTempLocal(local); - CodeEmitterLabel label2 = DefineLabel(); - EmitBr(label2); - MarkLabel(label1); - Emit(OpCodes.Unbox, type); - Emit(OpCodes.Ldobj, type); - MarkLabel(label2); - } - - internal void EmitLdc_I4(int i) - { - EmitOpCode(OpCodes.Ldc_I4, i); - } - - internal void Emit_idiv() - { - // we need to special case dividing by -1, because the CLR div instruction - // throws an OverflowException when dividing Int32.MinValue by -1, and - // Java just silently overflows - Emit(OpCodes.Dup); - Emit(OpCodes.Ldc_I4_M1); - CodeEmitterLabel label = DefineLabel(); - EmitBne_Un(label); - Emit(OpCodes.Pop); - Emit(OpCodes.Neg); - CodeEmitterLabel label2 = DefineLabel(); - EmitBr(label2); - MarkLabel(label); - Emit(OpCodes.Div); - MarkLabel(label2); - } - - internal void Emit_ldiv() - { - // we need to special case dividing by -1, because the CLR div instruction - // throws an OverflowException when dividing Int32.MinValue by -1, and - // Java just silently overflows - Emit(OpCodes.Dup); - Emit(OpCodes.Ldc_I4_M1); - Emit(OpCodes.Conv_I8); - CodeEmitterLabel label = DefineLabel(); - EmitBne_Un(label); - Emit(OpCodes.Pop); - Emit(OpCodes.Neg); - CodeEmitterLabel label2 = DefineLabel(); - EmitBr(label2); - MarkLabel(label); - Emit(OpCodes.Div); - MarkLabel(label2); - } - - internal void Emit_instanceof(Type type) - { - Emit(OpCodes.Isinst, type); - Emit(OpCodes.Ldnull); - Emit(OpCodes.Cgt_Un); - } - - internal enum Comparison - { - LessOrEqual, - LessThan, - GreaterOrEqual, - GreaterThan - } - - internal void Emit_if_le_lt_ge_gt(Comparison comp, CodeEmitterLabel label) - { - // don't change this Ldc_I4_0 to Ldc_I4(0) because the optimizer recognizes - // only this specific pattern - Emit(OpCodes.Ldc_I4_0); - switch (comp) - { - case Comparison.LessOrEqual: - EmitBle(label); - break; - case Comparison.LessThan: - EmitBlt(label); - break; - case Comparison.GreaterOrEqual: - EmitBge(label); - break; - case Comparison.GreaterThan: - EmitBgt(label); - break; - } - } - - private void EmitCmp(Type type, OpCode cmp1, OpCode cmp2) - { - CodeEmitterLocal value1 = AllocTempLocal(type); - CodeEmitterLocal value2 = AllocTempLocal(type); - Emit(OpCodes.Stloc, value2); - Emit(OpCodes.Stloc, value1); - Emit(OpCodes.Ldloc, value1); - Emit(OpCodes.Ldloc, value2); - Emit(cmp1); - Emit(OpCodes.Ldloc, value1); - Emit(OpCodes.Ldloc, value2); - Emit(cmp2); - Emit(OpCodes.Sub); - ReleaseTempLocal(value2); - ReleaseTempLocal(value1); - } - - internal void Emit_lcmp() - { - EmitCmp(Types.Int64, OpCodes.Cgt, OpCodes.Clt); - } - - internal void Emit_fcmpl() - { - EmitCmp(Types.Single, OpCodes.Cgt, OpCodes.Clt_Un); - } - - internal void Emit_fcmpg() - { - EmitCmp(Types.Single, OpCodes.Cgt_Un, OpCodes.Clt); - } - - internal void Emit_dcmpl() - { - EmitCmp(Types.Double, OpCodes.Cgt, OpCodes.Clt_Un); - } - - internal void Emit_dcmpg() - { - EmitCmp(Types.Double, OpCodes.Cgt_Un, OpCodes.Clt); - } - - internal void Emit_And_I4(int v) - { - EmitLdc_I4(v); - Emit(OpCodes.And); - } - - internal void CheckLabels() - { +#endif // IMPORTER + + internal void EmitThrow(string dottedClassName) + { + TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(dottedClassName); + MethodWrapper mw = exception.GetMethodWrapper("", "()V", false); + mw.Link(); + mw.EmitNewobj(this); + Emit(OpCodes.Throw); + } + + internal void EmitThrow(string dottedClassName, string message) + { + TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(dottedClassName); + Emit(OpCodes.Ldstr, message); + MethodWrapper mw = exception.GetMethodWrapper("", "(Ljava.lang.String;)V", false); + mw.Link(); + mw.EmitNewobj(this); + Emit(OpCodes.Throw); + } + + internal void EmitNullCheck() + { + // I think this is the most efficient way to generate a NullReferenceException if the reference is null + Emit(OpCodes.Ldvirtftn, objectToString); + Emit(OpCodes.Pop); + } + + internal void EmitCastclass(Type type) + { + if (verboseCastFailure != null) + { + CodeEmitterLocal lb = DeclareLocal(Types.Object); + Emit(OpCodes.Stloc, lb); + Emit(OpCodes.Ldloc, lb); + Emit(OpCodes.Isinst, type); + Emit(OpCodes.Dup); + CodeEmitterLabel ok = DefineLabel(); + EmitBrtrue(ok); + Emit(OpCodes.Ldloc, lb); + EmitBrfalse(ok); // handle null + Emit(OpCodes.Ldtoken, type); + Emit(OpCodes.Ldloc, lb); + Emit(OpCodes.Call, verboseCastFailure); + MarkLabel(ok); + } + else + { + Emit(OpCodes.Castclass, type); + } + } + + // This is basically the same as Castclass, except that it + // throws an IncompatibleClassChangeError on failure. + internal void EmitAssertType(Type type) + { + CodeEmitterLabel isnull = DefineLabel(); + Emit(OpCodes.Dup); + EmitBrfalse(isnull); + Emit(OpCodes.Isinst, type); + Emit(OpCodes.Dup); + CodeEmitterLabel ok = DefineLabel(); + EmitBrtrue(ok); + EmitThrow("java.lang.IncompatibleClassChangeError"); + MarkLabel(isnull); + Emit(OpCodes.Pop); + Emit(OpCodes.Ldnull); + MarkLabel(ok); + } + + internal void EmitUnboxSpecial(Type type) + { + // NOTE if the reference is null, we treat it as a default instance of the value type. + Emit(OpCodes.Dup); + CodeEmitterLabel label1 = DefineLabel(); + EmitBrtrue(label1); + Emit(OpCodes.Pop); + CodeEmitterLocal local = AllocTempLocal(type); + Emit(OpCodes.Ldloca, local); + Emit(OpCodes.Initobj, type); + Emit(OpCodes.Ldloc, local); + ReleaseTempLocal(local); + CodeEmitterLabel label2 = DefineLabel(); + EmitBr(label2); + MarkLabel(label1); + Emit(OpCodes.Unbox, type); + Emit(OpCodes.Ldobj, type); + MarkLabel(label2); + } + + internal void EmitLdc_I4(int i) + { + EmitOpCode(OpCodes.Ldc_I4, i); + } + + internal void Emit_idiv() + { + // we need to special case dividing by -1, because the CLR div instruction + // throws an OverflowException when dividing Int32.MinValue by -1, and + // Java just silently overflows + Emit(OpCodes.Dup); + Emit(OpCodes.Ldc_I4_M1); + CodeEmitterLabel label = DefineLabel(); + EmitBne_Un(label); + Emit(OpCodes.Pop); + Emit(OpCodes.Neg); + CodeEmitterLabel label2 = DefineLabel(); + EmitBr(label2); + MarkLabel(label); + Emit(OpCodes.Div); + MarkLabel(label2); + } + + internal void Emit_ldiv() + { + // we need to special case dividing by -1, because the CLR div instruction + // throws an OverflowException when dividing Int32.MinValue by -1, and + // Java just silently overflows + Emit(OpCodes.Dup); + Emit(OpCodes.Ldc_I4_M1); + Emit(OpCodes.Conv_I8); + CodeEmitterLabel label = DefineLabel(); + EmitBne_Un(label); + Emit(OpCodes.Pop); + Emit(OpCodes.Neg); + CodeEmitterLabel label2 = DefineLabel(); + EmitBr(label2); + MarkLabel(label); + Emit(OpCodes.Div); + MarkLabel(label2); + } + + internal void Emit_instanceof(Type type) + { + Emit(OpCodes.Isinst, type); + Emit(OpCodes.Ldnull); + Emit(OpCodes.Cgt_Un); + } + + internal enum Comparison + { + LessOrEqual, + LessThan, + GreaterOrEqual, + GreaterThan + } + + internal void Emit_if_le_lt_ge_gt(Comparison comp, CodeEmitterLabel label) + { + // don't change this Ldc_I4_0 to Ldc_I4(0) because the optimizer recognizes + // only this specific pattern + Emit(OpCodes.Ldc_I4_0); + switch (comp) + { + case Comparison.LessOrEqual: + EmitBle(label); + break; + case Comparison.LessThan: + EmitBlt(label); + break; + case Comparison.GreaterOrEqual: + EmitBge(label); + break; + case Comparison.GreaterThan: + EmitBgt(label); + break; + } + } + + private void EmitCmp(Type type, OpCode cmp1, OpCode cmp2) + { + CodeEmitterLocal value1 = AllocTempLocal(type); + CodeEmitterLocal value2 = AllocTempLocal(type); + Emit(OpCodes.Stloc, value2); + Emit(OpCodes.Stloc, value1); + Emit(OpCodes.Ldloc, value1); + Emit(OpCodes.Ldloc, value2); + Emit(cmp1); + Emit(OpCodes.Ldloc, value1); + Emit(OpCodes.Ldloc, value2); + Emit(cmp2); + Emit(OpCodes.Sub); + ReleaseTempLocal(value2); + ReleaseTempLocal(value1); + } + + internal void Emit_lcmp() + { + EmitCmp(Types.Int64, OpCodes.Cgt, OpCodes.Clt); + } + + internal void Emit_fcmpl() + { + EmitCmp(Types.Single, OpCodes.Cgt, OpCodes.Clt_Un); + } + + internal void Emit_fcmpg() + { + EmitCmp(Types.Single, OpCodes.Cgt_Un, OpCodes.Clt); + } + + internal void Emit_dcmpl() + { + EmitCmp(Types.Double, OpCodes.Cgt, OpCodes.Clt_Un); + } + + internal void Emit_dcmpg() + { + EmitCmp(Types.Double, OpCodes.Cgt_Un, OpCodes.Clt); + } + + internal void Emit_And_I4(int v) + { + EmitLdc_I4(v); + Emit(OpCodes.And); + } + + internal void CheckLabels() + { #if LABELCHECK foreach(System.Diagnostics.StackFrame frame in labels.Values) { @@ -2886,32 +2881,32 @@ internal void CheckLabels() IKVM.Internal.JVM.CriticalFailure("Label failure: " + name, null); } #endif - } - - internal void EmitMemoryBarrier() - { - EmitPseudoOpCode(CodeType.MemoryBarrier, null); - } - - internal void EmitTailCallPrevention() - { - EmitPseudoOpCode(CodeType.TailCallPrevention, null); - } - - internal void EmitClearStack() - { - EmitPseudoOpCode(CodeType.ClearStack, null); - } - - internal void EmitMonitorEnter() - { - EmitPseudoOpCode(CodeType.MonitorEnter, null); - } - - internal void EmitMonitorExit() - { - EmitPseudoOpCode(CodeType.MonitorExit, null); - } - } + } + + internal void EmitMemoryBarrier() + { + EmitPseudoOpCode(CodeType.MemoryBarrier, null); + } + + internal void EmitTailCallPrevention() + { + EmitPseudoOpCode(CodeType.TailCallPrevention, null); + } + + internal void EmitClearStack() + { + EmitPseudoOpCode(CodeType.ClearStack, null); + } + + internal void EmitMonitorEnter() + { + EmitPseudoOpCode(CodeType.MonitorEnter, null); + } + + internal void EmitMonitorExit() + { + EmitPseudoOpCode(CodeType.MonitorExit, null); + } + } } diff --git a/src/IKVM.Runtime/CodeEmitterLabel.cs b/src/IKVM.Runtime/CodeEmitterLabel.cs index 3d052734ab..3f1c3f8d3c 100644 --- a/src/IKVM.Runtime/CodeEmitterLabel.cs +++ b/src/IKVM.Runtime/CodeEmitterLabel.cs @@ -22,7 +22,7 @@ Jeroen Frijters */ -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; #else diff --git a/src/IKVM.Runtime/CodeEmitterLocal.cs b/src/IKVM.Runtime/CodeEmitterLocal.cs index 7fb7757564..01f0a9885a 100644 --- a/src/IKVM.Runtime/CodeEmitterLocal.cs +++ b/src/IKVM.Runtime/CodeEmitterLocal.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -76,7 +76,7 @@ internal void Declare(ILGenerator ilgen) local = ilgen.DeclareLocal(type); if (name != null) { -#if NET461_OR_GREATER +#if NETFRAMEWORK // SetLocalSymInfo does not exist in .net core local.SetLocalSymInfo(name); #endif diff --git a/src/IKVM.Runtime/CodeGenOptions.cs b/src/IKVM.Runtime/CodeGenOptions.cs index e2973fe4c8..f9f1e5779d 100644 --- a/src/IKVM.Runtime/CodeGenOptions.cs +++ b/src/IKVM.Runtime/CodeGenOptions.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; diff --git a/src/IKVM.Runtime/CompiledAccessStubFieldWrapper.cs b/src/IKVM.Runtime/CompiledAccessStubFieldWrapper.cs new file mode 100644 index 0000000000..ff8c43eacc --- /dev/null +++ b/src/IKVM.Runtime/CompiledAccessStubFieldWrapper.cs @@ -0,0 +1,135 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + sealed class CompiledAccessStubFieldWrapper : FieldWrapper + { + + readonly MethodInfo getter; + readonly MethodInfo setter; + + static Modifiers GetModifiers(PropertyInfo property) + { + // NOTE we only support the subset of modifiers that is expected for "access stub" properties + var getter = property.GetGetMethod(true); + var modifiers = getter.IsPublic ? Modifiers.Public : Modifiers.Protected; + if (!property.CanWrite) + modifiers |= Modifiers.Final; + if (getter.IsStatic) + modifiers |= Modifiers.Static; + + return modifiers; + } + + /// + /// Initializes a new instance. This constructor is used for type 1 access stubs. + /// + /// + /// + /// + internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, TypeWrapper propertyType) : + this(wrapper, property, null, propertyType, GetModifiers(property), MemberFlags.HideFromReflection | MemberFlags.AccessStub) + { + + } + + /// + /// Initializes a new instance. This constructor is used for type 2 access stubs. + /// + /// + /// + /// + /// + internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType) : + this(wrapper, property, field, propertyType, AttributeHelper.GetModifiersAttribute(property).Modifiers, MemberFlags.AccessStub) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + private CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType, Modifiers modifiers, MemberFlags flags) : + base(wrapper, propertyType, property.Name, propertyType.SigName, modifiers, field, flags) + { + this.getter = property.GetGetMethod(true); + this.setter = property.GetSetMethod(true); + } + +#if EMITTERS + + protected override void EmitGetImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, getter); + } + + protected override void EmitSetImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, setter); + } + +#endif + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + internal override object GetValue(object obj) + { + // we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field + return GetField().GetValue(obj); + } + + internal override void SetValue(object obj, object value) + { + // we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field + GetField().SetValue(obj, value); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/CompiledPropertyFieldWrapper.cs b/src/IKVM.Runtime/CompiledPropertyFieldWrapper.cs new file mode 100644 index 0000000000..bc7e26a78b --- /dev/null +++ b/src/IKVM.Runtime/CompiledPropertyFieldWrapper.cs @@ -0,0 +1,134 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + /// + /// Represents a .NET property defined in Java with the 'ikvm.lang.Property' annotation. + /// + sealed class CompiledPropertyFieldWrapper : FieldWrapper + { + + readonly PropertyInfo property; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal CompiledPropertyFieldWrapper(TypeWrapper declaringType, PropertyInfo property, ExModifiers modifiers) : + base(declaringType, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType), property.Name, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType).SigName, modifiers, null) + { + this.property = property; + } + + internal PropertyInfo GetProperty() + { + return property; + } + +#if EMITTERS + + protected override void EmitGetImpl(CodeEmitter ilgen) + { + var getter = property.GetGetMethod(true); + if (getter == null) + DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForGetter(ilgen, FieldTypeWrapper, this); + else if (getter.IsStatic) + ilgen.Emit(OpCodes.Call, getter); + else + ilgen.Emit(OpCodes.Callvirt, getter); + } + + protected override void EmitSetImpl(CodeEmitter ilgen) + { + var setter = property.GetSetMethod(true); + if (setter == null) + { + if (IsFinal) + { + ilgen.Emit(OpCodes.Pop); + if (IsStatic == false) + { + ilgen.Emit(OpCodes.Pop); + } + } + else + { + DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForSetter(ilgen, this); + } + } + else if (setter.IsStatic) + { + ilgen.Emit(OpCodes.Call, setter); + } + else + { + ilgen.Emit(OpCodes.Callvirt, setter); + } + } + +#endif + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + internal override object GetValue(object obj) + { + var getter = property.GetGetMethod(true); + if (getter == null) + throw new java.lang.NoSuchMethodError(); + + return getter.Invoke(obj, new object[0]); + } + + internal override void SetValue(object obj, object value) + { + var setter = property.GetSetMethod(true); + if (setter == null) + throw new java.lang.NoSuchMethodError(); + + setter.Invoke(obj, new object[] { value }); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/CompiledTypeWrapper.cs b/src/IKVM.Runtime/CompiledTypeWrapper.cs new file mode 100644 index 0000000000..ffe612c3c8 --- /dev/null +++ b/src/IKVM.Runtime/CompiledTypeWrapper.cs @@ -0,0 +1,1686 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using IKVM.Attributes; +using IKVM.Runtime; +using IKVM.Runtime.Syntax; +using IKVM.ByteCode; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + class CompiledTypeWrapper : TypeWrapper + { + + readonly Type type; + + TypeWrapper baseTypeWrapper = VerifierTypeWrapper.Null; + volatile TypeWrapper[] interfaces; + MethodInfo clinitMethod; + volatile bool clinitMethodSet; + Modifiers reflectiveModifiers; + + internal static CompiledTypeWrapper newInstance(string name, Type type) + { + // TODO since ghost and remapped types can only exist in the core library assembly, we probably + // should be able to remove the Type.IsDefined() tests in most cases + if (type.IsValueType && AttributeHelper.IsGhostInterface(type)) + { + return new CompiledGhostTypeWrapper(name, type); + } + else if (AttributeHelper.IsRemappedType(type)) + { + return new CompiledRemappedTypeWrapper(name, type); + } + else + { + return new CompiledTypeWrapper(name, type); + } + } + + private sealed class CompiledRemappedTypeWrapper : CompiledTypeWrapper + { + + readonly Type remappedType; + + internal CompiledRemappedTypeWrapper(string name, Type type) + : base(name, type) + { + RemappedTypeAttribute attr = AttributeHelper.GetRemappedType(type); + if (attr == null) + { + throw new InvalidOperationException(); + } + remappedType = attr.Type; + } + + internal override Type TypeAsTBD + { + get + { + return remappedType; + } + } + + internal override bool IsRemapped + { + get + { + return true; + } + } + + protected override void LazyPublishMethods() + { + List list = new List(); + const BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + foreach (ConstructorInfo ctor in type.GetConstructors(bindingFlags)) + { + AddMethod(list, ctor); + } + foreach (MethodInfo method in type.GetMethods(bindingFlags)) + { + AddMethod(list, method); + } + // if we're a remapped interface, we need to get the methods from the real interface + if (remappedType.IsInterface) + { + Type nestedHelper = type.GetNestedType("__Helper", BindingFlags.Public | BindingFlags.Static); + foreach (RemappedInterfaceMethodAttribute m in AttributeHelper.GetRemappedInterfaceMethods(type)) + { + MethodInfo method = remappedType.GetMethod(m.MappedTo); + MethodInfo mbHelper = method; + ExModifiers modifiers = AttributeHelper.GetModifiers(method, false); + string name; + string sig; + TypeWrapper retType; + TypeWrapper[] paramTypes; + MemberFlags flags = MemberFlags.None; + GetNameSigFromMethodBase(method, out name, out sig, out retType, out paramTypes, ref flags); + if (nestedHelper != null) + { + mbHelper = nestedHelper.GetMethod(m.Name); + if (mbHelper == null) + { + mbHelper = method; + } + } + MethodWrapper mw = new CompiledRemappedMethodWrapper(this, m.Name, sig, method, retType, paramTypes, modifiers, false, mbHelper, null); + mw.SetDeclaredExceptions(m.Throws); + list.Add(mw); + } + } + SetMethods(list.ToArray()); + } + + private void AddMethod(List list, MethodBase method) + { + HideFromJavaFlags flags = AttributeHelper.GetHideFromJavaFlags(method); + if ((flags & HideFromJavaFlags.Code) == 0 + && (remappedType.IsSealed || !method.Name.StartsWith("instancehelper_")) + && (!remappedType.IsSealed || method.IsStatic)) + { + list.Add(CreateRemappedMethodWrapper(method, flags)); + } + } + + protected override void LazyPublishFields() + { + List list = new List(); + FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + foreach (FieldInfo field in fields) + { + HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(field); + if ((hideFromJavaFlags & HideFromJavaFlags.Code) == 0) + { + list.Add(CreateFieldWrapper(field, hideFromJavaFlags)); + } + } + SetFields(list.ToArray()); + } + + private MethodWrapper CreateRemappedMethodWrapper(MethodBase mb, HideFromJavaFlags hideFromJavaflags) + { + ExModifiers modifiers = AttributeHelper.GetModifiers(mb, false); + string name; + string sig; + TypeWrapper retType; + TypeWrapper[] paramTypes; + MemberFlags flags = MemberFlags.None; + GetNameSigFromMethodBase(mb, out name, out sig, out retType, out paramTypes, ref flags); + MethodInfo mbHelper = mb as MethodInfo; + bool hideFromReflection = mbHelper != null && (hideFromJavaflags & HideFromJavaFlags.Reflection) != 0; + MethodInfo mbNonvirtualHelper = null; + if (!mb.IsStatic && !mb.IsConstructor) + { + ParameterInfo[] parameters = mb.GetParameters(); + Type[] argTypes = new Type[parameters.Length + 1]; + argTypes[0] = remappedType; + for (int i = 0; i < parameters.Length; i++) + { + argTypes[i + 1] = parameters[i].ParameterType; + } + MethodInfo helper = type.GetMethod("instancehelper_" + mb.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, argTypes, null); + if (helper != null) + { + mbHelper = helper; + } + mbNonvirtualHelper = type.GetMethod("nonvirtualhelper/" + mb.Name, BindingFlags.NonPublic | BindingFlags.Static, null, argTypes, null); + } + return new CompiledRemappedMethodWrapper(this, name, sig, mb, retType, paramTypes, modifiers, hideFromReflection, mbHelper, mbNonvirtualHelper); + } + } + + sealed class CompiledGhostTypeWrapper : CompiledTypeWrapper + { + + volatile FieldInfo ghostRefField; + volatile Type typeAsBaseType; + + /// + /// Initializes a new instance. + /// + /// + /// + internal CompiledGhostTypeWrapper(string name, Type type) : + base(name, type) + { + + } + + internal override Type TypeAsBaseType + { + get + { + if (typeAsBaseType == null) + typeAsBaseType = type.GetNestedType("__Interface"); + + return typeAsBaseType; + } + } + + internal override FieldInfo GhostRefField + { + get + { + if (ghostRefField == null) + ghostRefField = type.GetField("__"); + + return ghostRefField; + } + } + + internal override bool IsGhost => true; + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + internal override object GhostWrap(object obj) + { + return type.GetMethod("Cast").Invoke(null, new object[] { obj }); + } + + internal override object GhostUnwrap(object obj) + { + return type.GetMethod("ToObject").Invoke(obj, new object[0]); + } + +#endif + + } + + /// + /// Gets the Java type name of a Java type. + /// + /// + /// + /// + internal static JavaTypeName? GetName(Type type) + { + if (type.HasElementType) + return null; + if (type.IsGenericType) + return null; + if (AttributeHelper.IsJavaModule(type.Module) == false) + return null; + + // look for our custom attribute, that contains the real name of the type (for inner classes) + var attr = AttributeHelper.GetInnerClass(type); + if (attr != null) + { + var name = attr.InnerClassName; + if (name != null) + return name; + } + + // type is an inner type + if (type.DeclaringType != null) + return GetName(type.DeclaringType) + "$" + TypeNameUtil.Unescape(type.Name); + + return TypeNameUtil.Unescape(type.FullName); + } + + static TypeWrapper GetBaseTypeWrapper(Type type) + { + if (type.IsInterface || AttributeHelper.IsGhostInterface(type)) + { + return null; + } + else if (type.BaseType == null) + { + // System.Object must appear to be derived from java.lang.Object + return CoreClasses.java.lang.Object.Wrapper; + } + else + { + var attr = AttributeHelper.GetRemappedType(type); + if (attr != null) + { + if (attr.Type == Types.Object) + return null; + else + return CoreClasses.java.lang.Object.Wrapper; + } + else if (ClassLoaderWrapper.IsRemappedType(type.BaseType)) + { + // if we directly extend System.Object or System.Exception, the base class must be cli.System.Object or cli.System.Exception + return DotNetTypeWrapper.GetWrapperFromDotNetType(type.BaseType); + } + + TypeWrapper tw = null; + while (tw == null) + { + type = type.BaseType; + tw = ClassLoaderWrapper.GetWrapperFromType(type); + } + + return tw; + } + } + + /// + /// Initializes a new instance. + /// + /// + /// + CompiledTypeWrapper(ExModifiers exmod, string name) : + base(exmod.IsInternal ? TypeFlags.InternalAccess : TypeFlags.None, exmod.Modifiers, name) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + CompiledTypeWrapper(string name, Type type) : + this(GetModifiers(type), name) + { + Debug.Assert(!(type is TypeBuilder)); + Debug.Assert(!type.Name.EndsWith("[]")); + + this.type = type; + } + + internal override TypeWrapper BaseTypeWrapper + { + get + { + if (baseTypeWrapper != VerifierTypeWrapper.Null) + return baseTypeWrapper; + + return baseTypeWrapper = GetBaseTypeWrapper(type); + } + } + + internal override ClassLoaderWrapper GetClassLoader() + { + return AssemblyClassLoader.FromAssembly(type.Assembly); + } + + private static ExModifiers GetModifiers(Type type) + { + ModifiersAttribute attr = AttributeHelper.GetModifiersAttribute(type); + if (attr != null) + { + return new ExModifiers(attr.Modifiers, attr.IsInternal); + } + // only returns public, protected, private, final, static, abstract and interface (as per + // the documentation of Class.getModifiers()) + Modifiers modifiers = 0; + if (type.IsPublic || type.IsNestedPublic) + { + modifiers |= Modifiers.Public; + } + if (type.IsSealed) + { + modifiers |= Modifiers.Final; + } + if (type.IsAbstract) + { + modifiers |= Modifiers.Abstract; + } + if (type.IsInterface) + { + modifiers |= Modifiers.Interface; + } + else + { + modifiers |= Modifiers.Super; + } + + return new ExModifiers(modifiers, false); + } + + internal override bool HasStaticInitializer + { + get + { + if (!clinitMethodSet) + { + try + { + clinitMethod = type.GetMethod("__", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } +#if IMPORTER + catch (IKVM.Reflection.MissingMemberException) { } +#endif + finally { } + clinitMethodSet = true; + } + return clinitMethod != null; + } + } + + internal override TypeWrapper[] Interfaces + { + get + { + if (interfaces == null) + { + interfaces = GetInterfaces(); + } + return interfaces; + } + } + + private TypeWrapper[] GetInterfaces() + { + // NOTE instead of getting the interfaces list from Type, we use a custom + // attribute to list the implemented interfaces, because Java reflection only + // reports the interfaces *directly* implemented by the type, not the inherited + // interfaces. This is significant for serialVersionUID calculation (for example). + ImplementsAttribute attr = AttributeHelper.GetImplements(type); + if (attr == null) + { + if (BaseTypeWrapper == CoreClasses.java.lang.Object.Wrapper) + { + return GetImplementedInterfacesAsTypeWrappers(type); + } + return TypeWrapper.EmptyArray; + } + string[] interfaceNames = attr.Interfaces; + TypeWrapper[] interfaceWrappers = new TypeWrapper[interfaceNames.Length]; + if (this.IsRemapped) + { + for (int i = 0; i < interfaceWrappers.Length; i++) + { + interfaceWrappers[i] = ClassLoaderWrapper.LoadClassCritical(interfaceNames[i]); + } + } + else + { + TypeWrapper[] typeWrappers = GetImplementedInterfacesAsTypeWrappers(type); + for (int i = 0; i < interfaceWrappers.Length; i++) + { + for (int j = 0; j < typeWrappers.Length; j++) + { + if (typeWrappers[j].Name == interfaceNames[i]) + { + interfaceWrappers[i] = typeWrappers[j]; + break; + } + } + if (interfaceWrappers[i] == null) + { +#if IMPORTER + throw new FatalCompilerErrorException(Message.UnableToResolveInterface, interfaceNames[i], this); +#else + throw new InternalException($"Unable to resolve interface {interfaceNames[i]} on type {this}"); +#endif + } + } + } + return interfaceWrappers; + } + + private static bool IsNestedTypeAnonymousOrLocalClass(Type type) + { + switch (type.Attributes & (TypeAttributes.SpecialName | TypeAttributes.VisibilityMask)) + { + case TypeAttributes.SpecialName | TypeAttributes.NestedPublic: + case TypeAttributes.SpecialName | TypeAttributes.NestedAssembly: + return AttributeHelper.HasEnclosingMethodAttribute(type); + default: + return false; + } + } + + private static bool IsAnnotationAttribute(Type type) + { + return type.Name.EndsWith("Attribute", StringComparison.Ordinal) + && type.IsClass + && type.BaseType.FullName == "ikvm.internal.AnnotationAttributeBase"; + } + + internal override TypeWrapper[] InnerClasses + { + get + { + List wrappers = new List(); + foreach (Type nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if (IsAnnotationAttribute(nested)) + { + // HACK it's the custom attribute we generated for a corresponding annotation, so we shouldn't surface it as an inner classes + // (we can't put a HideFromJavaAttribute on it, because we do want the class to be visible as a $Proxy) + } + else if (IsNestedTypeAnonymousOrLocalClass(nested)) + { + // anonymous and local classes are not reported as inner classes + } + else if (AttributeHelper.IsHideFromJava(nested)) + { + // ignore + } + else + { + wrappers.Add(ClassLoaderWrapper.GetWrapperFromType(nested)); + } + } + foreach (string s in AttributeHelper.GetNonNestedInnerClasses(type)) + { + wrappers.Add(GetClassLoader().LoadClassByDottedName(s)); + } + return wrappers.ToArray(); + } + } + + internal override TypeWrapper DeclaringTypeWrapper + { + get + { + if (IsNestedTypeAnonymousOrLocalClass(type)) + { + return null; + } + Type declaringType = type.DeclaringType; + if (declaringType != null) + { + return ClassLoaderWrapper.GetWrapperFromType(declaringType); + } + string decl = AttributeHelper.GetNonNestedOuterClasses(type); + if (decl != null) + { + return GetClassLoader().LoadClassByDottedName(decl); + } + return null; + } + } + + // returns true iff name is of the form "...$" + private static bool IsAnonymousClassName(string name) + { + int index = name.LastIndexOf('$') + 1; + if (index > 1 && index < name.Length) + { + while (index < name.Length) + { + if ("0123456789".IndexOf(name[index++]) == -1) + { + return false; + } + } + return true; + } + return false; + } + + // This method uses some heuristics to predict the reflective modifiers and if the prediction matches + // we can avoid storing the InnerClassesAttribute to record the modifiers. + // The heuristics are based on javac from Java 7. + internal static Modifiers PredictReflectiveModifiers(TypeWrapper tw) + { + Modifiers modifiers = Modifiers.Static | (tw.Modifiers & (Modifiers.Public | Modifiers.Abstract | Modifiers.Interface)); + // javac marks anonymous classes as final, but the InnerClasses attribute access_flags does not have the ACC_FINAL flag set + if (tw.IsFinal && !IsAnonymousClassName(tw.Name)) + { + modifiers |= Modifiers.Final; + } + // javac uses the this$0 field to store the outer instance reference for non-static inner classes + foreach (FieldWrapper fw in tw.GetFields()) + { + if (fw.Name == "this$0") + { + modifiers &= ~Modifiers.Static; + break; + } + } + return modifiers; + } + + internal override Modifiers ReflectiveModifiers + { + get + { + if (reflectiveModifiers == 0) + { + Modifiers mods; + InnerClassAttribute attr = AttributeHelper.GetInnerClass(type); + if (attr != null) + { + // the mask comes from RECOGNIZED_INNER_CLASS_MODIFIERS in src/hotspot/share/vm/classfile/classFileParser.cpp + // (minus ACC_SUPER) + mods = attr.Modifiers & (Modifiers)0x761F; + } + else if (type.DeclaringType != null) + { + mods = PredictReflectiveModifiers(this); + } + else + { + // the mask comes from JVM_RECOGNIZED_CLASS_MODIFIERS in src/hotspot/share/vm/prims/jvm.h + // (minus ACC_SUPER) + mods = Modifiers & (Modifiers)0x7611; + } + if (IsInterface) + { + mods |= Modifiers.Abstract; + } + reflectiveModifiers = mods; + } + return reflectiveModifiers; + } + } + + internal override Type TypeAsBaseType + { + get + { + return type; + } + } + + private void SigTypePatchUp(string sigtype, ref TypeWrapper type) + { + if (sigtype != type.SigName) + { + // if type is an array, we know that it is a ghost array, because arrays of unloadable are compiled + // as object (not as arrays of object) + if (type.IsArray) + { + type = GetClassLoader().FieldTypeWrapperFromSig(sigtype, LoadMode.LoadOrThrow); + } + else if (type.IsPrimitive) + { + type = DotNetTypeWrapper.GetWrapperFromDotNetType(type.TypeAsTBD); + if (sigtype != type.SigName) + { + throw new InvalidOperationException(); + } + } + else if (type.IsNonPrimitiveValueType) + { + // this can't happen and even if it does happen we cannot return + // UnloadableTypeWrapper because that would result in incorrect code + // being generated + throw new InvalidOperationException(); + } + else + { + if (sigtype[0] == 'L') + { + sigtype = sigtype.Substring(1, sigtype.Length - 2); + } + try + { + TypeWrapper tw = GetClassLoader().LoadClassByDottedNameFast(sigtype); + if (tw != null && tw.IsRemapped) + { + type = tw; + return; + } + } + catch (RetargetableJavaException) + { + } + type = new UnloadableTypeWrapper(sigtype); + } + } + } + + private static void ParseSig(string sig, out string[] sigparam, out string sigret) + { + List list = new List(); + int pos = 1; + for (; ; ) + { + switch (sig[pos]) + { + case 'L': + { + int end = sig.IndexOf(';', pos) + 1; + list.Add(sig.Substring(pos, end - pos)); + pos = end; + break; + } + case '[': + { + int skip = 1; + while (sig[pos + skip] == '[') skip++; + if (sig[pos + skip] == 'L') + { + int end = sig.IndexOf(';', pos) + 1; + list.Add(sig.Substring(pos, end - pos)); + pos = end; + } + else + { + skip++; + list.Add(sig.Substring(pos, skip)); + pos += skip; + } + break; + } + case ')': + sigparam = list.ToArray(); + sigret = sig.Substring(pos + 1); + return; + default: + list.Add(sig.Substring(pos, 1)); + pos++; + break; + } + } + } + + private static bool IsCallerID(Type type) + { +#if EXPORTER + return type.FullName == "ikvm.internal.CallerID"; +#else + return type == CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType; +#endif + } + + private static bool IsCallerSensitive(MethodBase mb) + { +#if FIRST_PASS + return false; +#elif IMPORTER || EXPORTER + foreach (CustomAttributeData cad in mb.GetCustomAttributesData()) + { + if (cad.AttributeType.FullName == "sun.reflect.CallerSensitiveAttribute") + { + return true; + } + } + return false; +#else + return mb.IsDefined(typeof(global::sun.reflect.CallerSensitiveAttribute), false); +#endif + } + + private void GetNameSigFromMethodBase(MethodBase method, out string name, out string sig, out TypeWrapper retType, out TypeWrapper[] paramTypes, ref MemberFlags flags) + { + retType = method is ConstructorInfo ? PrimitiveTypeWrapper.VOID : GetParameterTypeWrapper(((MethodInfo)method).ReturnParameter); + ParameterInfo[] parameters = method.GetParameters(); + int len = parameters.Length; + if (len > 0 + && IsCallerID(parameters[len - 1].ParameterType) + && GetClassLoader() == ClassLoaderWrapper.GetBootstrapClassLoader() + && IsCallerSensitive(method)) + { + len--; + flags |= MemberFlags.CallerID; + } + paramTypes = new TypeWrapper[len]; + for (int i = 0; i < len; i++) + { + paramTypes[i] = GetParameterTypeWrapper(parameters[i]); + } + NameSigAttribute attr = AttributeHelper.GetNameSig(method); + if (attr != null) + { + name = attr.Name; + sig = attr.Sig; + string[] sigparams; + string sigret; + ParseSig(sig, out sigparams, out sigret); + // HACK newhelper methods have a return type, but it should be void + if (name == "") + { + retType = PrimitiveTypeWrapper.VOID; + } + SigTypePatchUp(sigret, ref retType); + // if we have a remapped method, the paramTypes array contains an additional entry for "this" so we have + // to remove that + if (paramTypes.Length == sigparams.Length + 1) + { + paramTypes = ArrayUtil.DropFirst(paramTypes); + } + Debug.Assert(sigparams.Length == paramTypes.Length); + for (int i = 0; i < sigparams.Length; i++) + { + SigTypePatchUp(sigparams[i], ref paramTypes[i]); + } + } + else + { + if (method is ConstructorInfo) + { + name = method.IsStatic ? "" : ""; + } + else + { + name = method.Name; + if (name.StartsWith(NamePrefix.Bridge, StringComparison.Ordinal)) + { + name = name.Substring(NamePrefix.Bridge.Length); + } + if (method.IsSpecialName) + { + name = UnicodeUtil.UnescapeInvalidSurrogates(name); + } + } + if (method.IsSpecialName && method.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal)) + { + paramTypes = ArrayUtil.DropFirst(paramTypes); + } + System.Text.StringBuilder sb = new System.Text.StringBuilder("("); + foreach (TypeWrapper tw in paramTypes) + { + sb.Append(tw.SigName); + } + sb.Append(")"); + sb.Append(retType.SigName); + sig = sb.ToString(); + } + } + + private sealed class DelegateConstructorMethodWrapper : MethodWrapper + { + private readonly ConstructorInfo constructor; + private MethodInfo invoke; + + private DelegateConstructorMethodWrapper(TypeWrapper tw, TypeWrapper iface, ExModifiers mods) + : base(tw, StringConstants.INIT, "(" + iface.SigName + ")V", null, PrimitiveTypeWrapper.VOID, new TypeWrapper[] { iface }, mods.Modifiers, mods.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None) + { + } + + internal DelegateConstructorMethodWrapper(TypeWrapper tw, MethodBase method) + : this(tw, tw.GetClassLoader().LoadClassByDottedName(tw.Name + DotNetTypeWrapper.DelegateInterfaceSuffix), AttributeHelper.GetModifiers(method, false)) + { + constructor = (ConstructorInfo)method; + } + + protected override void DoLinkMethod() + { + MethodWrapper mw = GetParameters()[0].GetMethods()[0]; + mw.Link(); + invoke = (MethodInfo)mw.GetMethod(); + } + +#if EMITTERS + internal override void EmitNewobj(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Ldvirtftn, invoke); + ilgen.Emit(OpCodes.Newobj, constructor); + } +#endif // EMITTERS + } + + protected override void LazyPublishMethods() + { + bool isDelegate = type.BaseType == Types.MulticastDelegate; + List methods = new List(); + const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + foreach (ConstructorInfo ctor in type.GetConstructors(flags)) + { + HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(ctor); + if (isDelegate && !ctor.IsStatic && (hideFromJavaFlags & HideFromJavaFlags.Code) == 0) + { + methods.Add(new DelegateConstructorMethodWrapper(this, ctor)); + } + else + { + AddMethodOrConstructor(ctor, hideFromJavaFlags, methods); + } + } + AddMethods(type.GetMethods(flags), methods); + if (type.IsInterface && (type.IsPublic || type.IsNestedPublic)) + { + Type privateInterfaceMethods = type.GetNestedType(NestedTypeName.PrivateInterfaceMethods, BindingFlags.NonPublic); + if (privateInterfaceMethods != null) + { + AddMethods(privateInterfaceMethods.GetMethods(flags), methods); + } + } + SetMethods(methods.ToArray()); + } + + private void AddMethods(MethodInfo[] add, List methods) + { + foreach (MethodInfo method in add) + { + AddMethodOrConstructor(method, AttributeHelper.GetHideFromJavaFlags(method), methods); + } + } + + private void AddMethodOrConstructor(MethodBase method, HideFromJavaFlags hideFromJavaFlags, List methods) + { + if ((hideFromJavaFlags & HideFromJavaFlags.Code) != 0) + { + if (method.Name.StartsWith(NamePrefix.Incomplete, StringComparison.Ordinal)) + { + SetHasIncompleteInterfaceImplementation(); + } + } + else + { + if (method.IsSpecialName && (method.Name.StartsWith("__<", StringComparison.Ordinal) || method.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal))) + { + // skip + } + else + { + string name; + string sig; + TypeWrapper retType; + TypeWrapper[] paramTypes; + MethodInfo mi = method as MethodInfo; + bool hideFromReflection = mi != null && (hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0; + MemberFlags flags = hideFromReflection ? MemberFlags.HideFromReflection : MemberFlags.None; + GetNameSigFromMethodBase(method, out name, out sig, out retType, out paramTypes, ref flags); + ExModifiers mods = AttributeHelper.GetModifiers(method, false); + if (mods.IsInternal) + { + flags |= MemberFlags.InternalAccess; + } + if (hideFromReflection && name.StartsWith(NamePrefix.AccessStub, StringComparison.Ordinal)) + { + int id = Int32.Parse(name.Substring(NamePrefix.AccessStub.Length, name.IndexOf('|', NamePrefix.AccessStub.Length) - NamePrefix.AccessStub.Length)); + name = name.Substring(name.IndexOf('|', NamePrefix.AccessStub.Length) + 1); + flags |= MemberFlags.AccessStub; + MethodInfo nonvirt = type.GetMethod(NamePrefix.NonVirtual + id, BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance); + methods.Add(new AccessStubMethodWrapper(this, name, sig, mi, mi, nonvirt ?? mi, retType, paramTypes, mods.Modifiers & ~Modifiers.Final, flags)); + return; + } + MethodInfo impl; + MethodWrapper mw; + if (IsGhost && (mods.Modifiers & (Modifiers.Static | Modifiers.Private)) == 0) + { + Type[] types = new Type[paramTypes.Length]; + for (int i = 0; i < types.Length; i++) + { + types[i] = paramTypes[i].TypeAsSignatureType; + } + MethodInfo ifmethod = TypeAsBaseType.GetMethod(method.Name, types); + mw = new GhostMethodWrapper(this, name, sig, ifmethod, (MethodInfo)method, retType, paramTypes, mods.Modifiers, flags); + if (!mw.IsAbstract) + { + ((GhostMethodWrapper)mw).SetDefaultImpl(TypeAsSignatureType.GetMethod(NamePrefix.DefaultMethod + method.Name, types)); + } + } + else if (method.IsSpecialName && method.Name.StartsWith(NamePrefix.PrivateInterfaceInstanceMethod, StringComparison.Ordinal)) + { + mw = new PrivateInterfaceMethodWrapper(this, name, sig, method, retType, paramTypes, mods.Modifiers, flags); + } + else if (IsInterface && method.IsAbstract && (mods.Modifiers & Modifiers.Abstract) == 0 && (impl = GetDefaultInterfaceMethodImpl(mi, sig)) != null) + { + mw = new DefaultInterfaceMethodWrapper(this, name, sig, mi, impl, retType, paramTypes, mods.Modifiers, flags); + } + else + { + mw = new TypicalMethodWrapper(this, name, sig, method, retType, paramTypes, mods.Modifiers, flags); + } + if (mw.HasNonPublicTypeInSignature) + { + if (mi != null) + { + MethodInfo stubVirt; + MethodInfo stubNonVirt; + if (GetType2AccessStubs(name, sig, out stubVirt, out stubNonVirt)) + { + mw = new AccessStubMethodWrapper(this, name, sig, mi, stubVirt, stubNonVirt ?? stubVirt, retType, paramTypes, mw.Modifiers, flags); + } + } + else + { + ConstructorInfo stub; + if (GetType2AccessStub(sig, out stub)) + { + mw = new AccessStubConstructorMethodWrapper(this, sig, (ConstructorInfo)method, stub, paramTypes, mw.Modifiers, flags); + } + } + } + methods.Add(mw); + } + } + } + + private MethodInfo GetDefaultInterfaceMethodImpl(MethodInfo method, string expectedSig) + { + foreach (MethodInfo candidate in method.DeclaringType.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)) + { + if (candidate.IsSpecialName + && candidate.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal) + && candidate.Name.Length == method.Name.Length + NamePrefix.DefaultMethod.Length + && candidate.Name.EndsWith(method.Name, StringComparison.Ordinal)) + { + string name; + string sig; + TypeWrapper retType; + TypeWrapper[] paramTypes; + MemberFlags flags = MemberFlags.None; + GetNameSigFromMethodBase(candidate, out name, out sig, out retType, out paramTypes, ref flags); + if (sig == expectedSig) + { + return candidate; + } + } + } + return null; + } + + private bool GetType2AccessStubs(string name, string sig, out MethodInfo stubVirt, out MethodInfo stubNonVirt) + { + stubVirt = null; + stubNonVirt = null; + const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + foreach (MethodInfo method in type.GetMethods(flags)) + { + if (AttributeHelper.IsHideFromJava(method)) + { + NameSigAttribute attr = AttributeHelper.GetNameSig(method); + if (attr != null && attr.Name == name && attr.Sig == sig) + { + if (method.Name.StartsWith(NamePrefix.NonVirtual, StringComparison.Ordinal)) + { + stubNonVirt = method; + } + else + { + stubVirt = method; + } + } + } + } + return stubVirt != null; + } + + private bool GetType2AccessStub(string sig, out ConstructorInfo stub) + { + stub = null; + const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + foreach (ConstructorInfo ctor in type.GetConstructors(flags)) + { + if (AttributeHelper.IsHideFromJava(ctor)) + { + NameSigAttribute attr = AttributeHelper.GetNameSig(ctor); + if (attr != null && attr.Sig == sig) + { + stub = ctor; + } + } + } + return stub != null; + } + + private static int SortFieldByToken(FieldInfo field1, FieldInfo field2) + { + return field1.MetadataToken.CompareTo(field2.MetadataToken); + } + + protected override void LazyPublishFields() + { + List fields = new List(); + const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + FieldInfo[] rawfields = type.GetFields(flags); + Array.Sort(rawfields, SortFieldByToken); + // FXBUG on .NET 3.5 and Mono Type.GetProperties() will not return "duplicate" properties (i.e. that have the same name and type, but differ in custom modifiers). + // .NET 4.0 works as expected. We don't have a workaround, because that would require name mangling again and this situation is very unlikely anyway. + PropertyInfo[] properties = type.GetProperties(flags); + foreach (FieldInfo field in rawfields) + { + HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(field); + if ((hideFromJavaFlags & HideFromJavaFlags.Code) != 0) + { + if (field.Name.StartsWith(NamePrefix.Type2AccessStubBackingField, StringComparison.Ordinal)) + { + TypeWrapper tw = GetFieldTypeWrapper(field); + string name = field.Name.Substring(NamePrefix.Type2AccessStubBackingField.Length); + for (int i = 0; i < properties.Length; i++) + { + if (properties[i] != null + && name == properties[i].Name + && MatchTypes(tw, GetPropertyTypeWrapper(properties[i]))) + { + fields.Add(new CompiledAccessStubFieldWrapper(this, properties[i], field, tw)); + properties[i] = null; + break; + } + } + } + } + else + { + if (field.IsSpecialName && field.Name.StartsWith("__<", StringComparison.Ordinal)) + { + // skip + } + else + { + fields.Add(CreateFieldWrapper(field, hideFromJavaFlags)); + } + } + } + foreach (PropertyInfo property in properties) + { + if (property != null) + { + AddPropertyFieldWrapper(fields, property, null); + } + } + SetFields(fields.ToArray()); + } + + private static bool MatchTypes(TypeWrapper tw1, TypeWrapper tw2) + { + return tw1 == tw2 || (tw1.IsUnloadable && tw2.IsUnloadable && tw1.Name == tw2.Name); + } + + private void AddPropertyFieldWrapper(List fields, PropertyInfo property, FieldInfo field) + { + // NOTE explictly defined properties (in map.xml) are decorated with HideFromJava, + // so we don't need to worry about them here + HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(property); + if ((hideFromJavaFlags & HideFromJavaFlags.Code) == 0) + { + // is it a type 1 access stub? + if ((hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0) + { + fields.Add(new CompiledAccessStubFieldWrapper(this, property, GetPropertyTypeWrapper(property))); + } + else + { + // It must be an explicit property + // (defined in Java source by an @ikvm.lang.Property annotation) + ModifiersAttribute mods = AttributeHelper.GetModifiersAttribute(property); + fields.Add(new CompiledPropertyFieldWrapper(this, property, new ExModifiers(mods.Modifiers, mods.IsInternal))); + } + } + } + + private sealed class CompiledRemappedMethodWrapper : SmartMethodWrapper + { + private readonly MethodInfo mbHelper; +#if !IMPORTER + private readonly MethodInfo mbNonvirtualHelper; +#endif + + internal CompiledRemappedMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, ExModifiers modifiers, bool hideFromReflection, MethodInfo mbHelper, MethodInfo mbNonvirtualHelper) + : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers.Modifiers, + (modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None) | (hideFromReflection ? MemberFlags.HideFromReflection : MemberFlags.None)) + { + this.mbHelper = mbHelper; +#if !IMPORTER + this.mbNonvirtualHelper = mbNonvirtualHelper; +#endif + } + +#if EMITTERS + protected override void CallImpl(CodeEmitter ilgen) + { + MethodBase mb = GetMethod(); + MethodInfo mi = mb as MethodInfo; + if (mi != null) + { + if (!IsStatic && IsFinal) + { + // When calling a final instance method on a remapped type from a class derived from a .NET class (i.e. a cli.System.Object or cli.System.Exception derived base class) + // then we can't call the java.lang.Object or java.lang.Throwable methods and we have to go through the instancehelper_ method. Note that since the method + // is final, this won't affect the semantics. + CallvirtImpl(ilgen); + } + else + { + ilgen.Emit(OpCodes.Call, mi); + } + } + else + { + ilgen.Emit(OpCodes.Call, mb); + } + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + Debug.Assert(!mbHelper.IsStatic || mbHelper.Name.StartsWith("instancehelper_") || mbHelper.DeclaringType.Name == "__Helper"); + if (mbHelper.IsPublic) + { + ilgen.Emit(mbHelper.IsStatic ? OpCodes.Call : OpCodes.Callvirt, mbHelper); + } + else + { + // HACK the helper is not public, this means that we're dealing with finalize or clone + ilgen.Emit(OpCodes.Callvirt, GetMethod()); + } + } + + protected override void NewobjImpl(CodeEmitter ilgen) + { + MethodBase mb = GetMethod(); + MethodInfo mi = mb as MethodInfo; + if (mi != null) + { + Debug.Assert(mi.Name == "newhelper"); + ilgen.Emit(OpCodes.Call, mi); + } + else + { + ilgen.Emit(OpCodes.Newobj, mb); + } + } +#endif // EMITTERS + +#if !IMPORTER && !FIRST_PASS && !EXPORTER + [HideFromJava] + internal override object Invoke(object obj, object[] args) + { + MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); + if (mb.IsStatic && !IsStatic) + { + args = ArrayUtil.Concat(obj, args); + obj = null; + } + return InvokeAndUnwrapException(mb, obj, args); + } + + [HideFromJava] + internal override object CreateInstance(object[] args) + { + MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); + if (mb.IsStatic) + { + return InvokeAndUnwrapException(mb, null, args); + } + return base.CreateInstance(args); + } + + [HideFromJava] + internal override object InvokeNonvirtualRemapped(object obj, object[] args) + { + MethodInfo mi = mbNonvirtualHelper; + if (mi == null) + { + mi = mbHelper; + } + return mi.Invoke(null, ArrayUtil.Concat(obj, args)); + } +#endif // !IMPORTER && !FIRST_PASS && !EXPORTER + +#if EMITTERS + internal override void EmitCallvirtReflect(CodeEmitter ilgen) + { + MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); + ilgen.Emit(mb.IsStatic ? OpCodes.Call : OpCodes.Callvirt, mb); + } +#endif // EMITTERS + + internal string GetGenericSignature() + { + SignatureAttribute attr = AttributeHelper.GetSignature(mbHelper != null ? mbHelper : GetMethod()); + if (attr != null) + { + return attr.Signature; + } + return null; + } + } + + private static TypeWrapper TypeWrapperFromModOpt(Type[] modopt) + { + int rank = 0; + TypeWrapper tw = null; + foreach (Type type in modopt) + { + if (type == JVM.LoadType(typeof(IKVM.Attributes.AccessStub))) + { + // ignore + } + else if (type == Types.Array) + { + rank++; + } + else if (type == Types.Void || type.IsPrimitive || ClassLoaderWrapper.IsRemappedType(type)) + { + tw = DotNetTypeWrapper.GetWrapperFromDotNetType(type); + } + else if (type.DeclaringType != null && type.DeclaringType.FullName == UnloadableTypeWrapper.ContainerTypeName) + { + tw = new UnloadableTypeWrapper(TypeNameUtil.UnmangleNestedTypeName(type.Name), type); + } + else + { + tw = ClassLoaderWrapper.GetWrapperFromType(type); + } + } + if (rank != 0) + { + tw = tw.MakeArrayType(rank); + } + return tw; + } + + private static TypeWrapper GetPropertyTypeWrapper(PropertyInfo property) + { + return TypeWrapperFromModOpt(property.GetOptionalCustomModifiers()) + ?? ClassLoaderWrapper.GetWrapperFromType(property.PropertyType); + } + + internal static TypeWrapper GetFieldTypeWrapper(FieldInfo field) + { + return TypeWrapperFromModOpt(field.GetOptionalCustomModifiers()) + ?? ClassLoaderWrapper.GetWrapperFromType(field.FieldType); + } + + internal static TypeWrapper GetParameterTypeWrapper(ParameterInfo param) + { + TypeWrapper tw = TypeWrapperFromModOpt(param.GetOptionalCustomModifiers()); + if (tw != null) + { + return tw; + } + Type parameterType = param.ParameterType; + if (parameterType.IsByRef) + { + // we only support ByRef parameters for automatically generated delegate invoke stubs + parameterType = parameterType.GetElementType().MakeArrayType(); + } + return ClassLoaderWrapper.GetWrapperFromType(parameterType); + } + + private FieldWrapper CreateFieldWrapper(FieldInfo field, HideFromJavaFlags hideFromJavaFlags) + { + ExModifiers modifiers = AttributeHelper.GetModifiers(field, false); + TypeWrapper type = GetFieldTypeWrapper(field); + string name = field.Name; + + if (field.IsSpecialName) + { + name = UnicodeUtil.UnescapeInvalidSurrogates(name); + } + + if (field.IsLiteral) + { + MemberFlags flags = MemberFlags.None; + if ((hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0) + { + flags |= MemberFlags.HideFromReflection; + } + if (modifiers.IsInternal) + { + flags |= MemberFlags.InternalAccess; + } + return new ConstantFieldWrapper(this, type, name, type.SigName, modifiers.Modifiers, field, null, flags); + } + else + { + return FieldWrapper.Create(this, type, field, name, type.SigName, modifiers); + } + } + + internal override Type TypeAsTBD + { + get + { + return type; + } + } + + internal override bool IsMapUnsafeException + { + get + { + return AttributeHelper.IsExceptionIsUnsafeForMapping(type); + } + } + +#if EMITTERS + internal override void EmitRunClassConstructor(CodeEmitter ilgen) + { + if (HasStaticInitializer) + { + ilgen.Emit(OpCodes.Call, clinitMethod); + } + } +#endif // EMITTERS + + internal override string GetGenericSignature() + { + SignatureAttribute attr = AttributeHelper.GetSignature(type); + if (attr != null) + { + return attr.Signature; + } + return null; + } + + internal override string GetGenericMethodSignature(MethodWrapper mw) + { + if (mw is CompiledRemappedMethodWrapper) + { + return ((CompiledRemappedMethodWrapper)mw).GetGenericSignature(); + } + MethodBase mb = mw.GetMethod(); + if (mb != null) + { + SignatureAttribute attr = AttributeHelper.GetSignature(mb); + if (attr != null) + { + return attr.Signature; + } + } + return null; + } + + internal override string GetGenericFieldSignature(FieldWrapper fw) + { + FieldInfo fi = fw.GetField(); + if (fi != null) + { + SignatureAttribute attr = AttributeHelper.GetSignature(fi); + if (attr != null) + { + return attr.Signature; + } + } + return null; + } + + internal override MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) + { + MethodBase mb = mw.GetMethod(); + if (mb == null) + { + // delegate constructor + return null; + } + MethodParametersAttribute attr = AttributeHelper.GetMethodParameters(mb); + if (attr == null) + { + return null; + } + if (attr.IsMalformed) + { + return MethodParametersEntry.Malformed; + } + ParameterInfo[] parameters = mb.GetParameters(); + MethodParametersEntry[] mp = new MethodParametersEntry[attr.Modifiers.Length]; + for (int i = 0; i < mp.Length; i++) + { + mp[i].name = i < parameters.Length ? parameters[i].Name : null; + mp[i].accessFlags = (AccessFlag)attr.Modifiers[i]; + } + + return mp; + } + +#if !IMPORTER && !EXPORTER + internal override string[] GetEnclosingMethod() + { + EnclosingMethodAttribute enc = AttributeHelper.GetEnclosingMethodAttribute(type); + if (enc != null) + { + return new string[] { enc.ClassName, enc.MethodName, enc.MethodSignature }; + } + return null; + } + + internal override object[] GetDeclaredAnnotations() + { + return type.GetCustomAttributes(false); + } + + internal override object[] GetMethodAnnotations(MethodWrapper mw) + { + MethodBase mb = mw.GetMethod(); + if (mb == null) + { + // delegate constructor + return null; + } + return mb.GetCustomAttributes(false); + } + + internal override object[][] GetParameterAnnotations(MethodWrapper mw) + { + MethodBase mb = mw.GetMethod(); + if (mb == null) + { + // delegate constructor + return null; + } + ParameterInfo[] parameters = mb.GetParameters(); + int skip = 0; + if (mb.IsStatic && !mw.IsStatic && mw.Name != "") + { + skip = 1; + } + int skipEnd = 0; + if (mw.HasCallerID) + { + skipEnd = 1; + } + object[][] attribs = new object[parameters.Length - skip - skipEnd][]; + for (int i = skip; i < parameters.Length - skipEnd; i++) + { + attribs[i - skip] = parameters[i].GetCustomAttributes(false); + } + return attribs; + } + + internal override object[] GetFieldAnnotations(FieldWrapper fw) + { + FieldInfo field = fw.GetField(); + if (field != null) + { + return field.GetCustomAttributes(false); + } + CompiledPropertyFieldWrapper prop = fw as CompiledPropertyFieldWrapper; + if (prop != null) + { + return prop.GetProperty().GetCustomAttributes(false); + } + return new object[0]; + } +#endif + + internal sealed class CompiledAnnotation : Annotation + { + private readonly ConstructorInfo constructor; + + internal CompiledAnnotation(Type type) + { + constructor = type.GetConstructor(new Type[] { JVM.Import(typeof(object[])) }); + } + + private CustomAttributeBuilder MakeCustomAttributeBuilder(ClassLoaderWrapper loader, object annotation) + { + return new CustomAttributeBuilder(constructor, new object[] { AnnotationDefaultAttribute.Escape(QualifyClassNames(loader, annotation)) }); + } + + internal override void Apply(ClassLoaderWrapper loader, TypeBuilder tb, object annotation) + { + tb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override void Apply(ClassLoaderWrapper loader, MethodBuilder mb, object annotation) + { + mb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation) + { + fb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation) + { + pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation) + { + ab.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override void Apply(ClassLoaderWrapper loader, PropertyBuilder pb, object annotation) + { + pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } + + internal override bool IsCustomAttribute + { + get { return false; } + } + } + + internal override Annotation Annotation + { + get + { + string annotationAttribute = AttributeHelper.GetAnnotationAttributeType(type); + if (annotationAttribute != null) + { + return new CompiledAnnotation(type.Assembly.GetType(annotationAttribute, true)); + } + return null; + } + } + + internal override Type EnumType + { + get + { + if ((this.Modifiers & Modifiers.Enum) != 0) + { + return type.GetNestedType("__Enum"); + } + return null; + } + } + +#if !IMPORTER && !EXPORTER + internal override string GetSourceFileName() + { + object[] attr = type.GetCustomAttributes(typeof(SourceFileAttribute), false); + if (attr.Length == 1) + { + return ((SourceFileAttribute)attr[0]).SourceFile; + } + if (DeclaringTypeWrapper != null) + { + return DeclaringTypeWrapper.GetSourceFileName(); + } + if (IsNestedTypeAnonymousOrLocalClass(type)) + { + return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).GetSourceFileName(); + } + if (type.Module.IsDefined(typeof(SourceFileAttribute), false)) + { + return type.Name + ".java"; + } + return null; + } + + internal override int GetSourceLineNumber(MethodBase mb, int ilOffset) + { + object[] attr = mb.GetCustomAttributes(typeof(LineNumberTableAttribute), false); + if (attr.Length == 1) + { + return ((LineNumberTableAttribute)attr[0]).GetLineNumber(ilOffset); + } + return -1; + } +#endif + + internal override bool IsFastClassLiteralSafe + { + get { return true; } + } + + internal override object[] GetConstantPool() + { + return AttributeHelper.GetConstantPool(type); + } + + internal override byte[] GetRawTypeAnnotations() + { + return AttributeHelper.GetRuntimeVisibleTypeAnnotations(type); + } + + internal override byte[] GetMethodRawTypeAnnotations(MethodWrapper mw) + { + MethodBase mb = mw.GetMethod(); + return mb == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(mb); + } + + internal override byte[] GetFieldRawTypeAnnotations(FieldWrapper fw) + { + FieldInfo fi = fw.GetField(); + return fi == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(fi); + } + } + +} diff --git a/src/IKVM.Runtime/ConstantFieldWrapper.cs b/src/IKVM.Runtime/ConstantFieldWrapper.cs new file mode 100644 index 0000000000..ff12655d96 --- /dev/null +++ b/src/IKVM.Runtime/ConstantFieldWrapper.cs @@ -0,0 +1,169 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Attributes; +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + /// + /// Wraps a const field. + /// + sealed class ConstantFieldWrapper : FieldWrapper + { + + // NOTE this field wrapper can represent a .NET enum, but in that case "constant" contains the raw constant value (i.e. the boxed underlying primitive value, not a boxed enum) + + /// + /// Reference to the constant. In the case of a primitive type or enum, this is the boxed underlying value. + /// + object constant; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal ConstantFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, object constant, MemberFlags flags) : + base(declaringType, fieldType, name, sig, modifiers, field, flags) + { + if (IsStatic == false) + throw new InternalException(); + + Debug.Assert(constant == null || constant.GetType().IsPrimitive || constant is string); + this.constant = constant; + } + + internal object GetConstantValue() + { + if (constant == null) + { + var field = GetField(); + constant = field.GetRawConstantValue(); + } + + return constant; + } + +#if EMITTERS + + protected override void EmitGetImpl(CodeEmitter ilgen) + { + // Reading a field should trigger the cctor, but since we're inlining the value + // we have to trigger it explicitly + DeclaringType.EmitRunClassConstructor(ilgen); + + // NOTE even though you're not supposed to access a constant static final (the compiler is supposed + // to inline them), we have to support it (because it does happen, e.g. if the field becomes final + // after the referencing class was compiled, or when we're accessing an unsigned primitive .NET field) + var v = GetConstantValue(); + if (v == null) + { + ilgen.Emit(OpCodes.Ldnull); + } + else if (constant is int || constant is short || constant is ushort || constant is byte || constant is sbyte || constant is char || constant is bool) + { + ilgen.EmitLdc_I4(((IConvertible)constant).ToInt32(null)); + } + else if (constant is string) + { + ilgen.Emit(OpCodes.Ldstr, (string)constant); + } + else if (constant is float) + { + ilgen.EmitLdc_R4((float)constant); + } + else if (constant is double) + { + ilgen.EmitLdc_R8((double)constant); + } + else if (constant is long) + { + ilgen.EmitLdc_I8((long)constant); + } + else if (constant is uint) + { + ilgen.EmitLdc_I4(unchecked((int)((IConvertible)constant).ToUInt32(null))); + } + else if (constant is ulong) + { + ilgen.EmitLdc_I8(unchecked((long)(ulong)constant)); + } + else + { + throw new InvalidOperationException(constant.GetType().FullName); + } + } + + protected override void EmitSetImpl(CodeEmitter ilgen) + { + // when constant static final fields are updated, the JIT normally doesn't see that (because the + // constant value is inlined), so we emulate that behavior by emitting a Pop + ilgen.Emit(OpCodes.Pop); + } + +#endif + +#if !EXPORTER && !IMPORTER && !FIRST_PASS + + internal override object GetValue(object obj) + { + var field = GetField(); + return FieldTypeWrapper.IsPrimitive || field == null ? GetConstantValue() : field.GetValue(null); + } + + internal override void SetValue(object obj, object value) + { + + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/CoreClasses.cs b/src/IKVM.Runtime/CoreClasses.cs index 8f555a3494..fa7f736c94 100644 --- a/src/IKVM.Runtime/CoreClasses.cs +++ b/src/IKVM.Runtime/CoreClasses.cs @@ -26,10 +26,13 @@ namespace IKVM.Internal { internal static class CoreClasses { + internal static class cli { + internal static class System { + internal static class Object { // NOTE we have a dummy static initializer, to make sure we don't get the beforeFieldInit attribute @@ -45,13 +48,17 @@ internal static class Exception static Exception() { } internal static readonly TypeWrapper Wrapper = DotNetTypeWrapper.GetWrapperFromDotNetType(Types.Exception); } + } + } internal static class ikvm { + internal static class @internal { + internal static class CallerID { // NOTE we have a dummy static initializer, to make sure we don't get the beforeFieldInit attribute @@ -59,7 +66,9 @@ internal static class CallerID static CallerID() { } internal static readonly TypeWrapper Wrapper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.CallerID"); } + } + } internal static class java diff --git a/src/IKVM.Runtime/DefaultInterfaceMethodWrapper.cs b/src/IKVM.Runtime/DefaultInterfaceMethodWrapper.cs new file mode 100644 index 0000000000..f4b65d185b --- /dev/null +++ b/src/IKVM.Runtime/DefaultInterfaceMethodWrapper.cs @@ -0,0 +1,96 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace IKVM.Internal +{ + + sealed class DefaultInterfaceMethodWrapper : SmartMethodWrapper + { + + MethodInfo impl; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal DefaultInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo ifmethod, MethodInfo impl, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, ifmethod, returnType, parameterTypes, modifiers, flags) + { + this.impl = impl; + } + + internal static MethodInfo GetImpl(MethodWrapper mw) + { + var dimw = mw as DefaultInterfaceMethodWrapper; + if (dimw != null) + return dimw.impl; + else + return ((GhostMethodWrapper)mw).GetDefaultImpl(); + } + + internal static void SetImpl(MethodWrapper mw, MethodInfo impl) + { + var dimw = mw as DefaultInterfaceMethodWrapper; + if (dimw != null) + dimw.impl = impl; + else + ((GhostMethodWrapper)mw).SetDefaultImpl(impl); + } + +#if EMITTERS + + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, impl); + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Callvirt, GetMethod()); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/DotNetTypeWrapper.cs b/src/IKVM.Runtime/DotNetTypeWrapper.cs index b317919258..4a7fdd819c 100644 --- a/src/IKVM.Runtime/DotNetTypeWrapper.cs +++ b/src/IKVM.Runtime/DotNetTypeWrapper.cs @@ -24,13 +24,11 @@ Jeroen Frijters using System; using System.Collections.Generic; using System.Diagnostics; -using System.Security; -using System.Security.Permissions; using IKVM.Attributes; using IKVM.Runtime; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; @@ -40,8 +38,8 @@ Jeroen Frijters using System.Reflection.Emit; #endif -#if STATIC_COMPILER -using ikvmc; +#if IMPORTER +using IKVM.Tools.Importer; #endif namespace IKVM.Internal @@ -277,7 +275,7 @@ internal static bool IsAllowedOutside(Type type) // SECURITY we never expose types from IKVM.Runtime, because doing so would lead to a security hole, // since the reflection implementation lives inside this assembly, all internal members would // be accessible through Java reflection. -#if !FIRST_PASS && !STATIC_COMPILER && !STUB_GENERATOR +#if !FIRST_PASS && !IMPORTER && !EXPORTER if (type.Assembly == typeof(DotNetTypeWrapper).Assembly) { return false; @@ -373,8 +371,8 @@ private sealed class DelegateInnerClassTypeWrapper : FakeTypeWrapper internal DelegateInnerClassTypeWrapper(string name, Type delegateType) : base(Modifiers.Public | Modifiers.Interface | Modifiers.Abstract, name, null) { -#if STATIC_COMPILER || STUB_GENERATOR - this.fakeType = FakeTypes.GetDelegateType(delegateType); +#if IMPORTER || EXPORTER + this.fakeType = FakeTypes.GetDelegateType(delegateType); #elif !FIRST_PASS this.fakeType = typeof(ikvm.@internal.DelegateInterface<>).MakeGenericType(delegateType); #endif @@ -448,7 +446,7 @@ internal sealed override bool IsDynamicOnly } } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER [HideFromJava] internal sealed override object Invoke(object obj, object[] args) @@ -468,47 +466,63 @@ internal sealed override object Invoke(object obj, object[] args) return mw.Invoke(obj, args); } -#endif // !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#endif // !IMPORTER && !FIRST_PASS && !EXPORTER } private sealed class EnumEnumTypeWrapper : FakeTypeWrapper { - private readonly Type fakeType; - internal EnumEnumTypeWrapper(string name, Type enumType) - : base(Modifiers.Public | Modifiers.Enum | Modifiers.Final, name, ClassLoaderWrapper.LoadClassCritical("java.lang.Enum")) + readonly Type fakeType; + + /// + /// Initializes a new instance. + /// + /// + /// + internal EnumEnumTypeWrapper(string name, Type enumType) : + base(Modifiers.Public | Modifiers.Enum | Modifiers.Final, name, ClassLoaderWrapper.LoadClassCritical("java.lang.Enum")) { -#if STATIC_COMPILER || STUB_GENERATOR - this.fakeType = FakeTypes.GetEnumType(enumType); +#if IMPORTER || EXPORTER + this.fakeType = FakeTypes.GetEnumType(enumType); #elif !FIRST_PASS this.fakeType = typeof(ikvm.@internal.EnumEnum<>).MakeGenericType(enumType); #endif } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS internal object GetUnspecifiedValue() { return GetFieldWrapper("__unspecified", this.SigName).GetValue(null); } #endif - private sealed class EnumFieldWrapper : FieldWrapper + sealed class EnumFieldWrapper : FieldWrapper { -#if !STATIC_COMPILER && !STUB_GENERATOR - private readonly int ordinal; - private object val; + +#if !IMPORTER && !EXPORTER + + readonly int ordinal; + object val; + #endif - internal EnumFieldWrapper(TypeWrapper tw, string name, int ordinal) - : base(tw, tw, name, tw.SigName, Modifiers.Public | Modifiers.Static | Modifiers.Final | Modifiers.Enum, null, MemberFlags.None) + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal EnumFieldWrapper(TypeWrapper tw, string name, int ordinal) : + base(tw, tw, name, tw.SigName, Modifiers.Public | Modifiers.Static | Modifiers.Final | Modifiers.Enum, null, MemberFlags.None) { -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER this.ordinal = ordinal; #endif } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS + internal override object GetValue(object obj) { if (val == null) @@ -520,16 +534,18 @@ internal override object GetValue(object obj) internal override void SetValue(object obj, object value) { + } + #endif #if EMITTERS protected override void EmitGetImpl(CodeEmitter ilgen) { -#if STATIC_COMPILER - Type typeofByteCodeHelper = StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper"); +#if IMPORTER + var typeofByteCodeHelper = StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper"); #else - Type typeofByteCodeHelper = typeof(IKVM.Runtime.ByteCodeHelper); + var typeofByteCodeHelper = typeof(IKVM.Runtime.ByteCodeHelper); #endif ilgen.Emit(OpCodes.Ldstr, this.Name); ilgen.Emit(OpCodes.Call, typeofByteCodeHelper.GetMethod("GetDotNetEnumField").MakeGenericMethod(this.DeclaringType.TypeAsBaseType)); @@ -537,26 +553,26 @@ protected override void EmitGetImpl(CodeEmitter ilgen) protected override void EmitSetImpl(CodeEmitter ilgen) { + throw new NotImplementedException(); } -#endif // EMITTERS + +#endif + } private sealed class EnumValuesMethodWrapper : MethodWrapper { - internal EnumValuesMethodWrapper(TypeWrapper declaringType) - : base(declaringType, "values", "()[" + declaringType.SigName, null, declaringType.MakeArrayType(1), TypeWrapper.EmptyArray, Modifiers.Public | Modifiers.Static, MemberFlags.None) - { - } - internal override bool IsDynamicOnly + internal EnumValuesMethodWrapper(TypeWrapper declaringType) : + base(declaringType, "values", "()[" + declaringType.SigName, null, declaringType.MakeArrayType(1), TypeWrapper.EmptyArray, Modifiers.Public | Modifiers.Static, MemberFlags.None) { - get - { - return true; - } + } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR + internal override bool IsDynamicOnly => true; + +#if !IMPORTER && !FIRST_PASS && !EXPORTER + internal override object Invoke(object obj, object[] args) { FieldWrapper[] values = this.DeclaringType.GetFields(); @@ -567,25 +583,24 @@ internal override object Invoke(object obj, object[] args) } return array; } -#endif // !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR + +#endif + } private sealed class EnumValueOfMethodWrapper : MethodWrapper { - internal EnumValueOfMethodWrapper(TypeWrapper declaringType) - : base(declaringType, "valueOf", "(Ljava.lang.String;)" + declaringType.SigName, null, declaringType, new TypeWrapper[] { CoreClasses.java.lang.String.Wrapper }, Modifiers.Public | Modifiers.Static, MemberFlags.None) - { - } - internal override bool IsDynamicOnly + internal EnumValueOfMethodWrapper(TypeWrapper declaringType) : + base(declaringType, "valueOf", "(Ljava.lang.String;)" + declaringType.SigName, null, declaringType, new TypeWrapper[] { CoreClasses.java.lang.String.Wrapper }, Modifiers.Public | Modifiers.Static, MemberFlags.None) { - get - { - return true; - } + } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR + internal override bool IsDynamicOnly => true; + +#if !IMPORTER && !FIRST_PASS && !EXPORTER + internal override object Invoke(object obj, object[] args) { FieldWrapper[] values = this.DeclaringType.GetFields(); @@ -598,7 +613,9 @@ internal override object Invoke(object obj, object[] args) } throw new java.lang.IllegalArgumentException("" + args[0]); } -#endif // !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR + +#endif + } protected override void LazyPublishMembers() @@ -683,8 +700,8 @@ private sealed class AttributeAnnotationTypeWrapper : AttributeAnnotationTypeWra internal AttributeAnnotationTypeWrapper(string name, Type attributeType) : base(name) { -#if STATIC_COMPILER || STUB_GENERATOR - this.fakeType = FakeTypes.GetAttributeType(attributeType); +#if IMPORTER || EXPORTER + this.fakeType = FakeTypes.GetAttributeType(attributeType); #elif !FIRST_PASS this.fakeType = typeof(ikvm.@internal.AttributeAnnotation<>).MakeGenericType(attributeType); #endif @@ -927,7 +944,7 @@ private static void AddMethodIfUnique(List methods, MethodWrapper methods.Add(method); } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override object GetAnnotationDefault(MethodWrapper mw) { if (mw.IsOptionalAttributeAnnotationValue) @@ -984,7 +1001,7 @@ internal override object GetAnnotationDefault(MethodWrapper mw) } return null; } -#endif // !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#endif // !IMPORTER && !FIRST_PASS && !EXPORTER internal override TypeWrapper DeclaringTypeWrapper { @@ -1010,8 +1027,8 @@ private sealed class ReturnValueAnnotationTypeWrapper : AttributeAnnotationTypeW internal ReturnValueAnnotationTypeWrapper(AttributeAnnotationTypeWrapper declaringType) : base(declaringType.Name + AttributeAnnotationReturnValueSuffix) { -#if STATIC_COMPILER || STUB_GENERATOR - this.fakeType = FakeTypes.GetAttributeReturnValueType(declaringType.attributeType); +#if IMPORTER || EXPORTER + this.fakeType = FakeTypes.GetAttributeReturnValueType(declaringType.attributeType); #elif !FIRST_PASS this.fakeType = typeof(ikvm.@internal.AttributeAnnotationReturnValue<>).MakeGenericType(declaringType.attributeType); #endif @@ -1045,7 +1062,7 @@ internal override Type TypeAsTBD } } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override object[] GetDeclaredAnnotations() { java.util.HashMap targetMap = new java.util.HashMap(); @@ -1150,8 +1167,8 @@ private sealed class MultipleAnnotationTypeWrapper : AttributeAnnotationTypeWrap internal MultipleAnnotationTypeWrapper(AttributeAnnotationTypeWrapper declaringType) : base(declaringType.Name + AttributeAnnotationMultipleSuffix) { -#if STATIC_COMPILER || STUB_GENERATOR - this.fakeType = FakeTypes.GetAttributeMultipleType(declaringType.attributeType); +#if IMPORTER || EXPORTER + this.fakeType = FakeTypes.GetAttributeMultipleType(declaringType.attributeType); #elif !FIRST_PASS this.fakeType = typeof(ikvm.@internal.AttributeAnnotationMultiple<>).MakeGenericType(declaringType.attributeType); #endif @@ -1181,7 +1198,7 @@ internal override Type TypeAsTBD } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal override object[] GetDeclaredAnnotations() { return declaringType.GetDeclaredAnnotations(); @@ -1355,7 +1372,7 @@ private AttributeUsageAttribute GetAttributeUsage() return attr; } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override object[] GetDeclaredAnnotations() { // note that AttributeUsageAttribute.Inherited does not map to java.lang.annotation.Inherited @@ -1456,166 +1473,100 @@ internal override void Apply(ClassLoaderWrapper loader, TypeBuilder tb, object a { // we have to handle this explicitly, because if we apply an illegal StructLayoutAttribute, // TypeBuilder.CreateType() will later on throw an exception. -#if STATIC_COMPILER - loader.IssueMessage(Message.IgnoredCustomAttribute, type.FullName, "Type '" + tb.FullName + "' does not extend cli.System.Object"); +#if IMPORTER + loader.IssueMessage(Message.IgnoredCustomAttribute, type.FullName, "Type '" + tb.FullName + "' does not extend cli.System.Object"); #else Tracer.Error(Tracer.Runtime, "StructLayoutAttribute cannot be applied to {0}, because it does not directly extend cli.System.Object", tb.FullName); #endif return; } - if (type.IsSubclassOf(Types.SecurityAttribute)) - { -#if STATIC_COMPILER - tb.__AddDeclarativeSecurity(MakeCustomAttributeBuilder(loader, annotation)); -#elif STUB_GENERATOR -#else - -#if NETFRAMEWORK - SecurityAction action; - PermissionSet permSet; - if (MakeDeclSecurity(type, annotation, out action, out permSet)) - { - tb.AddDeclarativeSecurity(action, permSet); - } -#endif -#endif - } - else - { - tb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + tb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); } internal override void Apply(ClassLoaderWrapper loader, MethodBuilder mb, object annotation) { - if (type.IsSubclassOf(Types.SecurityAttribute)) - { -#if STATIC_COMPILER - mb.__AddDeclarativeSecurity(MakeCustomAttributeBuilder(loader, annotation)); -#elif STUB_GENERATOR -#else + mb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } -#if NETFRAMEWORK - SecurityAction action; - PermissionSet permSet; - if (MakeDeclSecurity(type, annotation, out action, out permSet)) - { - mb.AddDeclarativeSecurity(action, permSet); - } -#endif + internal override void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation) + { + fb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + } -#endif - } + internal override void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation) + { + // TODO with the current custom attribute annotation restrictions it is impossible to use this CA, + // but if we make it possible, we should also implement it here + if (type == JVM.Import(typeof(System.Runtime.InteropServices.DefaultParameterValueAttribute))) + throw new NotImplementedException(); else - { - mb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); } - internal override void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation) + internal override void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation) { - if (type.IsSubclassOf(Types.SecurityAttribute)) +#if IMPORTER + if (type == JVM.Import(typeof(System.Runtime.CompilerServices.TypeForwardedToAttribute))) { - // you can't add declarative security to a field + ab.__AddTypeForwarder((Type)ConvertValue(loader, Types.Type, ((object[])annotation)[3])); } - else + else if (type == JVM.Import(typeof(System.Reflection.AssemblyVersionAttribute))) { - fb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + string str = (string)ConvertValue(loader, Types.String, ((object[])annotation)[3]); + Version version; + if (IkvmImporterInternal.TryParseVersion(str, out version)) + { + ab.__SetAssemblyVersion(version); + } + else + { + loader.IssueMessage(Message.InvalidCustomAttribute, type.FullName, "The version '" + str + "' is invalid."); + } } - } - - internal override void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation) - { - if (type.IsSubclassOf(Types.SecurityAttribute)) + else if (type == JVM.Import(typeof(System.Reflection.AssemblyCultureAttribute))) { - // you can't add declarative security to a parameter + string str = (string)ConvertValue(loader, Types.String, ((object[])annotation)[3]); + if (str != "") + { + ab.__SetAssemblyCulture(str); + } } - else if (type == JVM.Import(typeof(System.Runtime.InteropServices.DefaultParameterValueAttribute))) + else if (type == JVM.Import(typeof(System.Reflection.AssemblyDelaySignAttribute)) + || type == JVM.Import(typeof(System.Reflection.AssemblyKeyFileAttribute)) + || type == JVM.Import(typeof(System.Reflection.AssemblyKeyNameAttribute))) { - // TODO with the current custom attribute annotation restrictions it is impossible to use this CA, - // but if we make it possible, we should also implement it here - throw new NotImplementedException(); + loader.IssueMessage(Message.IgnoredCustomAttribute, type.FullName, "Please use the corresponding compiler switch."); } - else + else if (type == JVM.Import(typeof(System.Reflection.AssemblyAlgorithmIdAttribute))) { - pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + // this attribute is currently not exposed as an annotation and isn't very interesting + throw new NotImplementedException(); } - } - - internal override void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation) - { - if (type.IsSubclassOf(Types.SecurityAttribute)) + else if (type == JVM.Import(typeof(System.Reflection.AssemblyFlagsAttribute))) { -#if STATIC_COMPILER - ab.__AddDeclarativeSecurity(MakeCustomAttributeBuilder(loader, annotation)); -#endif + // this attribute is currently not exposed as an annotation and isn't very interesting + throw new NotImplementedException(); } -#if STATIC_COMPILER - else if (type == JVM.Import(typeof(System.Runtime.CompilerServices.TypeForwardedToAttribute))) - { - ab.__AddTypeForwarder((Type)ConvertValue(loader, Types.Type, ((object[])annotation)[3])); - } - else if (type == JVM.Import(typeof(System.Reflection.AssemblyVersionAttribute))) - { - string str = (string)ConvertValue(loader, Types.String, ((object[])annotation)[3]); - Version version; - if (IkvmcCompiler.TryParseVersion(str, out version)) - { - ab.__SetAssemblyVersion(version); - } - else - { - loader.IssueMessage(Message.InvalidCustomAttribute, type.FullName, "The version '" + str + "' is invalid."); - } - } - else if (type == JVM.Import(typeof(System.Reflection.AssemblyCultureAttribute))) - { - string str = (string)ConvertValue(loader, Types.String, ((object[])annotation)[3]); - if (str != "") - { - ab.__SetAssemblyCulture(str); - } - } - else if (type == JVM.Import(typeof(System.Reflection.AssemblyDelaySignAttribute)) - || type == JVM.Import(typeof(System.Reflection.AssemblyKeyFileAttribute)) - || type == JVM.Import(typeof(System.Reflection.AssemblyKeyNameAttribute))) - { - loader.IssueMessage(Message.IgnoredCustomAttribute, type.FullName, "Please use the corresponding compiler switch."); - } - else if (type == JVM.Import(typeof(System.Reflection.AssemblyAlgorithmIdAttribute))) - { - // this attribute is currently not exposed as an annotation and isn't very interesting - throw new NotImplementedException(); - } - else if (type == JVM.Import(typeof(System.Reflection.AssemblyFlagsAttribute))) - { - // this attribute is currently not exposed as an annotation and isn't very interesting - throw new NotImplementedException(); - } -#endif else { ab.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); } +#else + ab.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); +#endif } internal override void Apply(ClassLoaderWrapper loader, PropertyBuilder pb, object annotation) { - if (type.IsSubclassOf(Types.SecurityAttribute)) - { - // you can't add declarative security to a property - } - else - { - pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); } internal override bool IsCustomAttribute { get { return true; } } + } internal override Annotation Annotation @@ -1818,7 +1769,7 @@ internal override void EmitCall(CodeEmitter ilgen) private sealed class ByRefMethodWrapper : SmartMethodWrapper { -#if !STATIC_COMPILER +#if !IMPORTER private readonly bool[] byrefs; #endif private readonly Type[] args; @@ -1827,7 +1778,7 @@ internal ByRefMethodWrapper(Type[] args, bool[] byrefs, TypeWrapper declaringTyp : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, hideFromReflection ? MemberFlags.HideFromReflection : MemberFlags.None) { this.args = args; -#if !STATIC_COMPILER +#if !IMPORTER this.byrefs = byrefs; #endif } @@ -1893,7 +1844,7 @@ internal override void EmitCall(CodeEmitter ilgen) } #endif // EMITTERS -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS internal override object Invoke(object obj, object[] args) { return Enum.ToObject(DeclaringType.TypeAsTBD, args[0]); @@ -1903,15 +1854,22 @@ internal override object Invoke(object obj, object[] args) internal sealed class EnumValueFieldWrapper : FieldWrapper { - private readonly Type underlyingType; - internal EnumValueFieldWrapper(DotNetTypeWrapper tw, TypeWrapper fieldType) - : base(tw, fieldType, "Value", fieldType.SigName, new ExModifiers(Modifiers.Public | Modifiers.Final, false), null) + readonly Type underlyingType; + + /// + /// Initializes a new instance. + /// + /// + /// + internal EnumValueFieldWrapper(DotNetTypeWrapper tw, TypeWrapper fieldType) : + base(tw, fieldType, "Value", fieldType.SigName, new ExModifiers(Modifiers.Public | Modifiers.Final, false), null) { underlyingType = EnumHelper.GetUnderlyingType(tw.type); } #if EMITTERS + protected override void EmitGetImpl(CodeEmitter ilgen) { // NOTE if the reference on the stack is null, we *want* the NullReferenceException, so we don't use TypeWrapper.EmitUnbox @@ -1929,9 +1887,11 @@ protected override void EmitSetImpl(CodeEmitter ilgen) ilgen.Emit(OpCodes.Stobj, underlyingType); ilgen.ReleaseTempLocal(temp); } -#endif // EMITTERS -#if !STUB_GENERATOR && !STATIC_COMPILER && !FIRST_PASS +#endif + +#if !EXPORTER && !IMPORTER && !FIRST_PASS + internal override object GetValue(object obj) { return obj; @@ -1944,11 +1904,13 @@ internal override void SetValue(object obj, object value) #endif } - private sealed class ValueTypeDefaultCtor : MethodWrapper + sealed class ValueTypeDefaultCtor : MethodWrapper { + internal ValueTypeDefaultCtor(DotNetTypeWrapper tw) : base(tw, "", "()V", null, PrimitiveTypeWrapper.VOID, TypeWrapper.EmptyArray, Modifiers.Public, MemberFlags.None) { + } #if EMITTERS @@ -1965,7 +1927,7 @@ internal override void EmitCall(CodeEmitter ilgen) } #endif // EMITTERS -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS internal override object CreateInstance(object[] args) { return Activator.CreateInstance(DeclaringType.TypeAsTBD); @@ -2256,7 +2218,7 @@ protected override void LazyPublishMembers() } } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS // support serializing .NET exceptions (by replacing them with a placeholder exception) if (typeof(Exception).IsAssignableFrom(type) && !typeof(java.io.Serializable.__Interface).IsAssignableFrom(type) @@ -2264,7 +2226,7 @@ protected override void LazyPublishMembers() { methodsList.Add("writeReplace()Ljava.lang.Object;", new ExceptionWriteReplaceMethodWrapper(this)); } -#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#endif // !IMPORTER && !EXPORTER && !FIRST_PASS MethodWrapper[] methodArray = new MethodWrapper[methodsList.Count]; methodsList.Values.CopyTo(methodArray, 0); @@ -2272,7 +2234,7 @@ protected override void LazyPublishMembers() } } -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS private sealed class ExceptionWriteReplaceMethodWrapper : MethodWrapper { internal ExceptionWriteReplaceMethodWrapper(TypeWrapper declaringType) @@ -2295,7 +2257,7 @@ com.sun.xml.@internal.ws.developer.ServerSideException sse return sse; } } -#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#endif // !IMPORTER && !EXPORTER && !FIRST_PASS private void InterfaceMethodStubHelper(Dictionary methodsList, MethodBase method, string name, string sig, TypeWrapper[] args, TypeWrapper ret) { @@ -2378,8 +2340,8 @@ private static bool IsPointerType(Type type) } type = type.GetElementType(); } -#if STATIC_COMPILER || STUB_GENERATOR - return type.__IsFunctionPointer; +#if IMPORTER || EXPORTER + return type.__IsFunctionPointer; #else return false; #endif @@ -2752,7 +2714,7 @@ internal override MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) return mp; } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal override object[] GetDeclaredAnnotations() { return type.GetCustomAttributes(false); @@ -2800,7 +2762,7 @@ internal override bool IsFastClassLiteralSafe get { return type != Types.Void && !type.IsPrimitive && !IsRemapped; } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER // this override is only relevant for the runtime, because it handles the scenario // where classes are dynamically loaded by the assembly class loader // (i.e. injected into the assembly) diff --git a/src/IKVM.Runtime/DynamicClassLoader.cs b/src/IKVM.Runtime/DynamicClassLoader.cs index 5ac2779250..0f625c3ab9 100644 --- a/src/IKVM.Runtime/DynamicClassLoader.cs +++ b/src/IKVM.Runtime/DynamicClassLoader.cs @@ -26,11 +26,13 @@ Jeroen Frijters using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Serialization; -using System.Security.Cryptography; -#if STATIC_COMPILER +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; using Type = IKVM.Reflection.Type; using ProtectionDomain = System.Object; @@ -56,11 +58,11 @@ sealed class DynamicClassLoader : TypeWrapperFactory internal const string DynamicAssemblySuffixAndPublicKey = "-ikvm-runtime-injected"; #endif -#if !STATIC_COMPILER +#if !IMPORTER static AssemblyBuilder jniProxyAssemblyBuilder; #endif -#if STATIC_COMPILER || CLASSGC +#if IMPORTER || CLASSGC readonly Dictionary dynamicTypes = new Dictionary(); #else static readonly Dictionary dynamicTypes = new Dictionary(); @@ -68,7 +70,7 @@ sealed class DynamicClassLoader : TypeWrapperFactory readonly ModuleBuilder moduleBuilder; readonly bool hasInternalAccess; -#if STATIC_COMPILER +#if IMPORTER TypeBuilder proxiesContainer; List proxies; #endif @@ -77,7 +79,7 @@ sealed class DynamicClassLoader : TypeWrapperFactory TypeBuilder unloadableContainer; Type[] delegates; -#if !STATIC_COMPILER && !CLASSGC +#if !IMPORTER && !CLASSGC static DynamicClassLoader instance = new DynamicClassLoader(CreateModuleBuilder(), false); #endif @@ -91,7 +93,7 @@ sealed class DynamicClassLoader : TypeWrapperFactory [System.Security.SecuritySafeCritical] static DynamicClassLoader() { -#if !STATIC_COMPILER +#if !IMPORTER // TODO AppDomain.TypeResolve requires ControlAppDomain permission, but if we don't have that, // we should handle that by disabling dynamic class loading AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(OnTypeResolve); @@ -100,7 +102,7 @@ static DynamicClassLoader() // (since it already defines a pseudo-type named for global methods and fields) dynamicTypes.Add("", null); #endif // !CLASSGC -#endif // !STATIC_COMPILER +#endif // !IMPORTER } /// @@ -113,7 +115,7 @@ internal DynamicClassLoader(ModuleBuilder moduleBuilder, bool hasInternalAccess) this.moduleBuilder = moduleBuilder; this.hasInternalAccess = hasInternalAccess; -#if STATIC_COMPILER || CLASSGC +#if IMPORTER || CLASSGC // Ref.Emit doesn't like the "" name for types // (since it already defines a pseudo-type named for global methods and fields) dynamicTypes.Add("", null); @@ -135,7 +137,7 @@ internal override void AddInternalsVisibleTo(Assembly friend) } #endif -#if !STATIC_COMPILER +#if !IMPORTER static Assembly OnTypeResolve(object sender, ResolveEventArgs args) { #if CLASSGC @@ -213,7 +215,7 @@ internal static string TypeNameMangleImpl(Dictionary dict, // it loses the trailing period in the name that gets passed in the TypeResolve event. if (dict.ContainsKey(mangledTypeName) || mangledTypeName.EndsWith(".")) { -#if STATIC_COMPILER +#if IMPORTER Tracer.Warning(Tracer.Compiler, "Class name clash: {0}", mangledTypeName); #endif // Java class names cannot contain slashes (since they are converted into periods), @@ -231,7 +233,7 @@ internal static string TypeNameMangleImpl(Dictionary dict, internal sealed override TypeWrapper DefineClassImpl(Dictionary types, TypeWrapper host, ClassFile f, ClassLoaderWrapper classLoader, ProtectionDomain protectionDomain) { -#if STATIC_COMPILER +#if IMPORTER AotTypeWrapper type = new AotTypeWrapper(f, (CompilerClassLoader)classLoader); type.CreateStep1(); types[f.Name] = type; @@ -271,10 +273,10 @@ internal sealed override TypeWrapper DefineClassImpl(Dictionary + /// Provides utilities for working with dynamic methods. + /// + static class DynamicMethodUtil + { + + /// + /// Creates a new owned by the appropriate metadata element. + /// + /// + /// + /// + /// + /// + /// + [SecuritySafeCritical] + internal static DynamicMethod Create(string name, Type owner, bool nonPublic, Type returnType, Type[] paramTypes) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else +#if NETFRAMEWORK + return CreateFramework(name, owner, nonPublic, returnType, paramTypes); +#else + return CreateCore(name, owner, nonPublic, returnType, paramTypes); +#endif +#endif + } + + +#if NETFRAMEWORK + + static readonly Lazy dynamicModule = new Lazy(CreateDynamicModule, true); + + /// + /// Creates the used for dynamic methods. This module has to be security critical. + /// + /// + static Module CreateDynamicModule() + { + var name = RuntimeUtil.IsMono ? "__DynamicMethodModule" : "__"; + var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.RunAndCollect); + return dynamicAssembly.DefineDynamicModule(name); + } + + /// + /// Creates a new while targeting .NET Framework. + /// + /// + /// + /// + /// + /// + /// + static DynamicMethod CreateFramework(string name, Type owner, bool nonPublic, Type returnType, Type[] paramTypes) + { + try + { + return new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, paramTypes, dynamicModule.Value, true); + } + catch (SecurityException) + { + return new DynamicMethod(name, returnType, paramTypes, nonPublic); + } + } + +#else + + /// + /// Creates a new while targeting .NET Core. + /// + /// + /// + /// + /// + /// + /// + static DynamicMethod CreateCore(string name, Type owner, bool nonPublic, Type returnType, Type[] paramTypes) + { + if (ReflectUtil.CanOwnDynamicMethod(owner)) + return new DynamicMethod(name, returnType, paramTypes, owner); + else + return new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, paramTypes, owner.Module, true); + } + +#endif + + } + +#endif + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/DynamicMethodUtils.cs b/src/IKVM.Runtime/DynamicMethodUtils.cs deleted file mode 100644 index 88005503da..0000000000 --- a/src/IKVM.Runtime/DynamicMethodUtils.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - Copyright (C) 2007-2013 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Security; -using System.Security.Permissions; -using System.Threading; - -using IKVM.Internal; - -static class DynamicMethodUtils -{ - -#if NETFRAMEWORK - - private static Module dynamicModule; - -#endif - - [SecuritySafeCritical] - internal static DynamicMethod Create(string name, Type owner, bool nonPublic, Type returnType, Type[] paramTypes) - { - try - { -#if NETFRAMEWORK - if (dynamicModule == null) - { - // we have to create a module that is security critical to hold the dynamic method, if we want to be able to emit unverifiable code - AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(""), AssemblyBuilderAccess.RunAndCollect); - Interlocked.CompareExchange(ref dynamicModule, ab.DefineDynamicModule(""), null); - } - - return new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, paramTypes, dynamicModule, true); -#else - if (!ReflectUtil.CanOwnDynamicMethod(owner)) - { - // interfaces and arrays aren't allowed as owners of dynamic methods - return new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, paramTypes, owner.Module, true); - } - else - { - return new DynamicMethod(name, returnType, paramTypes, owner); - } -#endif - } - catch (SecurityException) - { - if (nonPublic && !RestrictedMemberAccess) - { - // we don't have RestrictedMemberAccess, so we stick the dynamic method in our module and hope for the best - // (i.e. that we're trying to access something with assembly access in an assembly that lets us) - return new DynamicMethod(name, returnType, paramTypes, typeof(DynamicMethodUtils).Module); - } - // apparently we don't have full trust, so we try again with .NET 2.0 SP1 method - // and we only request restrictSkipVisibility if it is required - return new DynamicMethod(name, returnType, paramTypes, nonPublic); - } - } - - static bool RestrictedMemberAccess - { - get - { - try - { - new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess).Demand(); - return true; - } - catch (SecurityException) - { - return false; - } - } - - } - -} \ No newline at end of file diff --git a/src/IKVM.Runtime/DynamicPropertyFieldWrapper.cs b/src/IKVM.Runtime/DynamicPropertyFieldWrapper.cs new file mode 100644 index 0000000000..6543bdd696 --- /dev/null +++ b/src/IKVM.Runtime/DynamicPropertyFieldWrapper.cs @@ -0,0 +1,224 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + +#if EXPORTER == false + + /// + /// Represents a .NET property defined in Java with the annotation. + /// + sealed class DynamicPropertyFieldWrapper : FieldWrapper + { + + readonly MethodWrapper getter; + readonly MethodWrapper setter; + PropertyBuilder pb; + + MethodWrapper GetMethod(string name, string sig, bool isstatic) + { + if (name != null) + { + var mw = DeclaringType.GetMethodWrapper(name, sig, false); + if (mw != null && mw.IsStatic == isstatic) + { + mw.IsPropertyAccessor = true; + return mw; + } + + Tracer.Error(Tracer.Compiler, "Property '{0}' accessor '{1}' not found in class '{2}'", this.Name, name, this.DeclaringType.Name); + } + + return null; + } + + internal DynamicPropertyFieldWrapper(TypeWrapper declaringType, ClassFile.Field fld) : + base(declaringType, null, fld.Name, fld.Signature, new ExModifiers(fld.Modifiers, fld.IsInternal), null) + { + getter = GetMethod(fld.PropertyGetter, "()" + fld.Signature, fld.IsStatic); + setter = GetMethod(fld.PropertySetter, "(" + fld.Signature + ")V", fld.IsStatic); + } + +#if !IMPORTER && !FIRST_PASS + + internal override void ResolveField() + { + getter?.ResolveMethod(); + setter?.ResolveMethod(); + } + +#endif + + internal PropertyBuilder GetPropertyBuilder() + { + AssertLinked(); + return pb; + } + + internal void DoLink(TypeBuilder tb) + { + if (getter != null) + { + getter.Link(); + } + if (setter != null) + { + setter.Link(); + } + pb = tb.DefineProperty(this.Name, PropertyAttributes.None, this.FieldTypeWrapper.TypeAsSignatureType, Type.EmptyTypes); + if (getter != null) + { + pb.SetGetMethod((MethodBuilder)getter.GetMethod()); + } + if (setter != null) + { + pb.SetSetMethod((MethodBuilder)setter.GetMethod()); + } +#if IMPORTER + AttributeHelper.SetModifiers(pb, this.Modifiers, this.IsInternal); +#endif + } + +#if EMITTERS + protected override void EmitGetImpl(CodeEmitter ilgen) + { + if (getter == null) + { + EmitThrowNoSuchMethodErrorForGetter(ilgen, this.FieldTypeWrapper, this); + } + else if (getter.IsStatic) + { + getter.EmitCall(ilgen); + } + else + { + getter.EmitCallvirt(ilgen); + } + } + + internal static void EmitThrowNoSuchMethodErrorForGetter(CodeEmitter ilgen, TypeWrapper type, MemberWrapper member) + { +#if IMPORTER + StaticCompiler.IssueMessage(Message.EmittedNoSuchMethodError, "", member.DeclaringType.Name + "." + member.Name + member.Signature); +#endif + // HACK the branch around the throw is to keep the verifier happy + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Ldc_I4_0); + ilgen.EmitBrtrue(label); + ilgen.EmitThrow("java.lang.NoSuchMethodError"); + ilgen.MarkLabel(label); + if (!member.IsStatic) + { + ilgen.Emit(OpCodes.Pop); + } + ilgen.Emit(OpCodes.Ldloc, ilgen.DeclareLocal(type.TypeAsLocalOrStackType)); + } + + protected override void EmitSetImpl(CodeEmitter ilgen) + { + if (setter == null) + { + if (this.IsFinal) + { + ilgen.Emit(OpCodes.Pop); + if (!this.IsStatic) + { + ilgen.Emit(OpCodes.Pop); + } + } + else + { + EmitThrowNoSuchMethodErrorForSetter(ilgen, this); + } + } + else if (setter.IsStatic) + { + setter.EmitCall(ilgen); + } + else + { + setter.EmitCallvirt(ilgen); + } + } + + internal static void EmitThrowNoSuchMethodErrorForSetter(CodeEmitter ilgen, MemberWrapper member) + { +#if IMPORTER + StaticCompiler.IssueMessage(Message.EmittedNoSuchMethodError, "", member.DeclaringType.Name + "." + member.Name + member.Signature); +#endif + // HACK the branch around the throw is to keep the verifier happy + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Ldc_I4_0); + ilgen.EmitBrtrue(label); + ilgen.EmitThrow("java.lang.NoSuchMethodError"); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Pop); + if (!member.IsStatic) + { + ilgen.Emit(OpCodes.Pop); + } + } +#endif + +#if !IMPORTER && !FIRST_PASS + internal override object GetValue(object obj) + { + if (getter == null) + { + throw new java.lang.NoSuchMethodError(); + } + return getter.Invoke(obj, new object[0]); + } + + internal override void SetValue(object obj, object value) + { + if (setter == null) + { + throw new java.lang.NoSuchMethodError(); + } + setter.Invoke(obj, new object[] { value }); + } +#endif + + } + +#endif + +} diff --git a/src/IKVM.Runtime/DynamicTypeWrapper.cs b/src/IKVM.Runtime/DynamicTypeWrapper.cs index 594dcc746d..fb9af6f440 100644 --- a/src/IKVM.Runtime/DynamicTypeWrapper.cs +++ b/src/IKVM.Runtime/DynamicTypeWrapper.cs @@ -23,66 +23,71 @@ Jeroen Frijters */ using System; using System.Collections.Generic; +using System.Diagnostics; + +using IKVM.Attributes; +using IKVM.Runtime; +using IKVM.ByteCode.Reading; +using IKVM.ByteCode.Parsing; + +using System.Linq; -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; using Type = IKVM.Reflection.Type; -using DynamicOrAotTypeWrapper = IKVM.Internal.AotTypeWrapper; +using DynamicOrAotTypeWrapper = IKVM.Tools.Importer.AotTypeWrapper; using ProtectionDomain = System.Object; - #else using System.Reflection; using System.Reflection.Emit; using DynamicOrAotTypeWrapper = IKVM.Internal.DynamicTypeWrapper; using ProtectionDomain = java.security.ProtectionDomain; - #endif -using System.Diagnostics; - -using IKVM.Attributes; - -using System.Linq; - -using IKVM.Runtime; namespace IKVM.Internal { -#if STATIC_COMPILER + +#if IMPORTER abstract class DynamicTypeWrapper : TypeWrapper #else #pragma warning disable 628 // don't complain about protected members in sealed type sealed class DynamicTypeWrapper : TypeWrapper #endif { -#if STATIC_COMPILER + +#if IMPORTER == false && NETCOREAPP + + static readonly PropertyInfo MetadataTokenInternalPropertyInfo = typeof(MethodBuilder).GetProperty("MetadataTokenInternal", BindingFlags.Instance | BindingFlags.NonPublic); + +#endif + +#if IMPORTER protected readonly CompilerClassLoader classLoader; #else protected readonly ClassLoaderWrapper classLoader; #endif - private volatile DynamicImpl impl; - private readonly TypeWrapper baseTypeWrapper; - private readonly TypeWrapper[] interfaces; - private readonly string sourceFileName; -#if !STATIC_COMPILER - private byte[][] lineNumberTables; + volatile DynamicImpl impl; + readonly TypeWrapper baseTypeWrapper; + readonly TypeWrapper[] interfaces; + readonly string sourceFileName; +#if !IMPORTER + byte[][] lineNumberTables; #endif - private MethodBase automagicSerializationCtor; + MethodBase automagicSerializationCtor; - private TypeWrapper LoadTypeWrapper(ClassLoaderWrapper classLoader, ProtectionDomain pd, ClassFile.ConstantPoolItemClass clazz) + TypeWrapper LoadTypeWrapper(ClassLoaderWrapper classLoader, ProtectionDomain pd, ClassFile.ConstantPoolItemClass clazz) { // check for patched constant pool items - TypeWrapper tw = clazz.GetClassType(); + var tw = clazz.GetClassType(); if (tw == null || tw == VerifierTypeWrapper.Null) - { tw = classLoader.LoadClassByDottedNameFast(clazz.Name); - } if (tw == null) - { throw new NoClassDefFoundError(clazz.Name); - } + CheckMissing(this, tw); classLoader.CheckPackageAccess(tw, pd); return tw; @@ -90,7 +95,7 @@ private TypeWrapper LoadTypeWrapper(ClassLoaderWrapper classLoader, ProtectionDo private static void CheckMissing(TypeWrapper prev, TypeWrapper tw) { -#if STATIC_COMPILER +#if IMPORTER do { UnloadableTypeWrapper missing = tw as UnloadableTypeWrapper; @@ -115,7 +120,7 @@ private static void CheckMissing(TypeWrapper prev, TypeWrapper tw) #endif } -#if STATIC_COMPILER +#if IMPORTER internal DynamicTypeWrapper(TypeWrapper host, ClassFile f, CompilerClassLoader classLoader, ProtectionDomain pd) #else internal DynamicTypeWrapper(TypeWrapper host, ClassFile f, ClassLoaderWrapper classLoader, ProtectionDomain pd) @@ -487,7 +492,7 @@ private sealed class JavaTypeImpl : DynamicImpl private MethodBuilder clinitMethod; private MethodBuilder finalizeMethod; private int recursionCount; -#if STATIC_COMPILER +#if IMPORTER private DynamicTypeWrapper enclosingClassWrapper; private AnnotationBuilder annotationBuilder; private TypeBuilder enumBuilder; @@ -515,7 +520,7 @@ internal void CreateStep1() ClassFile.Method m = classFile.Methods[i]; if (m.IsClassInitializer) { -#if STATIC_COMPILER +#if IMPORTER bool noop; if (IsSideEffectFreeStaticInitializerOrNoop(m, out noop)) { @@ -536,7 +541,7 @@ internal void CreateStep1() { flags |= MemberFlags.InternalAccess; } -#if STATIC_COMPILER +#if IMPORTER if (m.IsCallerSensitive && SupportsCallerID(m)) { flags |= MemberFlags.CallerID; @@ -601,7 +606,7 @@ internal void CreateStep1() if (fld.IsStaticFinalConstant) { TypeWrapper fieldType = null; -#if !STATIC_COMPILER +#if !IMPORTER fieldType = ClassLoaderWrapper.GetBootstrapClassLoader().FieldTypeWrapperFromSig(fld.Signature, LoadMode.LoadOrThrow); #endif fields[i] = new ConstantFieldWrapper(wrapper, fieldType, fld.Name, fld.Signature, fld.Modifiers, null, fld.ConstantValue, MemberFlags.None); @@ -615,13 +620,13 @@ internal void CreateStep1() fields[i] = FieldWrapper.Create(wrapper, null, null, fld.Name, fld.Signature, new ExModifiers(fld.Modifiers, fld.IsInternal)); } } -#if STATIC_COMPILER +#if IMPORTER wrapper.AddMapXmlFields(ref fields); #endif wrapper.SetFields(fields); } -#if STATIC_COMPILER +#if IMPORTER private bool SupportsCallerID(ClassFile.Method method) { if ((classFile.Name == "sun.reflect.Reflection" && method.Name == "getCallerClass") @@ -680,7 +685,7 @@ private static bool HasInterfaceMethod(TypeWrapper tw, string name, string signa internal void CreateStep2() { -#if STATIC_COMPILER +#if IMPORTER if (typeBuilder != null) { // in the static compiler we need to create the TypeBuilder from outer to inner @@ -709,7 +714,7 @@ internal void CreateStep2() { typeAttribs |= TypeAttributes.BeforeFieldInit; } -#if STATIC_COMPILER +#if IMPORTER bool cantNest = false; bool setModifiers = false; TypeBuilder enclosing = null; @@ -823,16 +828,16 @@ internal void CreateStep2() { typeAttribs |= TypeAttributes.NestedAssembly; } -#else // STATIC_COMPILER +#else // IMPORTER if (f.IsPublic) { typeAttribs |= TypeAttributes.Public; } -#endif // STATIC_COMPILER +#endif // IMPORTER if (f.IsInterface) { typeAttribs |= TypeAttributes.Interface | TypeAttributes.Abstract; -#if STATIC_COMPILER +#if IMPORTER // if any "meaningless" bits are set, preserve them setModifiers |= (f.Modifiers & (Modifiers)0x99CE) != 0; // by default we assume interfaces are abstract, so in the exceptional case we need a ModifiersAttribute @@ -860,14 +865,14 @@ internal void CreateStep2() typeBuilder = wrapper.classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(mangledTypeName, typeAttribs); } } -#else // STATIC_COMPILER +#else // IMPORTER typeBuilder = wrapper.classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(mangledTypeName, typeAttribs); -#endif // STATIC_COMPILER +#endif // IMPORTER } else { typeAttribs |= TypeAttributes.Class; -#if STATIC_COMPILER +#if IMPORTER // if any "meaningless" bits are set, preserve them setModifiers |= (f.Modifiers & (Modifiers)0x99CE) != 0; // by default we assume ACC_SUPER for classes, so in the exceptional case we need a ModifiersAttribute @@ -879,12 +884,12 @@ internal void CreateStep2() typeBuilder = enclosing.DefineNestedType(AllocNestedTypeName(enclosingClassWrapper.Name, f.Name), typeAttribs); } else -#endif // STATIC_COMPILER +#endif // IMPORTER { typeBuilder = wrapper.classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(mangledTypeName, typeAttribs); } } -#if STATIC_COMPILER +#if IMPORTER // When we're statically compiling, we associate the typeBuilder with the wrapper. This enables types in referenced assemblies to refer back to // types that we're currently compiling (i.e. a cyclic dependency between the currently assembly we're compiling and a referenced assembly). wrapper.GetClassLoader().SetWrapperForType(typeBuilder, wrapper); @@ -981,7 +986,7 @@ internal void CreateStep2() { AttributeHelper.SetModifiers(typeBuilder, classFile.Modifiers, classFile.IsInternal); } -#endif // STATIC_COMPILER +#endif // IMPORTER if (hasclinit) { AddClinitTrigger(); @@ -996,17 +1001,17 @@ internal void CreateStep2() } } } -#if STATIC_COMPILER +#if IMPORTER finally { } #else catch (Exception x) { - JVM.CriticalFailure("Exception during JavaTypeImpl.CreateStep2", x); + throw new InternalException("Exception during JavaTypeImpl.CreateStep2", x); } #endif } -#if STATIC_COMPILER +#if IMPORTER private void AddInnerClassAttribute(bool isNestedType, bool isInnerClass, string mangledTypeName, Modifiers innerClassFlags) { string name = classFile.Name; @@ -1053,14 +1058,15 @@ private void AddCliEnum() } #endif - private void AddClinitTrigger() + void AddClinitTrigger() { // We create a empty method that we can use to trigger our .cctor // (previously we used RuntimeHelpers.RunClassConstructor, but that is slow and requires additional privileges) - MethodAttributes attribs = MethodAttributes.Static | MethodAttributes.SpecialName; + var attribs = MethodAttributes.Static | MethodAttributes.SpecialName; if (classFile.IsAbstract) { - bool hasfields = false; + var hasfields = false; + // If we have any public static fields, the cctor trigger must (and may) be public as well foreach (ClassFile.Field fld in classFile.Fields) { @@ -1070,36 +1076,42 @@ private void AddClinitTrigger() break; } } + attribs |= hasfields ? MethodAttributes.Public : MethodAttributes.FamORAssem; } else { attribs |= MethodAttributes.Public; } + clinitMethod = typeBuilder.DefineMethod("__", attribs, null, null); clinitMethod.GetILGenerator().Emit(OpCodes.Ret); - // FXBUG on .NET 2.0 RTM x64 the JIT sometimes throws an InvalidProgramException while trying to inline this method, - // so we prevent inlining for now (it also turns out that on x86 not inlining this method actually has a positive perf impact in some cases...) - // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=285772 - clinitMethod.SetImplementationFlags(clinitMethod.GetMethodImplementationFlags() | MethodImplAttributes.NoInlining); + clinitMethod.SetImplementationFlags(clinitMethod.GetMethodImplementationFlags()); } - private sealed class DelegateConstructorMethodWrapper : MethodWrapper + sealed class DelegateConstructorMethodWrapper : MethodWrapper { - private MethodBuilder constructor; - private MethodInfo invoke; - internal DelegateConstructorMethodWrapper(DynamicTypeWrapper tw, ClassFile.Method m) - : base(tw, m.Name, m.Signature, null, null, null, m.Modifiers, MemberFlags.None) + MethodBuilder constructor; + MethodInfo invoke; + + /// + /// Initializes a new instance. + /// + /// + /// + internal DelegateConstructorMethodWrapper(DynamicTypeWrapper tw, ClassFile.Method m) : + base(tw, m.Name, m.Signature, null, null, null, m.Modifiers, MemberFlags.None) { + } internal void DoLink(TypeBuilder typeBuilder) { - MethodAttributes attribs = MethodAttributes.HideBySig | MethodAttributes.Public; + var attribs = MethodAttributes.HideBySig | MethodAttributes.Public; constructor = ReflectUtil.DefineConstructor(typeBuilder, attribs, new Type[] { Types.Object, Types.IntPtr }); constructor.SetImplementationFlags(MethodImplAttributes.Runtime); - MethodWrapper mw = GetParameters()[0].GetMethods()[0]; + var mw = GetParameters()[0].GetMethods()[0]; mw.Link(); invoke = (MethodInfo)mw.GetMethod(); } @@ -1110,6 +1122,7 @@ internal override void EmitNewobj(CodeEmitter ilgen) ilgen.Emit(OpCodes.Ldvirtftn, invoke); ilgen.Emit(OpCodes.Newobj, constructor); } + } private static bool HasStructLayoutAttributeAnnotation(ClassFile c) @@ -1127,7 +1140,7 @@ private static bool HasStructLayoutAttributeAnnotation(ClassFile c) return false; } -#if STATIC_COMPILER +#if IMPORTER private ClassFile.InnerClass getOuterClass() { ClassFile.InnerClass[] innerClasses = classFile.InnerClasses; @@ -1233,7 +1246,7 @@ private bool IsSideEffectFreeStaticInitializerOrNoop(ClassFile.Method m, out boo return false; } } -#endif // STATIC_COMPILER +#endif // IMPORTER private MethodWrapper GetMethodWrapperDuringCtor(TypeWrapper lookup, IList methods, string name, string sig) { @@ -1418,7 +1431,7 @@ internal MethodInfo DoLink(TypeBuilder tb) } } -#if STATIC_COMPILER +#if IMPORTER private static bool CheckInnerOuterNames(string inner, string outer) { // do some sanity checks on the inner/outer class names @@ -1434,7 +1447,7 @@ private string AllocNestedTypeName(string outer, string inner) } return DynamicClassLoader.TypeNameMangleImpl(nestedTypeNames, inner.Substring(outer.Length + 1), null); } -#endif // STATIC_COMPILER +#endif // IMPORTER private int GetMethodIndex(MethodWrapper mw) { @@ -1462,7 +1475,7 @@ private static void CheckLoaderConstraints(MethodWrapper mw, MethodWrapper baseM } else { -#if STATIC_COMPILER +#if IMPORTER StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a return type \"{0}\" and tries to override method \"{5}.{3}{4}\" that has a return type \"{1}\"", mw.ReturnType, baseMethod.ReturnType, mw.DeclaringType.Name, mw.Name, mw.Signature, baseMethod.DeclaringType.Name); #else throw new LinkageError("Loader constraints violated"); @@ -1485,7 +1498,7 @@ private static void CheckLoaderConstraints(MethodWrapper mw, MethodWrapper baseM } else { -#if STATIC_COMPILER +#if IMPORTER StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has an argument type \"{0}\" and tries to override method \"{5}.{3}{4}\" that has an argument type \"{1}\"", here[i], there[i], mw.DeclaringType.Name, mw.Name, mw.Signature, baseMethod.DeclaringType.Name); #else throw new LinkageError("Loader constraints violated"); @@ -1515,7 +1528,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) return null; } int fieldIndex = GetFieldIndex(fw); -#if STATIC_COMPILER +#if IMPORTER if (wrapper.GetClassLoader().RemoveUnusedFields && fw.IsPrivate && fw.IsStatic @@ -1571,7 +1584,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) } return DefineField(fw.Name, fw.FieldTypeWrapper, fieldAttribs, fw.IsVolatile); } -#endif // STATIC_COMPILER +#endif // IMPORTER FieldBuilder field; ClassFile.Field fld = classFile.Fields[fieldIndex]; FieldAttributes attribs = 0; @@ -1581,7 +1594,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) attribs |= FieldAttributes.SpecialName; } MethodAttributes methodAttribs = MethodAttributes.HideBySig; -#if STATIC_COMPILER +#if IMPORTER bool setModifiers = fld.IsInternal || (fld.Modifiers & (Modifiers.Synthetic | Modifiers.Enum)) != 0; #endif if (fld.IsPrivate) @@ -1621,7 +1634,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) } else { -#if STATIC_COMPILER +#if IMPORTER if (wrapper.IsPublic && wrapper.NeedsType2AccessStub(fw)) { // this field is going to get a type 2 access stub, so we hide the actual field @@ -1658,7 +1671,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) CustomAttributeBuilder transientAttrib = new CustomAttributeBuilder(JVM.Import(typeof(NonSerializedAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); field.SetCustomAttribute(transientAttrib); } -#if STATIC_COMPILER +#if IMPORTER { // if the Java modifiers cannot be expressed in .NET, we emit the Modifiers attribute to store // the Java modifiers @@ -1679,7 +1692,7 @@ internal override FieldInfo LinkField(FieldWrapper fw) AttributeHelper.SetRuntimeVisibleTypeAnnotationsAttribute(field, fld.RuntimeVisibleTypeAnnotations); } } -#endif // STATIC_COMPILER +#endif // IMPORTER return field; } @@ -1801,7 +1814,7 @@ private FinishedTypeImpl FinishCore() } } innerClassesTypeWrappers = wrappers.ToArray(); -#if STATIC_COMPILER +#if IMPORTER // before we bake our type, we need to link any inner annotations to allow them to create their attribute type (as a nested type) foreach (TypeWrapper tw in innerClassesTypeWrappers) { @@ -1818,9 +1831,9 @@ private FinishedTypeImpl FinishCore() } } } -#endif //STATIC_COMPILER +#endif //IMPORTER } -#if STATIC_COMPILER +#if IMPORTER if (annotationBuilder != null) { CustomAttributeBuilder cab = new CustomAttributeBuilder(JVM.LoadType(typeof(AnnotationAttributeAttribute)).GetConstructor(new Type[] { Types.String }), @@ -1838,7 +1851,7 @@ private FinishedTypeImpl FinishCore() FinishContext context = new FinishContext(host, classFile, wrapper, typeBuilder); Type type = context.FinishImpl(); -#if STATIC_COMPILER +#if IMPORTER if (annotationBuilder != null) { annotationBuilder.Finish(this); @@ -1853,7 +1866,7 @@ private FinishedTypeImpl FinishCore() } #endif MethodInfo finishedClinitMethod = clinitMethod; -#if !STATIC_COMPILER +#if !IMPORTER if (finishedClinitMethod != null) { // In dynamic mode, we may need to emit a call to this method from a DynamicMethod which doesn't support calling unfinished methods, @@ -1864,11 +1877,10 @@ private FinishedTypeImpl FinishCore() finishedType = new FinishedTypeImpl(type, innerClassesTypeWrappers, declaringTypeWrapper, wrapper.ReflectiveModifiers, Metadata.Create(classFile), finishedClinitMethod, finalizeMethod, host); return finishedType; } -#if !STATIC_COMPILER +#if !IMPORTER catch (Exception x) { - JVM.CriticalFailure("Exception during finishing of: " + wrapper.Name, x); - return null; + throw new InternalException($"Exception during finishing of: {wrapper.Name}", x); } #endif finally @@ -1877,7 +1889,7 @@ private FinishedTypeImpl FinishCore() } } -#if STATIC_COMPILER +#if IMPORTER private bool IsValidAnnotationElementType(string type) { if (type[0] == '[') @@ -2426,7 +2438,7 @@ internal override bool IsCustomAttribute get { return false; } } } -#endif // STATIC_COMPILER +#endif // IMPORTER internal override TypeWrapper[] InnerClasses { @@ -2981,7 +2993,7 @@ internal override MethodBase LinkMethod(MethodWrapper mw) ilgen.DoEmit(); wrapper.EmitLevel4Warning(mmw.IsConflictError ? HardError.IncompatibleClassChangeError : HardError.AbstractMethodError, message); } -#if STATIC_COMPILER +#if IMPORTER if (wrapper.IsInterface && !mmw.IsAbstract) { // even though we're not visible to reflection., we need to record the fact that we have a default implementation @@ -3023,7 +3035,7 @@ internal override MethodBase LinkMethod(MethodWrapper mw) } string[] exceptions = m.ExceptionsAttribute; methods[index].SetDeclaredExceptions(exceptions); -#if STATIC_COMPILER +#if IMPORTER AttributeHelper.SetThrowsAttribute(method, exceptions); if (setModifiers || m.IsInternal || (m.Modifiers & (Modifiers.Synthetic | Modifiers.Bridge)) != 0) { @@ -3058,7 +3070,7 @@ internal override MethodBase LinkMethod(MethodWrapper mw) Modifiers[] modifiers = new Modifiers[m.MethodParameters.Length]; for (int i = 0; i < modifiers.Length; i++) { - modifiers[i] = (Modifiers)m.MethodParameters[i].flags; + modifiers[i] = (Modifiers)m.MethodParameters[i].accessFlags; } AttributeHelper.SetMethodParametersAttribute(method, modifiers); } @@ -3066,12 +3078,12 @@ internal override MethodBase LinkMethod(MethodWrapper mw) { AttributeHelper.SetRuntimeVisibleTypeAnnotationsAttribute(method, m.RuntimeVisibleTypeAnnotations); } -#else // STATIC_COMPILER +#else // IMPORTER if (setModifiers) { // shut up the compiler } -#endif // STATIC_COMPILER +#endif // IMPORTER return method; } finally @@ -3084,27 +3096,22 @@ private bool HasSerialVersionUID { get { - foreach (FieldWrapper field in fields) - { + foreach (var field in fields) if (field.IsSerialVersionUID) - { return true; - } - } + return false; } } - private MethodBuilder GenerateConstructor(MethodWrapper mw) + MethodBuilder GenerateConstructor(MethodWrapper mw) { - MethodBuilder cb = mw.GetDefineMethodHelper().DefineConstructor(wrapper, typeBuilder, GetMethodAccess(mw) | MethodAttributes.HideBySig); - cb.SetImplementationFlags(MethodImplAttributes.NoInlining); - return cb; + return mw.GetDefineMethodHelper().DefineConstructor(wrapper, typeBuilder, GetMethodAccess(mw) | MethodAttributes.HideBySig); } - private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool setModifiers) + MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool setModifiers) { - MethodAttributes attribs = MethodAttributes.HideBySig; + var attribs = MethodAttributes.HideBySig; if (m.IsNative) { if (wrapper.IsPInvokeMethod(m)) @@ -3128,6 +3135,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set { attribs |= GetMethodAccess(methods[index]); } + if (m.IsAbstract || (!m.IsStatic && m.IsPublic && classFile.IsInterface)) { // only if the classfile is abstract, we make the CLR method abstract, otherwise, @@ -3182,7 +3190,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set // mark as specialname to remind us to unescape the name attribs |= MethodAttributes.SpecialName; } -#if STATIC_COMPILER +#if IMPORTER if ((m.Modifiers & Modifiers.Bridge) != 0 && (m.IsPublic || m.IsProtected) && wrapper.IsPublic) { string sigbase = m.Signature.Substring(0, m.Signature.LastIndexOf(')') + 1); @@ -3228,7 +3236,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set } } MethodBuilder mb = null; -#if STATIC_COMPILER +#if IMPORTER mb = wrapper.DefineGhostMethod(typeBuilder, name, attribs, methods[index]); #endif if (mb == null) @@ -3292,7 +3300,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set if (classFile.IsInterface && !m.IsPublic && !wrapper.IsGhost) { TypeBuilder tb = typeBuilder; -#if STATIC_COMPILER +#if IMPORTER if (wrapper.IsPublic && wrapper.classLoader.WorkaroundInterfacePrivateMethods) { // FXBUG csc.exe doesn't like non-public methods in interfaces, so we put them in a nested type @@ -3317,7 +3325,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set mb = methods[index].GetDefineMethodHelper().DefineMethod(wrapper.GetClassLoader().GetTypeWrapperFactory(), tb, NamePrefix.PrivateInterfaceInstanceMethod + name, attribs | MethodAttributes.Static | MethodAttributes.SpecialName, typeBuilder, false); -#if STATIC_COMPILER +#if IMPORTER AttributeHelper.SetNameSig(mb, m.Name, m.Signature); #endif } @@ -3340,6 +3348,7 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set { typeBuilder.DefineMethodOverride(mb, (MethodInfo)baseMethod.GetMethod()); } + // the non-primary base methods always need an explicit method override subsequent = true; } @@ -3348,29 +3357,28 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set // or if we're subclassing a non-Java class that has a Finalize method, we need a new Finalize override if (needFinalize) { - string finalizeName = baseFinalize.Name; - MethodWrapper mwClash = wrapper.GetMethodWrapper(finalizeName, StringConstants.SIG_VOID, true); + var finalizeName = baseFinalize.Name; + var mwClash = wrapper.GetMethodWrapper(finalizeName, StringConstants.SIG_VOID, true); if (mwClash != null && mwClash.GetMethod() != baseFinalize) - { finalizeName = "__"; - } - MethodAttributes attr = MethodAttributes.HideBySig | MethodAttributes.Virtual; - // make sure we don't reduce accessibility + + var attr = MethodAttributes.HideBySig | MethodAttributes.Virtual; attr |= baseFinalize.IsPublic ? MethodAttributes.Public : MethodAttributes.Family; if (m.IsFinal) - { attr |= MethodAttributes.Final; - } + finalizeMethod = typeBuilder.DefineMethod(finalizeName, attr, CallingConventions.Standard, Types.Void, Type.EmptyTypes); if (finalizeName != baseFinalize.Name) - { typeBuilder.DefineMethodOverride(finalizeMethod, baseFinalize); - } + AttributeHelper.HideFromJava(finalizeMethod); - CodeEmitter ilgen = CodeEmitter.Create(finalizeMethod); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.SkipFinalizer); - CodeEmitterLabel skip = ilgen.DefineLabel(); + + var ilgen = CodeEmitter.Create(finalizeMethod); + ilgen.EmitLdarg(0); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.SkipFinalizerOf); + var skip = ilgen.DefineLabel(); ilgen.EmitBrtrue(skip); + if (needDispatch) { ilgen.BeginExceptionBlock(); @@ -3386,42 +3394,36 @@ private MethodBuilder GenerateMethod(int index, ClassFile.Method m, ref bool set ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Call, baseFinalize); } + ilgen.MarkLabel(skip); ilgen.Emit(OpCodes.Ret); ilgen.DoEmit(); } -#if STATIC_COMPILER +#if IMPORTER if (classFile.Methods[index].AnnotationDefault != null) { - CustomAttributeBuilder cab = new CustomAttributeBuilder(StaticCompiler.GetRuntimeType("IKVM.Attributes.AnnotationDefaultAttribute").GetConstructor(new Type[] { Types.Object }), - new object[] { AnnotationDefaultAttribute.Escape(classFile.Methods[index].AnnotationDefault) }); + CustomAttributeBuilder cab = new CustomAttributeBuilder(StaticCompiler.GetRuntimeType("IKVM.Attributes.AnnotationDefaultAttribute").GetConstructor(new Type[] { Types.Object }), new object[] { AnnotationDefaultAttribute.Escape(classFile.Methods[index].AnnotationDefault) }); mb.SetCustomAttribute(cab); } -#endif // STATIC_COMPILER +#endif } + // method is a synchronized method if ((methods[index].Modifiers & (Modifiers.Synchronized | Modifiers.Static)) == Modifiers.Synchronized) - { mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.Synchronized); - } + // java method specifies to force inline, the best we can do is set aggressive inlining if (classFile.Methods[index].IsForceInline) - { - const MethodImplAttributes AggressiveInlining = (MethodImplAttributes)256; - mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | AggressiveInlining); - } + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.AggressiveInlining); if (classFile.Methods[index].IsLambdaFormCompiled || classFile.Methods[index].IsLambdaFormHidden) { - HideFromJavaFlags flags = HideFromJavaFlags.None; + var flags = HideFromJavaFlags.None; if (classFile.Methods[index].IsLambdaFormCompiled) - { flags |= HideFromJavaFlags.StackWalk; - } if (classFile.Methods[index].IsLambdaFormHidden) - { flags |= HideFromJavaFlags.StackTrace; - } + AttributeHelper.HideFromJava(mb, flags); } @@ -3459,7 +3461,7 @@ private static MethodAttributes GetMethodAccess(MethodWrapper mw) } } -#if STATIC_COMPILER +#if IMPORTER // The classic example of an access bridge is StringBuilder.length(), the JDK 6 compiler // generates this to work around a reflection problem (which otherwise wouldn't surface the // length() method, because it is defined in the non-public base class AbstractStringBuilder.) @@ -3482,7 +3484,7 @@ private static bool IsAccessBridge(ClassFile classFile, ClassFile.Method m) } return false; } -#endif // STATIC_COMPILER +#endif // IMPORTER internal override Type Type { @@ -3587,181 +3589,143 @@ internal override TypeWrapper Host } } - private sealed class Metadata + sealed class Metadata { - private readonly string[][] genericMetaData; - private readonly object[][] annotations; - private readonly MethodParametersEntry[][] methodParameters; - private readonly byte[][][] runtimeVisibleTypeAnnotations; - private readonly object[] constantPool; - private Metadata(string[][] genericMetaData, object[][] annotations, MethodParametersEntry[][] methodParameters, - byte[][][] runtimeVisibleTypeAnnotations, object[] constantPool) + readonly string[][] genericMetaData; + readonly object[][] annotations; + readonly MethodParametersEntry[][] methodParameters; + readonly IReadOnlyList runtimeVisibleTypeAnnotations; + readonly IReadOnlyList[] fieldRuntimeVisibleTypeAnnotations; + readonly IReadOnlyList[] methodRuntimeVisibleTypeAnnotations; + readonly object[] constantPool; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + Metadata(string[][] genericMetaData, object[][] annotations, MethodParametersEntry[][] methodParameters, IReadOnlyList runtimeVisibleTypeAnnotations, IReadOnlyList[] fieldRuntimeVisibleTypeAnnotations, IReadOnlyList[] methodRuntimeVisibleTypeAnnotations, object[] constantPool) { this.genericMetaData = genericMetaData; this.annotations = annotations; this.methodParameters = methodParameters; this.runtimeVisibleTypeAnnotations = runtimeVisibleTypeAnnotations; + this.fieldRuntimeVisibleTypeAnnotations = fieldRuntimeVisibleTypeAnnotations; + this.methodRuntimeVisibleTypeAnnotations = methodRuntimeVisibleTypeAnnotations; this.constantPool = constantPool; } internal static Metadata Create(ClassFile classFile) { if (classFile.MajorVersion < 49) - { return null; - } + string[][] genericMetaData = null; object[][] annotations = null; MethodParametersEntry[][] methodParameters = null; - byte[][][] runtimeVisibleTypeAnnotations = null; + IReadOnlyList runtimeVisibleTypeAnnotations = null; + IReadOnlyList[] fieldRuntimeVisibleTypeAnnotations = null; + IReadOnlyList[] methodRuntimeVisibleTypeAnnotations = null; + + if (classFile.EnclosingMethod != null) + { + genericMetaData ??= new string[4][]; + genericMetaData[2] = classFile.EnclosingMethod; + } + + if (classFile.GenericSignature != null) + { + genericMetaData ??= new string[4][]; + genericMetaData[3] = new string[] { classFile.GenericSignature }; + } + + if (classFile.Annotations != null) + { + annotations ??= new object[5][]; + annotations[0] = classFile.Annotations; + } + + if (classFile.RuntimeVisibleTypeAnnotations != null) + { + runtimeVisibleTypeAnnotations = classFile.RuntimeVisibleTypeAnnotations; + } + for (int i = 0; i < classFile.Methods.Length; i++) { if (classFile.Methods[i].GenericSignature != null) { - if (genericMetaData == null) - { - genericMetaData = new string[4][]; - } - if (genericMetaData[0] == null) - { - genericMetaData[0] = new string[classFile.Methods.Length]; - } + genericMetaData ??= new string[4][]; + genericMetaData[0] ??= new string[classFile.Methods.Length]; genericMetaData[0][i] = classFile.Methods[i].GenericSignature; } + if (classFile.Methods[i].Annotations != null) { - if (annotations == null) - { - annotations = new object[5][]; - } - if (annotations[1] == null) - { - annotations[1] = new object[classFile.Methods.Length]; - } + annotations ??= new object[5][]; + annotations[1] ??= new object[classFile.Methods.Length]; annotations[1][i] = classFile.Methods[i].Annotations; } + if (classFile.Methods[i].ParameterAnnotations != null) { - if (annotations == null) - { - annotations = new object[5][]; - } - if (annotations[2] == null) - { - annotations[2] = new object[classFile.Methods.Length]; - } + annotations ??= new object[5][]; + annotations[2] ??= new object[classFile.Methods.Length]; annotations[2][i] = classFile.Methods[i].ParameterAnnotations; } + if (classFile.Methods[i].AnnotationDefault != null) { - if (annotations == null) - { - annotations = new object[5][]; - } - if (annotations[3] == null) - { - annotations[3] = new object[classFile.Methods.Length]; - } + annotations ??= new object[5][]; + annotations[3] ??= new object[classFile.Methods.Length]; annotations[3][i] = classFile.Methods[i].AnnotationDefault; } + if (classFile.Methods[i].MethodParameters != null) { - if (methodParameters == null) - { - methodParameters = new MethodParametersEntry[classFile.Methods.Length][]; - } + methodParameters ??= new MethodParametersEntry[classFile.Methods.Length][]; methodParameters[i] = classFile.Methods[i].MethodParameters; } + if (classFile.Methods[i].RuntimeVisibleTypeAnnotations != null) { - if (runtimeVisibleTypeAnnotations == null) - { - runtimeVisibleTypeAnnotations = new byte[3][][]; - } - if (runtimeVisibleTypeAnnotations[1] == null) - { - runtimeVisibleTypeAnnotations[1] = new byte[classFile.Methods.Length][]; - } - runtimeVisibleTypeAnnotations[1][i] = classFile.Methods[i].RuntimeVisibleTypeAnnotations; + methodRuntimeVisibleTypeAnnotations ??= new IReadOnlyList[classFile.Methods.Length]; + methodRuntimeVisibleTypeAnnotations[i] = classFile.Methods[i].RuntimeVisibleTypeAnnotations; } } + for (int i = 0; i < classFile.Fields.Length; i++) { if (classFile.Fields[i].GenericSignature != null) { - if (genericMetaData == null) - { - genericMetaData = new string[4][]; - } - if (genericMetaData[1] == null) - { - genericMetaData[1] = new string[classFile.Fields.Length]; - } + genericMetaData ??= new string[4][]; + genericMetaData[1] ??= new string[classFile.Fields.Length]; genericMetaData[1][i] = classFile.Fields[i].GenericSignature; } + if (classFile.Fields[i].Annotations != null) { - if (annotations == null) - { - annotations = new object[5][]; - } - if (annotations[4] == null) - { - annotations[4] = new object[classFile.Fields.Length][]; - } + annotations ??= new object[5][]; + annotations[4] ??= new object[classFile.Fields.Length][]; annotations[4][i] = classFile.Fields[i].Annotations; } + if (classFile.Fields[i].RuntimeVisibleTypeAnnotations != null) { - if (runtimeVisibleTypeAnnotations == null) - { - runtimeVisibleTypeAnnotations = new byte[3][][]; - } - if (runtimeVisibleTypeAnnotations[2] == null) - { - runtimeVisibleTypeAnnotations[2] = new byte[classFile.Fields.Length][]; - } - runtimeVisibleTypeAnnotations[2][i] = classFile.Fields[i].RuntimeVisibleTypeAnnotations; + fieldRuntimeVisibleTypeAnnotations ??= new IReadOnlyList[classFile.Fields.Length]; + fieldRuntimeVisibleTypeAnnotations[i] = classFile.Fields[i].RuntimeVisibleTypeAnnotations; } } - if (classFile.EnclosingMethod != null) - { - if (genericMetaData == null) - { - genericMetaData = new string[4][]; - } - genericMetaData[2] = classFile.EnclosingMethod; - } - if (classFile.GenericSignature != null) - { - if (genericMetaData == null) - { - genericMetaData = new string[4][]; - } - genericMetaData[3] = new string[] { classFile.GenericSignature }; - } - if (classFile.Annotations != null) - { - if (annotations == null) - { - annotations = new object[5][]; - } - annotations[0] = classFile.Annotations; - } - if (classFile.RuntimeVisibleTypeAnnotations != null) - { - if (runtimeVisibleTypeAnnotations == null) - { - runtimeVisibleTypeAnnotations = new byte[3][][]; - } - runtimeVisibleTypeAnnotations[0] = new byte[1][] { classFile.RuntimeVisibleTypeAnnotations }; - } - if (genericMetaData != null || annotations != null || methodParameters != null || runtimeVisibleTypeAnnotations != null) + + if (genericMetaData != null || annotations != null || methodParameters != null || runtimeVisibleTypeAnnotations != null || fieldRuntimeVisibleTypeAnnotations != null || methodRuntimeVisibleTypeAnnotations != null) { - object[] constantPool = runtimeVisibleTypeAnnotations == null ? null : classFile.GetConstantPool(); - return new Metadata(genericMetaData, annotations, methodParameters, runtimeVisibleTypeAnnotations, constantPool); + var constantPool = runtimeVisibleTypeAnnotations != null || fieldRuntimeVisibleTypeAnnotations != null || methodRuntimeVisibleTypeAnnotations != null ? classFile.GetConstantPool() : null; + return new Metadata(genericMetaData, annotations, methodParameters, runtimeVisibleTypeAnnotations, fieldRuntimeVisibleTypeAnnotations, methodRuntimeVisibleTypeAnnotations, constantPool); } + return null; } @@ -3863,42 +3827,55 @@ internal static object[] GetConstantPool(Metadata m) internal static byte[] GetRawTypeAnnotations(Metadata m) { - if (m != null && m.runtimeVisibleTypeAnnotations != null && m.runtimeVisibleTypeAnnotations[0] != null) - { - return m.runtimeVisibleTypeAnnotations[0][0]; - } - return null; + if (m != null && m.runtimeVisibleTypeAnnotations != null) + return SerializeTypeAnnotations(m.runtimeVisibleTypeAnnotations); + else + return null; } internal static byte[] GetMethodRawTypeAnnotations(Metadata m, int index) { - if (m != null && m.runtimeVisibleTypeAnnotations != null && m.runtimeVisibleTypeAnnotations[1] != null) - { - return m.runtimeVisibleTypeAnnotations[1][index]; - } - return null; + if (m != null && m.methodRuntimeVisibleTypeAnnotations != null) + return SerializeTypeAnnotations(m.methodRuntimeVisibleTypeAnnotations[index]); + else + return null; } internal static byte[] GetFieldRawTypeAnnotations(Metadata m, int index) { - if (m != null && m.runtimeVisibleTypeAnnotations != null && m.runtimeVisibleTypeAnnotations[2] != null) - { - return m.runtimeVisibleTypeAnnotations[2][index]; - } - return null; + if (m != null && m.fieldRuntimeVisibleTypeAnnotations != null) + return SerializeTypeAnnotations(m.fieldRuntimeVisibleTypeAnnotations[index]); + else + return null; + } + + static byte[] SerializeTypeAnnotations(IReadOnlyList annotations) + { + if (annotations == null) + return null; + + var record = new RuntimeVisibleTypeAnnotationsAttributeRecord(annotations.Select(i => i.Record).ToArray()); + var buffer = new byte[record.GetSize()]; + var writer = new ClassFormatWriter(buffer); + if (record.TryWrite(ref writer) == false) + throw new InternalException("Failed to serialize raw type annotations."); + + return buffer; } + } - private sealed class FinishedTypeImpl : DynamicImpl + sealed class FinishedTypeImpl : DynamicImpl { - private readonly Type type; - private readonly TypeWrapper[] innerclasses; - private readonly TypeWrapper declaringTypeWrapper; - private readonly Modifiers reflectiveModifiers; - private readonly MethodInfo clinitMethod; - private readonly MethodInfo finalizeMethod; - private readonly Metadata metadata; - private readonly TypeWrapper host; + + readonly Type type; + readonly TypeWrapper[] innerclasses; + readonly TypeWrapper declaringTypeWrapper; + readonly Modifiers reflectiveModifiers; + readonly MethodInfo clinitMethod; + readonly MethodInfo finalizeMethod; + readonly Metadata metadata; + readonly TypeWrapper host; internal FinishedTypeImpl(Type type, TypeWrapper[] innerclasses, TypeWrapper declaringTypeWrapper, Modifiers reflectiveModifiers, Metadata metadata, MethodInfo clinitMethod, MethodInfo finalizeMethod, TypeWrapper host) { @@ -4066,7 +4043,7 @@ internal sealed class FinishContext private Dictionary arfuMap; private Dictionary invokespecialstubcache; private Dictionary dynamicClassLiteral; -#if STATIC_COMPILER +#if IMPORTER private TypeBuilder interfaceHelperMethodsTypeBuilder; #else private List liveObjects; @@ -4151,7 +4128,7 @@ internal void EmitDynamicClassLiteral(CodeEmitter ilgen, TypeWrapper tw, bool dy internal void EmitHostCallerID(CodeEmitter ilgen) { -#if STATIC_COMPILER || FIRST_PASS +#if IMPORTER || FIRST_PASS throw new InvalidOperationException(); #else EmitLiveObjectLoad(ilgen, DynamicCallerIDProvider.CreateCallerID(host)); @@ -4161,7 +4138,7 @@ internal void EmitHostCallerID(CodeEmitter ilgen) internal void EmitCallerID(CodeEmitter ilgen, bool dynamic) { -#if !FIRST_PASS && !STATIC_COMPILER +#if !FIRST_PASS && !IMPORTER if (dynamic) { EmitLiveObjectLoad(ilgen, DynamicCallerIDProvider.Instance); @@ -4206,9 +4183,9 @@ internal Type FinishImpl() { MethodWrapper[] methods = wrapper.GetMethods(); FieldWrapper[] fields = wrapper.GetFields(); -#if STATIC_COMPILER +#if IMPORTER wrapper.FinishGhost(typeBuilder, methods); -#endif // STATIC_COMPILER +#endif // IMPORTER if (!classFile.IsInterface) { @@ -4287,7 +4264,7 @@ internal Type FinishImpl() parent = parent.BaseTypeWrapper; } } -#if STATIC_COMPILER +#if IMPORTER TypeBuilder tbDefaultMethods = null; #endif bool basehasclinit = wrapper.BaseTypeWrapper != null && wrapper.BaseTypeWrapper.HasStaticInitializer; @@ -4320,7 +4297,7 @@ internal Type FinishImpl() } else { -#if STATIC_COMPILER +#if IMPORTER if (methods[i].GetParameters().Length > MethodHandleUtil.MaxArity && methods[i].RequiresNonVirtualDispatcher && wrapper.GetClassLoader().EmitNoRefEmitHelpers) { wrapper.GetClassLoader().GetTypeWrapperFactory().DefineDelegate(methods[i].GetParameters().Length, methods[i].ReturnType == PrimitiveTypeWrapper.VOID); @@ -4366,7 +4343,7 @@ internal Type FinishImpl() { var ilGenerator = CodeEmitter.Create(mb); TraceHelper.EmitMethodTrace(ilGenerator, classFile.Name + "." + m.Name + m.Signature); -#if STATIC_COMPILER +#if IMPORTER // do we have an implementation in map.xml? if (wrapper.EmitMapXmlMethodPrologueAndOrBody(ilGenerator, classFile, m)) { @@ -4385,7 +4362,7 @@ internal Type FinishImpl() var args = methods[i].GetParameters(); var nargs = args; -#if STATIC_COMPILER +#if IMPORTER // see if there exists a "managed JNI" class for this type var nativeCodeType = StaticCompiler.GetType(wrapper.GetClassLoader(), "IKVM.Java.Externs." + classFile.Name.Replace("$", "+")); @@ -4421,7 +4398,7 @@ internal Type FinishImpl() if (nativeMethod != null) { -#if STATIC_COMPILER +#if IMPORTER for (int j = 0; j < nargs.Length; j++) ilGenerator.EmitLdarg(j); @@ -4509,52 +4486,46 @@ internal Type FinishImpl() if (m.IsVirtual && classFile.IsInterface) { mb = (MethodBuilder)DefaultInterfaceMethodWrapper.GetImpl(methods[i]); -#if STATIC_COMPILER +#if IMPORTER CreateDefaultMethodInterop(ref tbDefaultMethods, mb, methods[i]); #endif } - CodeEmitter ilGenerator = CodeEmitter.Create(mb); + var ilGenerator = CodeEmitter.Create(mb); if (!m.IsStatic && !m.IsPublic && classFile.IsInterface) { // Java 8 non-virtual interface method that we compile as a static method, // we need to make sure the passed in this reference isn't null ilGenerator.EmitLdarg(0); if (wrapper.IsGhost) - { ilGenerator.Emit(OpCodes.Ldfld, wrapper.GhostRefField); - } ilGenerator.EmitNullCheck(); } + TraceHelper.EmitMethodTrace(ilGenerator, classFile.Name + "." + m.Name + m.Signature); -#if STATIC_COMPILER +#if IMPORTER // do we have an implementation in map.xml? if (wrapper.EmitMapXmlMethodPrologueAndOrBody(ilGenerator, classFile, m)) { ilGenerator.DoEmit(); continue; } -#endif // STATIC_COMPILER - bool nonleaf = false; +#endif // IMPORTER + var nonleaf = false; Compiler.Compile(this, host, wrapper, methods[i], classFile, m, ilGenerator, ref nonleaf); ilGenerator.CheckLabels(); ilGenerator.DoEmit(); - if (nonleaf && !m.IsForceInline) - { - mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.NoInlining); - } -#if STATIC_COMPILER + if (nonleaf) + mb.SetImplementationFlags(mb.GetMethodImplementationFlags()); +#if IMPORTER ilGenerator.EmitLineNumberTable(mb); -#else // STATIC_COMPILER - byte[] linenumbers = ilGenerator.GetLineNumberTable(); +#else // IMPORTER + var linenumbers = ilGenerator.GetLineNumberTable(); if (linenumbers != null) { - if (wrapper.lineNumberTables == null) - { - wrapper.lineNumberTables = new byte[methods.Length][]; - } + wrapper.lineNumberTables ??= new byte[methods.Length][]; wrapper.lineNumberTables[i] = linenumbers; } -#endif // STATIC_COMPILER +#endif // IMPORTER } } } @@ -4573,13 +4544,13 @@ internal Type FinishImpl() cb = ReflectUtil.DefineTypeInitializer(typeBuilder, wrapper.classLoader); AttributeHelper.HideFromJava(cb); } - CodeEmitter ilGenerator = CodeEmitter.Create(cb); + var ilGenerator = CodeEmitter.Create(cb); + // before we call the base class initializer, we need to set the non-final static ConstantValue fields EmitConstantValueInitialization(fields, ilGenerator); if (basehasclinit) - { wrapper.BaseTypeWrapper.EmitRunClassConstructor(ilGenerator); - } + if (clinitIndex != -1) { CompileConstructorBody(this, ilGenerator, clinitIndex); @@ -4589,6 +4560,7 @@ internal Type FinishImpl() ilGenerator.Emit(OpCodes.Ret); ilGenerator.DoEmit(); } + ilGenerator.CheckLabels(); } @@ -4643,7 +4615,7 @@ internal Type FinishImpl() } } -#if STATIC_COMPILER +#if IMPORTER // If we're an interface that has public/protected fields, we create an inner class // to expose these fields to C# (which stubbornly refuses to see fields in interfaces). AddInterfaceFieldsInterop(fields); @@ -4662,7 +4634,7 @@ internal Type FinishImpl() } AddConstantPoolAttributeIfNecessary(classFile, typeBuilder); -#endif // STATIC_COMPILER +#endif // IMPORTER for (int i = 0; i < classFile.Methods.Length; i++) { @@ -4687,7 +4659,7 @@ internal Type FinishImpl() } string[] parameterNames; AddMethodParameterInfo(m, methods[i], mb, out parameterNames); -#if STATIC_COMPILER +#if IMPORTER if (methods[i].HasCallerID) { AttributeHelper.SetEditorBrowsableNever(mb); @@ -4697,7 +4669,7 @@ internal Type FinishImpl() { mb.__AddUnmanagedExport(m.DllExportName, m.DllExportOrdinal); } -#endif // STATIC_COMPILER +#endif // IMPORTER } for (int i = 0; i < classFile.Fields.Length; i++) @@ -4737,7 +4709,7 @@ internal Type FinishImpl() } } -#if STATIC_COMPILER +#if IMPORTER AddImplementsAttribute(); #endif @@ -4755,7 +4727,7 @@ internal Type FinishImpl() tb.CreateType(); } } -#if !STATIC_COMPILER +#if !IMPORTER if (liveObjects != null) { typeof(IKVM.Runtime.LiveObjectHolder<>).MakeGenericType(type).GetField("values", BindingFlags.Static | BindingFlags.Public).SetValue(null, liveObjects.ToArray()); @@ -4766,31 +4738,30 @@ internal Type FinishImpl() { Profiler.Leave("TypeBuilder.CreateType"); } -#if !STATIC_COMPILER +#if !IMPORTER // When we're statically compiling we don't need to set the wrapper here, because we've already done so for the typeBuilder earlier. wrapper.GetClassLoader().SetWrapperForType(type, wrapper); #endif -#if STATIC_COMPILER +#if IMPORTER wrapper.FinishGhostStep2(); #endif return type; } -#if STATIC_COMPILER +#if IMPORTER private static void AddConstantPoolAttributeIfNecessary(ClassFile classFile, TypeBuilder typeBuilder) { object[] constantPool = null; bool[] inUse = null; MarkConstantPoolUsage(classFile, classFile.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse); - foreach (ClassFile.Method method in classFile.Methods) - { + + foreach (var method in classFile.Methods) MarkConstantPoolUsage(classFile, method.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse); - } - foreach (ClassFile.Field field in classFile.Fields) - { + + foreach (var field in classFile.Fields) MarkConstantPoolUsage(classFile, field.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse); - } + if (constantPool != null) { // to save space, we clear out the items that aren't used by the RuntimeVisibleTypeAnnotations and @@ -4799,7 +4770,7 @@ private static void AddConstantPoolAttributeIfNecessary(ClassFile classFile, Typ } } - private static void MarkConstantPoolUsage(ClassFile classFile, byte[] runtimeVisibleTypeAnnotations, ref object[] constantPool, ref bool[] inUse) + private static void MarkConstantPoolUsage(ClassFile classFile, IReadOnlyList runtimeVisibleTypeAnnotations, ref object[] constantPool, ref bool[] inUse) { if (runtimeVisibleTypeAnnotations != null) { @@ -4810,12 +4781,9 @@ private static void MarkConstantPoolUsage(ClassFile classFile, byte[] runtimeVis } try { - BigEndianBinaryReader br = new BigEndianBinaryReader(runtimeVisibleTypeAnnotations, 0, runtimeVisibleTypeAnnotations.Length); - ushort num_annotations = br.ReadUInt16(); - for (int i = 0; i < num_annotations; i++) - { - MarkConstantPoolUsageForTypeAnnotation(br, inUse); - } + foreach (var annotation in runtimeVisibleTypeAnnotations) + MarkConstantPoolUsageForTypeAnnotation(annotation, inUse); + return; } catch (ClassFormatError) @@ -4832,90 +4800,51 @@ private static void MarkConstantPoolUsage(ClassFile classFile, byte[] runtimeVis } } - private static void MarkConstantPoolUsageForTypeAnnotation(BigEndianBinaryReader br, bool[] inUse) + static void MarkConstantPoolUsageForAnnotation(AnnotationReader annotation, bool[] inUse) { - switch (br.ReadByte()) // target_type - { - case 0x00: - case 0x01: - br.ReadByte(); // type_parameter_index - break; - case 0x10: - br.ReadUInt16(); // supertype_index - break; - case 0x11: - case 0x12: - br.ReadByte(); // type_parameter_index - br.ReadByte(); // bound_index - break; - case 0x13: - case 0x14: - case 0x15: - // empty_target - break; - case 0x16: - br.ReadByte(); // formal_parameter_index - break; - case 0x17: - br.ReadUInt16(); // throws_type_index - break; - default: - throw new ClassFormatError(""); - } - byte path_length = br.ReadByte(); - for (int i = 0; i < path_length; i++) + ushort type_index = annotation.Record.TypeIndex; + inUse[type_index] = true; + + for (int i = 0; i < annotation.Record.Elements.Length; i++) { - br.ReadByte(); // type_path_kind - br.ReadByte(); // type_argument_index + inUse[annotation.Record.Elements[i].NameIndex] = true; + MarkConstantPoolUsageForAnnotationComponentValue(annotation.Elements[i], inUse); } - MarkConstantPoolUsageForAnnotation(br, inUse); } - private static void MarkConstantPoolUsageForAnnotation(BigEndianBinaryReader br, bool[] inUse) + static void MarkConstantPoolUsageForTypeAnnotation(TypeAnnotationReader annotation, bool[] inUse) { - ushort type_index = br.ReadUInt16(); + ushort type_index = annotation.Record.TypeIndex; inUse[type_index] = true; - ushort num_components = br.ReadUInt16(); - for (int i = 0; i < num_components; i++) + + for (int i = 0; i < annotation.Record.Elements.Length; i++) { - ushort component_name_index = br.ReadUInt16(); - inUse[component_name_index] = true; - MarkConstantPoolUsageForAnnotationComponentValue(br, inUse); + inUse[annotation.Record.Elements[i].NameIndex] = true; + MarkConstantPoolUsageForAnnotationComponentValue(annotation.Elements[i], inUse); } } - private static void MarkConstantPoolUsageForAnnotationComponentValue(BigEndianBinaryReader br, bool[] inUse) + static void MarkConstantPoolUsageForAnnotationComponentValue(ElementValueReader element, bool[] inUse) { - switch ((char)br.ReadByte()) // tag + switch (element) { - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - case 's': - case 'c': - inUse[br.ReadUInt16()] = true; + case ElementValueConstantReader constant: + inUse[constant.ValueRecord.Index] = true; break; - case 'e': - inUse[br.ReadUInt16()] = true; - inUse[br.ReadUInt16()] = true; + case ElementValueClassReader classInfo: + inUse[classInfo.ValueRecord.ClassIndex] = true; break; - case '@': - MarkConstantPoolUsageForAnnotation(br, inUse); + case ElementValueEnumConstantReader enumConstant: + inUse[enumConstant.ValueRecord.TypeNameIndex] = true; + inUse[enumConstant.ValueRecord.ConstantNameIndex] = true; break; - case '[': - ushort num_values = br.ReadUInt16(); - for (int i = 0; i < num_values; i++) - { - MarkConstantPoolUsageForAnnotationComponentValue(br, inUse); - } + case ElementValueAnnotationReader annotation: + MarkConstantPoolUsageForAnnotation(annotation.Annotation, inUse); + break; + case ElementValueArrayReader array: + foreach (var i in array.Values) + MarkConstantPoolUsageForAnnotationComponentValue(i, inUse); break; - default: - throw new ClassFormatError(""); } } @@ -5032,8 +4961,9 @@ private void AddMethodParameterInfo(ClassFile.Method m, MethodWrapper mw, Method { parameterNames = null; ParameterBuilder[] parameterBuilders = null; + if (wrapper.GetClassLoader().EmitDebugInfo -#if STATIC_COMPILER +#if IMPORTER || (classFile.IsPublic && (m.IsPublic || m.IsProtected)) || (m.MethodParameters != null && !wrapper.GetClassLoader().NoParameterReflection) #endif @@ -5041,57 +4971,51 @@ private void AddMethodParameterInfo(ClassFile.Method m, MethodWrapper mw, Method { parameterNames = new string[mw.GetParameters().Length]; GetParameterNamesFromMP(m, parameterNames); -#if STATIC_COMPILER +#if IMPORTER if (m.MethodParameters == null) #endif { GetParameterNamesFromLVT(m, parameterNames); GetParameterNamesFromSig(m.Signature, parameterNames); } -#if STATIC_COMPILER +#if IMPORTER wrapper.GetParameterNamesFromXml(m.Name, m.Signature, parameterNames); #endif parameterBuilders = GetParameterBuilders(mb, parameterNames.Length, parameterNames); } -#if STATIC_COMPILER + +#if IMPORTER if ((m.Modifiers & Modifiers.VarArgs) != 0 && !mw.HasCallerID) { - if (parameterBuilders == null) - { - parameterBuilders = GetParameterBuilders(mb, mw.GetParameters().Length, null); - } + parameterBuilders ??= GetParameterBuilders(mb, mw.GetParameters().Length, null); if (parameterBuilders.Length > 0) - { AttributeHelper.SetParamArrayAttribute(parameterBuilders[parameterBuilders.Length - 1]); - } } + wrapper.AddXmlMapParameterAttributes(mb, classFile.Name, m.Name, m.Signature, ref parameterBuilders); #endif + if (m.ParameterAnnotations != null) { - if (parameterBuilders == null) - { - parameterBuilders = GetParameterBuilders(mb, mw.GetParameters().Length, null); - } + parameterBuilders ??= GetParameterBuilders(mb, mw.GetParameters().Length, null); + object[][] defs = m.ParameterAnnotations; for (int j = 0; j < defs.Length; j++) { foreach (object[] def in defs[j]) { - Annotation annotation = Annotation.Load(wrapper, def); + var annotation = Annotation.Load(wrapper, def); if (annotation != null) - { annotation.Apply(wrapper.GetClassLoader(), parameterBuilders[j], def); - } } } } } -#if STATIC_COMPILER +#if IMPORTER private void AddImplementsAttribute() { - TypeWrapper[] interfaces = wrapper.Interfaces; + var interfaces = wrapper.Interfaces; if (wrapper.BaseTypeWrapper == CoreClasses.java.lang.Object.Wrapper) { // We special case classes extending java.lang.Object to optimize the metadata encoding @@ -5287,7 +5211,7 @@ internal static void EmitCallDefaultInterfaceMethod(MethodBuilder mb, MethodWrap ilgen.DoEmit(); } -#if STATIC_COMPILER +#if IMPORTER private void AddAccessStubs() { /* @@ -5564,7 +5488,7 @@ private bool ParametersAreAccessible(MethodWrapper mw) } return true; } -#endif // STATIC_COMPILER +#endif // IMPORTER private void ImplementInterfaceMethodStubs(Dictionary doneSet, TypeWrapper interfaceTypeWrapper, bool baseClassInterface) { @@ -5690,7 +5614,7 @@ private MethodBuilder DefineInterfaceStubMethod(string name, MethodWrapper mw) return mw.GetDefineMethodHelper().DefineMethod(wrapper, typeBuilder, name, MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final); } -#if !STATIC_COMPILER +#if !IMPORTER internal static class JniProxyBuilder { @@ -5740,11 +5664,11 @@ internal static void Generate(DynamicTypeWrapper.FinishContext context, CodeEmit ilGenerator.Emit(OpCodes.Ret); } } -#endif // !STATIC_COMPILER +#endif // !IMPORTER private static class JniBuilder { -#if STATIC_COMPILER +#if IMPORTER private static readonly Type localRefStructType = StaticCompiler.GetRuntimeType("IKVM.Runtime.JNI.JNIFrame"); #elif FIRST_PASS private static readonly Type localRefStructType = null; @@ -5941,7 +5865,7 @@ internal static void Generate(DynamicTypeWrapper.FinishContext context, CodeEmit private static class TraceHelper { -#if STATIC_COMPILER +#if IMPORTER private readonly static MethodInfo methodIsTracedMethod = JVM.LoadType(typeof(Tracer)).GetMethod("IsTracedMethod"); #endif private readonly static MethodInfo methodMethodInfo = JVM.LoadType(typeof(Tracer)).GetMethod("MethodInfo"); @@ -5951,7 +5875,7 @@ internal static void EmitMethodTrace(CodeEmitter ilgen, string tracemessage) if (Tracer.IsTracedMethod(tracemessage)) { CodeEmitterLabel label = ilgen.DefineLabel(); -#if STATIC_COMPILER +#if IMPORTER // TODO this should be a boolean field test instead of a call to Tracer.IsTracedMessage ilgen.Emit(OpCodes.Ldstr, tracemessage); ilgen.Emit(OpCodes.Call, methodIsTracedMethod); @@ -5964,17 +5888,17 @@ internal static void EmitMethodTrace(CodeEmitter ilgen, string tracemessage) } } -#if STATIC_COMPILER - private void EmitCallerIDStub(MethodWrapper mw, string[] parameterNames) +#if IMPORTER + + void EmitCallerIDStub(MethodWrapper mw, string[] parameterNames) { // we don't need to support custom modifiers, because there aren't any callerid methods that have parameter types that require a custom modifier - TypeWrapper[] parameters = mw.GetParameters(); - Type[] parameterTypes = new Type[parameters.Length]; + var parameters = mw.GetParameters(); + var parameterTypes = new Type[parameters.Length]; for (int i = 0; i < parameterTypes.Length; i++) - { parameterTypes[i] = parameters[i].TypeAsSignatureType; - } - MethodAttributes attribs = MethodAttributes.HideBySig; + + var attribs = MethodAttributes.HideBySig; int argcount = parameterTypes.Length; if (mw.IsStatic) { @@ -6000,44 +5924,42 @@ private void EmitCallerIDStub(MethodWrapper mw, string[] parameterNames) { attribs |= MethodAttributes.Assembly; } - MethodBuilder mb = typeBuilder.DefineMethod(mw.Name, attribs, mw.ReturnType.TypeAsSignatureType, parameterTypes); + + var mb = typeBuilder.DefineMethod(mw.Name, attribs, mw.ReturnType.TypeAsSignatureType, parameterTypes); AttributeHelper.HideFromJava(mb); mb.SetImplementationFlags(MethodImplAttributes.NoInlining); - CodeEmitter ilgen = CodeEmitter.Create(mb); + + var ilgen = CodeEmitter.Create(mb); for (int i = 0; i < argcount; i++) { if (parameterNames != null && (mw.IsStatic || i > 0)) { - ParameterBuilder pb = mb.DefineParameter(mw.IsStatic ? i + 1 : i, ParameterAttributes.None, parameterNames[mw.IsStatic ? i : i - 1]); + var pb = mb.DefineParameter(mw.IsStatic ? i + 1 : i, ParameterAttributes.None, parameterNames[mw.IsStatic ? i : i - 1]); if (i == argcount - 1 && (mw.Modifiers & Modifiers.VarArgs) != 0) - { AttributeHelper.SetParamArrayAttribute(pb); - } } + ilgen.EmitLdarg(i); } ilgen.Emit(OpCodes.Ldc_I4_1); ilgen.Emit(OpCodes.Ldc_I4_0); ilgen.Emit(OpCodes.Newobj, JVM.Import(typeof(StackFrame)).GetConstructor(new Type[] { Types.Int32, Types.Boolean })); - MethodWrapper callerID = CoreClasses.ikvm.@internal.CallerID.Wrapper.GetMethodWrapper("create", "(Lcli.System.Diagnostics.StackFrame;)Likvm.internal.CallerID;", false); + var callerID = CoreClasses.ikvm.@internal.CallerID.Wrapper.GetMethodWrapper("create", "(Lcli.System.Diagnostics.StackFrame;)Likvm.internal.CallerID;", false); callerID.Link(); callerID.EmitCall(ilgen); if (mw.IsStatic) - { mw.EmitCall(ilgen); - } else - { mw.EmitCallvirt(ilgen); - } ilgen.Emit(OpCodes.Ret); ilgen.DoEmit(); } -#endif // STATIC_COMPILER - private void ImplementInterfaces(TypeWrapper[] interfaces, List interfaceList) +#endif // IMPORTER + + void ImplementInterfaces(TypeWrapper[] interfaces, List interfaceList) { - foreach (TypeWrapper iface in interfaces) + foreach (var iface in interfaces) { if (!interfaceList.Contains(iface)) { @@ -6048,26 +5970,25 @@ private void ImplementInterfaces(TypeWrapper[] interfaces, List int } // NOTE we're using TypeAsBaseType for the interfaces! typeBuilder.AddInterfaceImplementation(iface.TypeAsBaseType); -#if STATIC_COMPILER +#if IMPORTER if (!wrapper.IsInterface) { // look for "magic" interfaces that imply a .NET interface if (iface.GetClassLoader() == CoreClasses.java.lang.Object.Wrapper.GetClassLoader()) { - if (iface.Name == "java.lang.Iterable" - && !wrapper.ImplementsInterface(ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(System.Collections.IEnumerable))))) + if (iface.Name == "java.lang.Iterable" && !wrapper.ImplementsInterface(ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(System.Collections.IEnumerable))))) { - TypeWrapper enumeratorType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedNameFast("ikvm.lang.IterableEnumerator"); + var enumeratorType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedNameFast("ikvm.lang.IterableEnumerator"); if (enumeratorType != null) { typeBuilder.AddInterfaceImplementation(JVM.Import(typeof(System.Collections.IEnumerable))); // FXBUG we're using the same method name as the C# compiler here because both the .NET and Mono implementations of Xml serialization depend on this method name - MethodBuilder mb = typeBuilder.DefineMethod("System.Collections.IEnumerable.GetEnumerator", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName, JVM.Import(typeof(System.Collections.IEnumerator)), Type.EmptyTypes); + var mb = typeBuilder.DefineMethod("System.Collections.IEnumerable.GetEnumerator", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName, JVM.Import(typeof(System.Collections.IEnumerator)), Type.EmptyTypes); AttributeHelper.HideFromJava(mb); typeBuilder.DefineMethodOverride(mb, JVM.Import(typeof(System.Collections.IEnumerable)).GetMethod("GetEnumerator")); - CodeEmitter ilgen = CodeEmitter.Create(mb); + var ilgen = CodeEmitter.Create(mb); ilgen.Emit(OpCodes.Ldarg_0); - MethodWrapper mw = enumeratorType.GetMethodWrapper("", "(Ljava.lang.Iterable;)V", false); + var mw = enumeratorType.GetMethodWrapper("", "(Ljava.lang.Iterable;)V", false); mw.Link(); mw.EmitNewobj(ilgen); ilgen.Emit(OpCodes.Ret); @@ -6078,10 +5999,10 @@ private void ImplementInterfaces(TypeWrapper[] interfaces, List int // if we implement a ghost interface, add an implicit conversion to the ghost reference value type if (iface.IsGhost && wrapper.IsPublic) { - MethodBuilder mb = typeBuilder.DefineMethod("op_Implicit", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.SpecialName, iface.TypeAsSignatureType, new Type[] { wrapper.TypeAsSignatureType }); + var mb = typeBuilder.DefineMethod("op_Implicit", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.SpecialName, iface.TypeAsSignatureType, new Type[] { wrapper.TypeAsSignatureType }); AttributeHelper.HideFromJava(mb); - CodeEmitter ilgen = CodeEmitter.Create(mb); - CodeEmitterLocal local = ilgen.DeclareLocal(iface.TypeAsSignatureType); + var ilgen = CodeEmitter.Create(mb); + var local = ilgen.DeclareLocal(iface.TypeAsSignatureType); ilgen.Emit(OpCodes.Ldloca, local); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Stfld, iface.GhostRefField); @@ -6091,7 +6012,7 @@ private void ImplementInterfaces(TypeWrapper[] interfaces, List int ilgen.DoEmit(); } } -#endif // STATIC_COMPILER +#endif // IMPORTER // NOTE we're recursively "implementing" all interfaces that we inherit from the interfaces we implement. // The C# compiler also does this and the Compact Framework requires it. ImplementInterfaces(iface.Interfaces, interfaceList); @@ -6099,84 +6020,76 @@ private void ImplementInterfaces(TypeWrapper[] interfaces, List int } } - private void AddUnsupportedAbstractMethods() + void AddUnsupportedAbstractMethods() { - foreach (MethodBase mb in wrapper.BaseTypeWrapper.TypeAsBaseType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - { + foreach (var mb in wrapper.BaseTypeWrapper.TypeAsBaseType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) if (DotNetTypeWrapper.IsUnsupportedAbstractMethod(mb)) - { GenerateUnsupportedAbstractMethodStub(mb); - } - } - Dictionary h = new Dictionary(); - TypeWrapper tw = wrapper; + + var h = new Dictionary(); + var tw = (TypeWrapper)wrapper; while (tw != null) { - foreach (TypeWrapper iface in tw.Interfaces) + foreach (var iface in tw.Interfaces) { - foreach (MethodBase mb in iface.TypeAsBaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) + foreach (var mb in iface.TypeAsBaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { if (!h.ContainsKey(mb)) { h.Add(mb, mb); if (DotNetTypeWrapper.IsUnsupportedAbstractMethod(mb)) - { GenerateUnsupportedAbstractMethodStub(mb); - } } } } + tw = tw.BaseTypeWrapper; } } - private void GenerateUnsupportedAbstractMethodStub(MethodBase mb) + void GenerateUnsupportedAbstractMethodStub(MethodBase mb) { - ParameterInfo[] parameters = mb.GetParameters(); - Type[] parameterTypes = new Type[parameters.Length]; + var parameters = mb.GetParameters(); + var parameterTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) - { parameterTypes[i] = parameters[i].ParameterType; - } - MethodAttributes attr = MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Private; - MethodBuilder m = typeBuilder.DefineMethod("__" + mb.DeclaringType.FullName + "/" + mb.Name, attr, ((MethodInfo)mb).ReturnType, parameterTypes); + + var attr = MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Private; + var m = typeBuilder.DefineMethod("__" + mb.DeclaringType.FullName + "/" + mb.Name, attr, ((MethodInfo)mb).ReturnType, parameterTypes); if (mb.IsGenericMethodDefinition) - { CopyGenericArguments(mb, m); - } - CodeEmitter ilgen = CodeEmitter.Create(m); + + var ilgen = CodeEmitter.Create(m); ilgen.EmitThrow("java.lang.AbstractMethodError", "Method " + mb.DeclaringType.FullName + "." + mb.Name + " is unsupported by IKVM."); ilgen.DoEmit(); typeBuilder.DefineMethodOverride(m, (MethodInfo)mb); } - private static void CopyGenericArguments(MethodBase mi, MethodBuilder mb) + static void CopyGenericArguments(MethodBase mi, MethodBuilder mb) { - Type[] genericParameters = mi.GetGenericArguments(); - string[] genParamNames = new string[genericParameters.Length]; + var genericParameters = mi.GetGenericArguments(); + var genParamNames = new string[genericParameters.Length]; for (int i = 0; i < genParamNames.Length; i++) - { genParamNames[i] = genericParameters[i].Name; - } - GenericTypeParameterBuilder[] genParamBuilders = mb.DefineGenericParameters(genParamNames); + + var genParamBuilders = mb.DefineGenericParameters(genParamNames); for (int i = 0; i < genParamBuilders.Length; i++) { // NOTE apparently we don't need to set the interface constraints // (and if we do, it fails for some reason) if (genericParameters[i].BaseType != Types.Object) - { genParamBuilders[i].SetBaseTypeConstraint(genericParameters[i].BaseType); - } + genParamBuilders[i].SetGenericParameterAttributes(genericParameters[i].GenericParameterAttributes); } } - private void CompileConstructorBody(FinishContext context, CodeEmitter ilGenerator, int methodIndex) + void CompileConstructorBody(FinishContext context, CodeEmitter ilGenerator, int methodIndex) { - MethodWrapper[] methods = wrapper.GetMethods(); - ClassFile.Method m = classFile.Methods[methodIndex]; + var methods = wrapper.GetMethods(); + var m = classFile.Methods[methodIndex]; TraceHelper.EmitMethodTrace(ilGenerator, classFile.Name + "." + m.Name + m.Signature); -#if STATIC_COMPILER +#if IMPORTER // do we have an implementation in map.xml? if (wrapper.EmitMapXmlMethodPrologueAndOrBody(ilGenerator, classFile, m)) { @@ -6184,50 +6097,47 @@ private void CompileConstructorBody(FinishContext context, CodeEmitter ilGenerat return; } #endif - bool nonLeaf = false; + var nonLeaf = false; Compiler.Compile(context, host, wrapper, methods[methodIndex], classFile, m, ilGenerator, ref nonLeaf); ilGenerator.DoEmit(); -#if STATIC_COMPILER +#if IMPORTER ilGenerator.EmitLineNumberTable((MethodBuilder)methods[methodIndex].GetMethod()); -#else // STATIC_COMPILER - byte[] linenumbers = ilGenerator.GetLineNumberTable(); +#else // IMPORTER + var linenumbers = ilGenerator.GetLineNumberTable(); if (linenumbers != null) { if (wrapper.lineNumberTables == null) - { wrapper.lineNumberTables = new byte[methods.Length][]; - } + wrapper.lineNumberTables[methodIndex] = linenumbers; } -#endif // STATIC_COMPILER +#endif // IMPORTER } - private static bool IsCompatibleArgList(TypeWrapper[] caller, TypeWrapper[] callee) + static bool IsCompatibleArgList(TypeWrapper[] caller, TypeWrapper[] callee) { if (caller.Length == callee.Length) { for (int i = 0; i < caller.Length; i++) { if (caller[i].IsUnloadable || callee[i].IsUnloadable) - { return false; - } if (!caller[i].IsAssignableTo(callee[i])) - { return false; - } } + return true; } + return false; } - private void EmitCallerIDInitialization(CodeEmitter ilGenerator, FieldInfo callerIDField) + void EmitCallerIDInitialization(CodeEmitter ilGenerator, FieldInfo callerIDField) { - TypeWrapper tw = CoreClasses.ikvm.@internal.CallerID.Wrapper; + var tw = CoreClasses.ikvm.@internal.CallerID.Wrapper; if (tw.InternalsVisibleTo(wrapper)) { - MethodWrapper create = tw.GetMethodWrapper("create", "(Lcli.System.RuntimeTypeHandle;)Likvm.internal.CallerID;", false); + var create = tw.GetMethodWrapper("create", "(Lcli.System.RuntimeTypeHandle;)Likvm.internal.CallerID;", false); ilGenerator.Emit(OpCodes.Ldtoken, this.typeBuilder); create.Link(); create.EmitCall(ilGenerator); @@ -6236,17 +6146,18 @@ private void EmitCallerIDInitialization(CodeEmitter ilGenerator, FieldInfo calle { RegisterNestedTypeBuilder(EmitCreateCallerID(typeBuilder, ilGenerator)); } + ilGenerator.Emit(OpCodes.Stsfld, callerIDField); } internal static TypeBuilder EmitCreateCallerID(TypeBuilder typeBuilder, CodeEmitter ilGenerator) { - TypeWrapper tw = CoreClasses.ikvm.@internal.CallerID.Wrapper; - TypeBuilder typeCallerID = typeBuilder.DefineNestedType(NestedTypeName.CallerID, TypeAttributes.Sealed | TypeAttributes.NestedPrivate, tw.TypeAsBaseType); - MethodBuilder cb = ReflectUtil.DefineConstructor(typeCallerID, MethodAttributes.Assembly, null); - CodeEmitter ctorIlgen = CodeEmitter.Create(cb); + var tw = CoreClasses.ikvm.@internal.CallerID.Wrapper; + var typeCallerID = typeBuilder.DefineNestedType(NestedTypeName.CallerID, TypeAttributes.Sealed | TypeAttributes.NestedPrivate, tw.TypeAsBaseType); + var cb = ReflectUtil.DefineConstructor(typeCallerID, MethodAttributes.Assembly, null); + var ctorIlgen = CodeEmitter.Create(cb); ctorIlgen.Emit(OpCodes.Ldarg_0); - MethodWrapper mw = tw.GetMethodWrapper("", "()V", false); + var mw = tw.GetMethodWrapper("", "()V", false); mw.Link(); mw.EmitCall(ctorIlgen); ctorIlgen.Emit(OpCodes.Ret); @@ -6255,7 +6166,7 @@ internal static TypeBuilder EmitCreateCallerID(TypeBuilder typeBuilder, CodeEmit return typeCallerID; } - private void EmitConstantValueInitialization(FieldWrapper[] fields, CodeEmitter ilGenerator) + void EmitConstantValueInitialization(FieldWrapper[] fields, CodeEmitter ilGenerator) { ClassFile.Field[] flds = classFile.Fields; for (int i = 0; i < flds.Length; i++) @@ -6314,40 +6225,41 @@ private void EmitConstantValueInitialization(FieldWrapper[] fields, CodeEmitter internal MethodBuilder DefineThreadLocalType() { - TypeWrapper threadLocal = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.IntrinsicThreadLocal"); + var threadLocal = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.IntrinsicThreadLocal"); int id = nestedTypeBuilders == null ? 0 : nestedTypeBuilders.Count; - TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.ThreadLocal + id, TypeAttributes.NestedPrivate | TypeAttributes.Sealed, threadLocal.TypeAsBaseType); - FieldBuilder fb = tb.DefineField("field", Types.Object, FieldAttributes.Private | FieldAttributes.Static); + var tb = typeBuilder.DefineNestedType(NestedTypeName.ThreadLocal + id, TypeAttributes.NestedPrivate | TypeAttributes.Sealed, threadLocal.TypeAsBaseType); + var fb = tb.DefineField("field", Types.Object, FieldAttributes.Private | FieldAttributes.Static); fb.SetCustomAttribute(new CustomAttributeBuilder(JVM.Import(typeof(ThreadStaticAttribute)).GetConstructor(Type.EmptyTypes), new object[0])); - MethodBuilder mbGet = tb.DefineMethod("get", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, Types.Object, Type.EmptyTypes); - ILGenerator ilgen = mbGet.GetILGenerator(); + + var mbGet = tb.DefineMethod("get", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, Types.Object, Type.EmptyTypes); + var ilgen = mbGet.GetILGenerator(); ilgen.Emit(OpCodes.Ldsfld, fb); ilgen.Emit(OpCodes.Ret); - MethodBuilder mbSet = tb.DefineMethod("set", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, null, new Type[] { Types.Object }); + + var mbSet = tb.DefineMethod("set", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, null, new Type[] { Types.Object }); ilgen = mbSet.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_1); ilgen.Emit(OpCodes.Stsfld, fb); ilgen.Emit(OpCodes.Ret); - MethodBuilder cb = ReflectUtil.DefineConstructor(tb, MethodAttributes.Assembly, Type.EmptyTypes); - CodeEmitter ctorilgen = CodeEmitter.Create(cb); + + var cb = ReflectUtil.DefineConstructor(tb, MethodAttributes.Assembly, Type.EmptyTypes); + var ctorilgen = CodeEmitter.Create(cb); ctorilgen.Emit(OpCodes.Ldarg_0); - MethodWrapper basector = threadLocal.GetMethodWrapper("", "()V", false); + var basector = threadLocal.GetMethodWrapper("", "()V", false); basector.Link(); basector.EmitCall(ctorilgen); ctorilgen.Emit(OpCodes.Ret); ctorilgen.DoEmit(); + RegisterNestedTypeBuilder(tb); return cb; } internal MethodBuilder GetAtomicReferenceFieldUpdater(FieldWrapper field) { - if (arfuMap == null) - { - arfuMap = new Dictionary(); - } - MethodBuilder cb; - if (!arfuMap.TryGetValue(field, out cb)) + arfuMap ??= new Dictionary(); + + if (!arfuMap.TryGetValue(field, out var cb)) { TypeWrapper arfuTypeWrapper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.IntrinsicAtomicReferenceFieldUpdater"); TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.AtomicReferenceFieldUpdater + arfuMap.Count, TypeAttributes.NestedPrivate | TypeAttributes.Sealed, arfuTypeWrapper.TypeAsBaseType); @@ -6369,21 +6281,21 @@ internal MethodBuilder GetAtomicReferenceFieldUpdater(FieldWrapper field) internal TypeBuilder DefineIndyCallSiteType() { int id = nestedTypeBuilders == null ? 0 : nestedTypeBuilders.Count; - TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.IndyCallSite + id, TypeAttributes.NestedPrivate | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); + var tb = typeBuilder.DefineNestedType(NestedTypeName.IndyCallSite + id, TypeAttributes.NestedPrivate | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); RegisterNestedTypeBuilder(tb); return tb; } internal TypeBuilder DefineMethodHandleConstantType(int index) { - TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.MethodHandleConstant + index, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit); ; + var tb = typeBuilder.DefineNestedType(NestedTypeName.MethodHandleConstant + index, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit); ; RegisterNestedTypeBuilder(tb); return tb; } internal TypeBuilder DefineMethodTypeConstantType(int index) { - TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.MethodTypeConstant + index, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit); + var tb = typeBuilder.DefineNestedType(NestedTypeName.MethodTypeConstant + index, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit); RegisterNestedTypeBuilder(tb); return tb; } @@ -6392,14 +6304,14 @@ internal TypeBuilder DefineMethodTypeConstantType(int index) internal TypeBuilder DefineAnonymousClass() { int id = nestedTypeBuilders == null ? 0 : nestedTypeBuilders.Count; - TypeBuilder tb = typeBuilder.DefineNestedType(NestedTypeName.IntrinsifiedAnonymousClass + id, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.SpecialName | TypeAttributes.BeforeFieldInit); + var tb = typeBuilder.DefineNestedType(NestedTypeName.IntrinsifiedAnonymousClass + id, TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.SpecialName | TypeAttributes.BeforeFieldInit); RegisterNestedTypeBuilder(tb); return tb; } - private MethodBuilder DefineHelperMethod(string name, Type returnType, Type[] parameterTypes) + MethodBuilder DefineHelperMethod(string name, Type returnType, Type[] parameterTypes) { -#if STATIC_COMPILER +#if IMPORTER // FXBUG csc.exe doesn't like non-public methods in interfaces, so for public interfaces we move // the helper methods into a nested type. if (wrapper.IsPublic && wrapper.IsInterface && wrapper.classLoader.WorkaroundInterfacePrivateMethods) @@ -6412,6 +6324,7 @@ private MethodBuilder DefineHelperMethod(string name, Type returnType, Type[] pa return interfaceHelperMethodsTypeBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.Static, returnType, parameterTypes); } #endif + return typeBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.Static, returnType, parameterTypes); } @@ -6442,112 +6355,97 @@ internal MethodBuilder DefineDelegateInvokeErrorStub(Type returnType, Type[] par internal MethodInfo GetInvokeSpecialStub(MethodWrapper method) { - if (invokespecialstubcache == null) - { - invokespecialstubcache = new Dictionary(); - } - MethodKey key = new MethodKey(method.DeclaringType.Name, method.Name, method.Signature); - MethodInfo mi; - if (!invokespecialstubcache.TryGetValue(key, out mi)) + invokespecialstubcache ??= new Dictionary(); + + var key = new MethodKey(method.DeclaringType.Name, method.Name, method.Signature); + + if (!invokespecialstubcache.TryGetValue(key, out var mi)) { - DefineMethodHelper dmh = method.GetDefineMethodHelper(); - MethodBuilder stub = dmh.DefineMethod(wrapper, typeBuilder, "__<>", MethodAttributes.PrivateScope); - CodeEmitter ilgen = CodeEmitter.Create(stub); + var dmh = method.GetDefineMethodHelper(); + var stub = dmh.DefineMethod(wrapper, typeBuilder, "__<>", MethodAttributes.PrivateScope); + var ilgen = CodeEmitter.Create(stub); ilgen.Emit(OpCodes.Ldarg_0); for (int i = 1; i <= dmh.ParameterCount; i++) - { ilgen.EmitLdarg(i); - } method.EmitCall(ilgen); ilgen.Emit(OpCodes.Ret); ilgen.DoEmit(); invokespecialstubcache[key] = stub; mi = stub; } + return mi; } -#if !STATIC_COMPILER +#if !IMPORTER + internal void EmitLiveObjectLoad(CodeEmitter ilgen, object value) { - if (liveObjects == null) - { - liveObjects = new List(); - } - FieldInfo fi = TypeBuilder.GetField(typeof(IKVM.Runtime.LiveObjectHolder<>).MakeGenericType(typeBuilder), typeof(IKVM.Runtime.LiveObjectHolder<>).GetField("values", BindingFlags.Static | BindingFlags.Public)); + liveObjects ??= new List(); + var fi = TypeBuilder.GetField(typeof(IKVM.Runtime.LiveObjectHolder<>).MakeGenericType(typeBuilder), typeof(IKVM.Runtime.LiveObjectHolder<>).GetField("values", BindingFlags.Static | BindingFlags.Public)); ilgen.Emit(OpCodes.Ldsfld, fi); ilgen.EmitLdc_I4(liveObjects.Count); ilgen.Emit(OpCodes.Ldelem_Ref); liveObjects.Add(value); } + #endif } - private static bool CheckRequireOverrideStub(MethodWrapper mw1, MethodWrapper mw2) + static bool CheckRequireOverrideStub(MethodWrapper mw1, MethodWrapper mw2) { // TODO this is too late to generate LinkageErrors so we need to figure this out earlier if (!TypesMatchForOverride(mw1.ReturnType, mw2.ReturnType)) - { return true; - } - TypeWrapper[] args1 = mw1.GetParameters(); - TypeWrapper[] args2 = mw2.GetParameters(); + + var args1 = mw1.GetParameters(); + var args2 = mw2.GetParameters(); for (int i = 0; i < args1.Length; i++) - { if (!TypesMatchForOverride(args1[i], args2[i])) - { return true; - } - } + return false; } - private static bool TypesMatchForOverride(TypeWrapper tw1, TypeWrapper tw2) + static bool TypesMatchForOverride(TypeWrapper tw1, TypeWrapper tw2) { if (tw1 == tw2) - { return true; - } else if (tw1.IsUnloadable && tw2.IsUnloadable) - { return ((UnloadableTypeWrapper)tw1).CustomModifier == ((UnloadableTypeWrapper)tw2).CustomModifier; - } else - { return false; - } } - private void GenerateOverrideStub(TypeBuilder typeBuilder, MethodWrapper baseMethod, MethodInfo target, MethodWrapper targetMethod) + void GenerateOverrideStub(TypeBuilder typeBuilder, MethodWrapper baseMethod, MethodInfo target, MethodWrapper targetMethod) { Debug.Assert(!baseMethod.HasCallerID); - MethodBuilder overrideStub = baseMethod.GetDefineMethodHelper().DefineMethod(this, typeBuilder, "__" + baseMethod.DeclaringType.Name + "::" + baseMethod.Name, MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final); + var overrideStub = baseMethod.GetDefineMethodHelper().DefineMethod(this, typeBuilder, "__" + baseMethod.DeclaringType.Name + "::" + baseMethod.Name, MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final); typeBuilder.DefineMethodOverride(overrideStub, (MethodInfo)baseMethod.GetMethod()); - TypeWrapper[] stubargs = baseMethod.GetParameters(); - TypeWrapper[] targetArgs = targetMethod.GetParameters(); - CodeEmitter ilgen = CodeEmitter.Create(overrideStub); + var stubargs = baseMethod.GetParameters(); + var targetArgs = targetMethod.GetParameters(); + var ilgen = CodeEmitter.Create(overrideStub); ilgen.Emit(OpCodes.Ldarg_0); + for (int i = 0; i < targetArgs.Length; i++) { ilgen.EmitLdarg(i + 1); ConvertStubArg(stubargs[i], targetArgs[i], ilgen); } + if (target != null) - { ilgen.Emit(OpCodes.Callvirt, target); - } else - { targetMethod.EmitCallvirt(ilgen); - } + ConvertStubArg(targetMethod.ReturnType, baseMethod.ReturnType, ilgen); ilgen.Emit(OpCodes.Ret); ilgen.DoEmit(); } - private static void ConvertStubArg(TypeWrapper src, TypeWrapper dst, CodeEmitter ilgen) + static void ConvertStubArg(TypeWrapper src, TypeWrapper dst, CodeEmitter ilgen) { if (src != dst) { @@ -6569,9 +6467,9 @@ private static void ConvertStubArg(TypeWrapper src, TypeWrapper dst, CodeEmitter } } - private static void GetParameterNamesFromMP(ClassFile.Method m, string[] parameterNames) + static void GetParameterNamesFromMP(ClassFile.Method m, string[] parameterNames) { - MethodParametersEntry[] methodParameters = m.MethodParameters; + var methodParameters = m.MethodParameters; if (methodParameters != null) { for (int i = 0, count = Math.Min(parameterNames.Length, methodParameters.Length); i < count; i++) @@ -6586,7 +6484,7 @@ private static void GetParameterNamesFromMP(ClassFile.Method m, string[] paramet protected static void GetParameterNamesFromLVT(ClassFile.Method m, string[] parameterNames) { - ClassFile.Method.LocalVariableTableEntry[] localVars = m.LocalVariableTableAttribute; + var localVars = m.LocalVariableTableAttribute; if (localVars != null) { for (int i = m.IsStatic ? 0 : 1, pos = 0; i < m.ArgMap.Length; i++) @@ -6605,6 +6503,7 @@ protected static void GetParameterNamesFromLVT(ClassFile.Method m, string[] para } } } + pos++; } } @@ -6613,7 +6512,7 @@ protected static void GetParameterNamesFromLVT(ClassFile.Method m, string[] para protected static void GetParameterNamesFromSig(string sig, string[] parameterNames) { - List names = new List(); + var names = new List(); for (int i = 1; sig[i] != ')'; i++) { if (sig[i] == 'L') @@ -6691,18 +6590,15 @@ protected static void GetParameterNamesFromSig(string sig, string[] parameterNam } } } + for (int i = 0; i < parameterNames.Length; i++) - { if (parameterNames[i] == null) - { parameterNames[i] = names[i]; - } - } } protected static ParameterBuilder[] GetParameterBuilders(MethodBuilder mb, int parameterCount, string[] parameterNames) { - ParameterBuilder[] parameterBuilders = new ParameterBuilder[parameterCount]; + var parameterBuilders = new ParameterBuilder[parameterCount]; Dictionary clashes = null; for (int i = 0; i < parameterBuilders.Length; i++) { @@ -6712,15 +6608,12 @@ protected static ParameterBuilder[] GetParameterBuilders(MethodBuilder mb, int p name = parameterNames[i]; if (Array.IndexOf(parameterNames, name, i + 1) >= 0 || (clashes != null && clashes.ContainsKey(name))) { - if (clashes == null) - { - clashes = new Dictionary(); - } + clashes ??= new Dictionary(); + int clash = 1; if (clashes.ContainsKey(name)) - { clash = clashes[name] + 1; - } + clashes[name] = clash; name += clash; } @@ -6742,19 +6635,17 @@ private static string GetParameterName(string type) } else { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); + var sb = new System.Text.StringBuilder(); for (int i = type.LastIndexOf('.') + 1; i < type.Length; i++) - { if (char.IsUpper(type, i)) - { sb.Append(char.ToLower(type[i])); - } - } + return sb.ToString(); } } -#if STATIC_COMPILER +#if IMPORTER + protected abstract void AddMapXmlFields(ref FieldWrapper[] fields); protected abstract bool EmitMapXmlMethodPrologueAndOrBody(CodeEmitter ilgen, ClassFile f, ClassFile.Method m); protected abstract void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile classFile, FieldWrapper[] fields, MethodWrapper[] methods); @@ -6762,24 +6653,25 @@ private static string GetParameterName(string type) protected abstract void FinishGhost(TypeBuilder typeBuilder, MethodWrapper[] methods); protected abstract void FinishGhostStep2(); protected abstract TypeBuilder DefineGhostType(string mangledTypeName, TypeAttributes typeAttribs); -#endif // STATIC_COMPILER + +#endif // IMPORTER private bool IsPInvokeMethod(ClassFile.Method m) { -#if STATIC_COMPILER - Dictionary mapxml = classLoader.GetMapXmlClasses(); +#if IMPORTER + Dictionary mapxml = classLoader.GetMapXmlClasses(); if (mapxml != null) { - IKVM.Internal.MapXml.Class clazz; + IKVM.Tools.Importer.MapXml.Class clazz; if (mapxml.TryGetValue(this.Name, out clazz) && clazz.Methods != null) { - foreach (IKVM.Internal.MapXml.Method method in clazz.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method method in clazz.Methods) { if (method.Name == m.Name && method.Sig == m.Signature) { if (method.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in method.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in method.Attributes) { if (StaticCompiler.GetType(classLoader, attr.Type) == JVM.Import(typeof(System.Runtime.InteropServices.DllImportAttribute))) { @@ -6876,64 +6768,95 @@ internal override MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) return null; } -#if !STATIC_COMPILER +#if !IMPORTER + internal override string[] GetEnclosingMethod() { return impl.GetEnclosingMethod(); } + /// + /// Gets the name of the source file that declared this type. + /// + /// internal override string GetSourceFileName() { return sourceFileName; } + /// + /// Gets the metadata token of the specified method. + /// + /// + /// int GetMethodBaseToken(MethodBase mb) { -#if NETFRAMEWORK if (mb is MethodBuilder mbld) + { +#if NETFRAMEWORK return mbld.GetToken().Token; +#else + try + { + return mbld.GetMetadataToken(); + } + catch (InvalidOperationException) + { + if (MetadataTokenInternalPropertyInfo != null) + return (int)MetadataTokenInternalPropertyInfo.GetValue(mbld); + } #endif + } +#if NETFRAMEWORK return mb.MetadataToken; +#else + return mb.GetMetadataToken(); +#endif } + /// + /// Gets the line number within the original source of the specified method that maps to the specified offset in the IL. + /// + /// + /// + /// internal override int GetSourceLineNumber(MethodBase mb, int ilOffset) { if (lineNumberTables != null) { - int token = GetMethodBaseToken(mb); - MethodWrapper[] methods = GetMethods(); + var token = GetMethodBaseToken(mb); + var methods = GetMethods(); for (int i = 0; i < methods.Length; i++) { if (GetMethodBaseToken(methods[i].GetMethod()) == token) { if (lineNumberTables[i] != null) - { return new LineNumberTableAttribute(lineNumberTables[i]).GetLineNumber(ilOffset); - } + break; } } } + return -1; } - private object[] DecodeAnnotations(object[] definitions) + object[] DecodeAnnotations(object[] definitions) { if (definitions == null) - { return null; - } - java.lang.ClassLoader loader = GetClassLoader().GetJavaClassLoader(); - List annotations = new List(); + + var loader = GetClassLoader().GetJavaClassLoader(); + var annotations = new List(); + for (int i = 0; i < definitions.Length; i++) { - object obj = JVM.NewAnnotation(loader, definitions[i]); + var obj = JVM.NewAnnotation(loader, definitions[i]); if (obj != null) - { annotations.Add(obj); - } } + return annotations.ToArray(); } @@ -7016,9 +6939,10 @@ private Type GetBaseTypeForDefineType() { return BaseTypeWrapper.TypeAsBaseType; } + #endif -#if STATIC_COMPILER +#if IMPORTER protected virtual Type GetBaseTypeForDefineType() { return BaseTypeWrapper.TypeAsBaseType; @@ -7028,7 +6952,7 @@ internal virtual MethodWrapper[] GetReplacedMethodsFor(MethodWrapper mw) { return null; } -#endif // STATIC_COMPILER +#endif // IMPORTER internal override MethodBase GetSerializationConstructor() { @@ -7084,7 +7008,7 @@ private static Type GetModOptHelper(TypeWrapper tw) } } -#if STATIC_COMPILER +#if IMPORTER private bool NeedsType2AccessStub(FieldWrapper fw) { Debug.Assert(this.IsPublic && fw.DeclaringType == this); @@ -7107,35 +7031,39 @@ internal static bool RequiresDynamicReflectionCallerClass(string classFile, stri internal override object[] GetConstantPool() { + Finish(); return impl.GetConstantPool(); } internal override byte[] GetRawTypeAnnotations() { + Finish(); return impl.GetRawTypeAnnotations(); } internal override byte[] GetMethodRawTypeAnnotations(MethodWrapper mw) { + Finish(); return impl.GetMethodRawTypeAnnotations(Array.IndexOf(GetMethods(), mw)); } internal override byte[] GetFieldRawTypeAnnotations(FieldWrapper fw) { + Finish(); return impl.GetFieldRawTypeAnnotations(Array.IndexOf(GetFields(), fw)); } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal override TypeWrapper Host { get { return impl.Host; } } #endif - [Conditional("STATIC_COMPILER")] + [Conditional("IMPORTER")] internal void EmitLevel4Warning(HardError error, string message) { -#if STATIC_COMPILER +#if IMPORTER if (GetClassLoader().WarningLevelHigh) { switch (error) @@ -7217,11 +7145,14 @@ internal MethodBuilder DefineConstructor(TypeWrapperFactory context, TypeBuilder { return DefineMethod(context, tb, ConstructorInfo.ConstructorName, attribs | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); } + } -#if !STATIC_COMPILER +#if !IMPORTER + sealed class DynamicCallerIDProvider { + // this object acts as a capability that is passed to trusted code to allow the DynamicCallerID() // method to be public without giving untrusted code the ability to forge a CallerID token internal static readonly DynamicCallerIDProvider Instance = new DynamicCallerIDProvider(); @@ -7256,6 +7187,9 @@ internal static ikvm.@internal.CallerID CreateCallerID(TypeWrapper tw) return ikvm.@internal.CallerID.create(tw.ClassObject, tw.GetClassLoader().GetJavaClassLoader()); #endif } + } + #endif + } \ No newline at end of file diff --git a/src/IKVM.Runtime/EnumHelper.cs b/src/IKVM.Runtime/EnumHelper.cs new file mode 100644 index 0000000000..2e6550984f --- /dev/null +++ b/src/IKVM.Runtime/EnumHelper.cs @@ -0,0 +1,152 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + static class EnumHelper + { + internal static Type GetUnderlyingType(Type enumType) + { +#if IMPORTER || EXPORTER + return enumType.GetEnumUnderlyingType(); +#else + return Enum.GetUnderlyingType(enumType); +#endif + } + +#if IMPORTER + internal static object Parse(Type type, string value) + { + object retval = null; + foreach (string str in value.Split(',')) + { + FieldInfo field = type.GetField(str.Trim(), BindingFlags.Public | BindingFlags.Static); + if (field == null) + { + throw new InvalidOperationException("Enum value '" + str + "' not found in " + type.FullName); + } + if (retval == null) + { + retval = field.GetRawConstantValue(); + } + else + { + retval = OrBoxedIntegrals(retval, field.GetRawConstantValue()); + } + } + return retval; + } +#endif + + // note that we only support the integer types that C# supports + // (the CLI also supports bool, char, IntPtr & UIntPtr) + internal static object OrBoxedIntegrals(object v1, object v2) + { + Debug.Assert(v1.GetType() == v2.GetType()); + if (v1 is ulong) + { + ulong l1 = (ulong)v1; + ulong l2 = (ulong)v2; + return l1 | l2; + } + else + { + long v = ((IConvertible)v1).ToInt64(null) | ((IConvertible)v2).ToInt64(null); + switch (Type.GetTypeCode(JVM.Import(v1.GetType()))) + { + case TypeCode.SByte: + return (sbyte)v; + case TypeCode.Byte: + return (byte)v; + case TypeCode.Int16: + return (short)v; + case TypeCode.UInt16: + return (ushort)v; + case TypeCode.Int32: + return (int)v; + case TypeCode.UInt32: + return (uint)v; + case TypeCode.Int64: + return (long)v; + default: + throw new InvalidOperationException(); + } + } + } + + // this method can be used to convert an enum value or its underlying value to a Java primitive + internal static object GetPrimitiveValue(Type underlyingType, object obj) + { + // Note that this method doesn't trust that obj is of the correct type, + // because it turns out there exist assemblies (e.g. gtk-sharp.dll) that + // have incorrectly typed enum constant values (e.g. int32 instead of uint32). + long value; + if (obj is ulong || (obj is Enum && underlyingType == Types.UInt64)) + { + value = unchecked((long)((IConvertible)obj).ToUInt64(null)); + } + else + { + value = ((IConvertible)obj).ToInt64(null); + } + if (underlyingType == Types.SByte || underlyingType == Types.Byte) + { + return unchecked((byte)value); + } + else if (underlyingType == Types.Int16 || underlyingType == Types.UInt16) + { + return unchecked((short)value); + } + else if (underlyingType == Types.Int32 || underlyingType == Types.UInt32) + { + return unchecked((int)value); + } + else if (underlyingType == Types.Int64 || underlyingType == Types.UInt64) + { + return value; + } + else + { + throw new InvalidOperationException(); + } + } + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/And.cs b/src/IKVM.Runtime/ExModifiers.cs similarity index 72% rename from src/ikvmc/IKVM/Internal/MapXml/And.cs rename to src/IKVM.Runtime/ExModifiers.cs index f53da6203e..bd6187098e 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/And.cs +++ b/src/IKVM.Runtime/ExModifiers.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,21 +21,20 @@ Jeroen Frijters jeroen@frijters.net */ +using IKVM.Attributes; -using System.Xml.Serialization; - -using IKVM.Reflection.Emit; - -namespace IKVM.Internal.MapXml +namespace IKVM.Internal { - - [XmlType("and")] - public sealed class And : Simple + struct ExModifiers { - public And() : base(OpCodes.And) - { + internal readonly Modifiers Modifiers; + internal readonly bool IsInternal; + internal ExModifiers(Modifiers modifiers, bool isInternal) + { + this.Modifiers = modifiers; + this.IsInternal = isInternal; } } diff --git a/src/IKVM.Runtime/ExceptionHelper.cs b/src/IKVM.Runtime/ExceptionHelper.cs index ef18616da5..d45e0e60a9 100644 --- a/src/IKVM.Runtime/ExceptionHelper.cs +++ b/src/IKVM.Runtime/ExceptionHelper.cs @@ -29,6 +29,7 @@ Jeroen Frijters using System.Security; using IKVM.Attributes; +using IKVM.Runtime; using IDictionary = System.Collections.IDictionary; using Interlocked = System.Threading.Interlocked; @@ -273,18 +274,19 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) } } - private static bool IsNative(MethodBase m) + static bool IsNative(MethodBase m) { - object[] methodFlagAttribs = m.GetCustomAttributes(typeof(ModifiersAttribute), false); + var methodFlagAttribs = m.GetCustomAttributes(typeof(ModifiersAttribute), false); if (methodFlagAttribs.Length == 1) { ModifiersAttribute modifiersAttrib = (ModifiersAttribute)methodFlagAttribs[0]; return (modifiersAttrib.Modifiers & Modifiers.Native) != 0; } + return false; } - private static string GetMethodName(MethodBase mb) + static string GetMethodName(MethodBase mb) { object[] attr = mb.GetCustomAttributes(typeof(NameSigAttribute), false); if (attr.Length == 1) @@ -320,7 +322,7 @@ private static string GetMethodName(MethodBase mb) private static bool IsHideFromJava(MethodBase mb) { #if FIRST_PASS - return false; + return false; #else return (IKVM.Java.Externs.sun.reflect.Reflection.GetHideFromJavaFlags(mb) & HideFromJavaFlags.StackTrace) != 0 || (mb.DeclaringType == typeof(ikvm.runtime.Util) && mb.Name == "mapException"); @@ -399,7 +401,7 @@ private static string GetFileName(StackFrame frame) internal static ObjectStreamField[] getPersistentFields() { #if FIRST_PASS - return null; + return null; #else return new ObjectStreamField[] { new ObjectStreamField("detailMessage", typeof(global::java.lang.String)), @@ -530,7 +532,7 @@ internal static string FilterMessage(string message) internal static string GetMessageFromCause(Exception cause) { #if FIRST_PASS - return null; + return null; #else if (cause == null) { @@ -543,7 +545,7 @@ internal static string GetMessageFromCause(Exception cause) internal static string getLocalizedMessage(Exception x) { #if FIRST_PASS - return null; + return null; #else return ikvm.extensions.ExtensionMethods.getMessage(x); #endif @@ -552,7 +554,7 @@ internal static string getLocalizedMessage(Exception x) internal static string toString(Exception x) { #if FIRST_PASS - return null; + return null; #else string message = ikvm.extensions.ExtensionMethods.getLocalizedMessage(x); if (message == null) @@ -566,7 +568,7 @@ internal static string toString(Exception x) internal static Exception getCause(Exception _this) { #if FIRST_PASS - return null; + return null; #else lock (_this) { @@ -627,7 +629,7 @@ internal static void addSuppressed(Exception _this, Exception x) internal static Exception[] getSuppressed(Exception _this) { #if FIRST_PASS - return null; + return null; #else lock (_this) { @@ -663,7 +665,7 @@ internal static StackTraceElement getStackTraceElement(Exception _this, int inde internal static StackTraceElement[] getOurStackTrace(Exception x) { #if FIRST_PASS - return null; + return null; #else Throwable _this = x as Throwable; if (_this == null) @@ -780,7 +782,7 @@ internal static void FixateException(Exception x) internal static Exception UnmapException(Exception x) { #if FIRST_PASS - return null; + return null; #else if (x is Throwable) { @@ -799,7 +801,7 @@ internal static Exception UnmapException(Exception x) private static Exception MapTypeInitializeException(TypeInitializationException t, Type handler) { #if FIRST_PASS - return null; + return null; #else bool wrapped = false; Exception r = MapException(t.InnerException, true, false); @@ -836,7 +838,7 @@ private static bool IsInstanceOfType(Exception t, bool remap) where T : Exception { #if FIRST_PASS - return false; + return false; #else if (!remap && typeof(T) == typeof(Exception)) { @@ -851,7 +853,7 @@ internal static T MapException(Exception x, bool remap, bool unused) where T : Exception { #if FIRST_PASS - return null; + return null; #else Exception org = x; bool nonJavaException = !(x is Throwable); diff --git a/src/IKVM.Runtime/Extensions/EnumerableExtensions.cs b/src/IKVM.Runtime/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000000..ed0ff153a0 --- /dev/null +++ b/src/IKVM.Runtime/Extensions/EnumerableExtensions.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace IKVM.Runtime.Extensions +{ + + static class EnumerableExtensions + { + +#if NETFRAMEWORK + + public static IEnumerable Append(this IEnumerable values, T value) + { + foreach (var i in values) + yield return i; + + yield return value; + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Extensions/PathExtensions.cs b/src/IKVM.Runtime/Extensions/PathExtensions.cs index 5c337495c0..d2734db883 100644 --- a/src/IKVM.Runtime/Extensions/PathExtensions.cs +++ b/src/IKVM.Runtime/Extensions/PathExtensions.cs @@ -18,7 +18,7 @@ static class PathExtensions /// public static string TrimEndingDirectorySeparator(string path) { -#if NET461 +#if NETFRAMEWORK return path.TrimEnd(DirectorySeparatorChars); #else return Path.TrimEndingDirectorySeparator(path); diff --git a/src/IKVM.Runtime/Extensions/ReadOnlySpanCharExtensions.cs b/src/IKVM.Runtime/Extensions/ReadOnlySpanCharExtensions.cs index cae657a09c..6a957a7a7d 100644 --- a/src/IKVM.Runtime/Extensions/ReadOnlySpanCharExtensions.cs +++ b/src/IKVM.Runtime/Extensions/ReadOnlySpanCharExtensions.cs @@ -6,7 +6,7 @@ namespace IKVM.Runtime.Extensions /// /// Provides extension methods for working with . /// - public static class ReadOnlySpanCharExtensions + static class ReadOnlySpanCharExtensions { /// @@ -16,7 +16,7 @@ public static class ReadOnlySpanCharExtensions /// public static int GetHashCodeExtension(this ReadOnlySpan span) { -#if NET461 +#if NETFRAMEWORK return span.ToString().GetHashCode(); #else return string.GetHashCode(span); diff --git a/src/IKVM.Runtime/FieldWrapper.cs b/src/IKVM.Runtime/FieldWrapper.cs new file mode 100644 index 0000000000..dea3336a4a --- /dev/null +++ b/src/IKVM.Runtime/FieldWrapper.cs @@ -0,0 +1,736 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Attributes; + +using System.Threading; +using System.CodeDom; +using System.Linq; +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace IKVM.Internal +{ + + abstract class FieldWrapper : MemberWrapper + { + +#if !IMPORTER && !FIRST_PASS && !EXPORTER + volatile java.lang.reflect.Field reflectionField; + sun.reflect.FieldAccessor jniAccessor; +#endif + internal static readonly FieldWrapper[] EmptyArray = new FieldWrapper[0]; + FieldInfo field; + TypeWrapper fieldType; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, MemberFlags flags) : + base(declaringType, name, sig, modifiers, flags) + { + if (name == null) + throw new ArgumentNullException(); + if (sig == null) + throw new ArgumentNullException(); + + this.fieldType = fieldType; + this.field = field; + + UpdateNonPublicTypeInSignatureFlag(); +#if IMPORTER + if (IsFinal + && DeclaringType.IsPublic + && !DeclaringType.IsInterface + && (IsPublic || (IsProtected && !DeclaringType.IsFinal)) + && !DeclaringType.GetClassLoader().StrictFinalFieldSemantics + && DeclaringType.IsDynamic + && !(this is ConstantFieldWrapper) + && !(this is DynamicPropertyFieldWrapper)) + { + SetType2FinalField(); + } +#endif + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, ExModifiers modifiers, FieldInfo field) : + this(declaringType, fieldType, name, sig, modifiers.Modifiers, field, (modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None)) + { + + } + + void UpdateNonPublicTypeInSignatureFlag() + { + if ((IsPublic || IsProtected) && fieldType != null && !IsAccessStub) + { + if (!fieldType.IsPublic && !fieldType.IsUnloadable) + { + SetNonPublicTypeInSignatureFlag(); + } + } + } + + /// + /// Gets the CLR that forms the implementation of the field. + /// + /// + internal FieldInfo GetField() + { + AssertLinked(); + return field; + } + + /// + /// Fails if the field has not yet been linked. + /// + [Conditional("DEBUG")] + internal void AssertLinked() + { + if (fieldType == null) + throw new InternalException($"Field is not linked: {DeclaringType.Name}::{Name} ({Signature})"); + + Debug.Assert(fieldType != null, this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature + ")"); + } + +#if !IMPORTER && !EXPORTER + + /// + /// Gets the for the given . + /// + /// + /// + internal static FieldWrapper FromField(java.lang.reflect.Field field) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + int slot = field._slot(); + if (slot == -1) + { + // it's a Field created by Unsafe.objectFieldOffset(Class,String) so we must resolve based on the name + foreach (var fw in TypeWrapper.FromClass(field.getDeclaringClass()).GetFields()) + if (fw.Name == field.getName()) + return fw; + } + + return TypeWrapper.FromClass(field.getDeclaringClass()).GetFields()[slot]; +#endif + } + + internal object ToField(bool copy) + { + return ToField(copy, null); + } + + internal object ToField(bool copy, int? fieldIndex) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + java.lang.reflect.Field field = reflectionField; + if (field == null) + { + const Modifiers ReflectionFieldModifiersMask = Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Volatile | Modifiers.Transient | Modifiers.Synthetic | Modifiers.Enum; + Link(); + field = new java.lang.reflect.Field( + DeclaringType.ClassObject, + Name, + FieldTypeWrapper.EnsureLoadable(DeclaringType.GetClassLoader()).ClassObject, + (int)(Modifiers & ReflectionFieldModifiersMask) | (IsInternal ? 0x40000000 : 0), + fieldIndex ?? Array.IndexOf(DeclaringType.GetFields(), this), + DeclaringType.GetGenericFieldSignature(this), + null + ); + } + + lock (this) + { + if (reflectionField == null) + reflectionField = field; + else + field = reflectionField; + } + + if (copy) + field = field.copy(); + + return field; +#endif + } + +#endif + + /// + /// Resolves the instance referenced by the specified cookie. + /// + /// + /// + [System.Security.SecurityCritical] + internal static FieldWrapper FromCookie(IntPtr cookie) + { + return (FieldWrapper)FromCookieImpl(cookie); + } + + internal TypeWrapper FieldTypeWrapper + { + get + { + AssertLinked(); + return fieldType; + } + } + +#if EMITTERS + + /// + /// Emits the IL code to set the value of the field. + /// + /// + internal void EmitGet(CodeEmitter il) + { + AssertLinked(); + EmitGetImpl(il); + } + + /// + /// Emits the IL code to set the value of the field. + /// + /// + protected abstract void EmitGetImpl(CodeEmitter il); + + /// + /// Emits the IL code to implement the unsafe get operation. + /// + /// + internal void EmitUnsafeGet(CodeEmitter il) + { + AssertLinked(); + EmitUnsafeGetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe get operation. + /// + /// + protected virtual void EmitUnsafeGetImpl(CodeEmitter il) + { + EmitGetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe volatile get operation. + /// + /// + internal void EmitUnsafeVolatileGet(CodeEmitter il) + { + AssertLinked(); + EmitUnsafeVolatileGetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe volatile get operation. + /// + /// + protected virtual void EmitUnsafeVolatileGetImpl(CodeEmitter il) + { + throw new NotSupportedException(); + } + + /// + /// Emits the IL code to implement the set operation. + /// + /// + internal void EmitSet(CodeEmitter il) + { + AssertLinked(); + EmitSetImpl(il); + } + + /// + /// Emits the IL code to implement the set operation. + /// + /// + protected abstract void EmitSetImpl(CodeEmitter il); + + /// + /// Emits the IL code to implement the unsafe set operation. + /// + /// + internal void EmitUnsafeSet(CodeEmitter il) + { + AssertLinked(); + EmitSetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe set operation. + /// + /// + protected virtual void EmitUnsafeSetImpl(CodeEmitter il) + { + EmitSetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe volatile set operation. + /// + /// + internal void EmitUnsafeVolatileSet(CodeEmitter il) + { + AssertLinked(); + EmitUnsafeVolatileSetImpl(il); + } + + /// + /// Emits the IL code to implement the unsafe volatile set operation. + /// + /// + protected virtual void EmitUnsafeVolatileSetImpl(CodeEmitter il) + { + throw new NotSupportedException(); + } + + /// + /// Emits the IL code to implement an unsafe compare/exchange operation. The stack is expected to contain the object, + /// the comparand and the value. The applied value is placed on the stack after execution. + /// + /// + internal void EmitUnsafeCompareAndSwap(CodeEmitter il) + { + AssertLinked(); + EmitUnsafeCompareAndSwapImpl(il); + } + + /// + /// Emits the IL code to implement an unsafe compare/exchange operation. The stack is expected to contain the object, + /// the comparand and the value. The applied value is placed on the stack after execution. + /// + /// + protected virtual void EmitUnsafeCompareAndSwapImpl(CodeEmitter il) + { + throw new NotSupportedException(); + } + +#endif + +#if IMPORTER + + internal bool IsLinked + { + get { return fieldType != null; } + } + +#endif + + internal void Link() + { + Link(LoadMode.Link); + } + + internal void Link(LoadMode mode) + { + lock (this) + if (fieldType != null) + return; + + var fld = DeclaringType.GetClassLoader().FieldTypeWrapperFromSig(Signature, mode); + + lock (this) + { + try + { + // critical code in the finally block to avoid Thread.Abort interrupting the thread + } + finally + { + if (fieldType == null) + { + fieldType = fld; + UpdateNonPublicTypeInSignatureFlag(); + + try + { + field = this.DeclaringType.LinkField(this); + } + catch + { + // HACK if linking fails, we unlink to make sure + // that the next link attempt will fail again + fieldType = null; + throw; + } + } + } + } + } + + internal bool IsVolatile + { + get + { + return (Modifiers & Modifiers.Volatile) != 0; + } + } + + internal bool IsSerialVersionUID + { + get + { + // a serialVersionUID field must be static and final to be recognized (see ObjectStreamClass.getDeclaredSUID()) + return (Modifiers & (Modifiers.Static | Modifiers.Final)) == (Modifiers.Static | Modifiers.Final) + && Name == "serialVersionUID" + && (FieldTypeWrapper == PrimitiveTypeWrapper.LONG + || FieldTypeWrapper == PrimitiveTypeWrapper.INT + || FieldTypeWrapper == PrimitiveTypeWrapper.CHAR + || FieldTypeWrapper == PrimitiveTypeWrapper.SHORT + || FieldTypeWrapper == PrimitiveTypeWrapper.BYTE); + } + } + + internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) + { + // volatile long & double field accesses must be made atomic + if ((modifiers.Modifiers & Modifiers.Volatile) != 0 && (sig == "J" || sig == "D")) + return new VolatileLongDoubleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers); + + return new SimpleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers); + } + +#if !IMPORTER && !EXPORTER + internal virtual void ResolveField() + { + var fb = field as FieldBuilder; + if (fb != null) + { +#if NETFRAMEWORK + field = fb.DeclaringType.Module.ResolveField(fb.GetToken().Token); +#else + BindingFlags flags = BindingFlags.DeclaredOnly; + flags |= fb.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; + flags |= fb.IsStatic ? BindingFlags.Static : BindingFlags.Instance; + field = DeclaringType.TypeAsTBD.GetField(fb.Name, flags); +#endif + } + } + + internal object GetFieldAccessorJNI() + { +#if FIRST_PASS + return null; +#else + if (jniAccessor == null) + Interlocked.CompareExchange(ref jniAccessor, IKVM.Java.Externs.sun.reflect.ReflectionFactory.NewFieldAccessorJNI(this), null); + + return jniAccessor; +#endif + } + +#if !FIRST_PASS + + /// + /// Gets the value of the field. + /// + /// + /// + internal abstract object GetValue(object obj); + + /// + /// Sets the value of the field. + /// + /// + /// + internal abstract void SetValue(object obj, object value); + + Delegate unsafeGetValueDelegate; + Delegate unsafeSetValueDelegate; + Delegate unsafeVolatileGetDelegate; + Delegate unsafeVolatileSetDelegate; + Delegate unsafeCompareExchangeDelegate; + + /// + /// Performs an unsafe get operation on the field. + /// + /// + /// + internal virtual TField UnsafeGetValue(object obj) + { + return ((Func)GetUnsafeGetDelegate())(obj); + } + + /// + /// Gets a delegate capable of performing an unsafe get operation on the field. + /// + /// + Delegate GetUnsafeGetDelegate() + { + if (unsafeGetValueDelegate == null) + Interlocked.CompareExchange(ref unsafeGetValueDelegate, CreateUnsafeGetDelegate(), null); + + return unsafeGetValueDelegate; + } + + /// + /// Creates a delegate capable of performing an unsafe get operation on the field. + /// + /// + Delegate CreateUnsafeGetDelegate() + { + DeclaringType.Finish(); + FieldTypeWrapper.Finish(); + ResolveField(); + + var ft = FieldTypeWrapper.IsPrimitive ? FieldTypeWrapper.TypeAsSignatureType : typeof(object); + var dm = DynamicMethodUtil.Create($"____{DeclaringType.Name.Replace(".", "_")}__{Name}", DeclaringType.TypeAsTBD, true, ft, new[] { typeof(object) }); + var il = CodeEmitter.Create(dm); + + if (IsStatic == false) + il.Emit(OpCodes.Ldarg_0); + + EmitUnsafeGet(il); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + return dm.CreateDelegate(typeof(Func<,>).MakeGenericType(typeof(object), ft)); + } + + /// + /// Performs an unsafe set operation on the field. + /// + /// + /// + /// + internal virtual void UnsafeSetValue(object obj, TField value) + { + ((Action)GetUnsafeSetDelegate())(obj, value); + } + + /// + /// Gets a delegate capable of performing an unsafe set operation on the field. + /// + /// + Delegate GetUnsafeSetDelegate() + { + if (unsafeSetValueDelegate == null) + Interlocked.CompareExchange(ref unsafeSetValueDelegate, CreateUnsafeSetDelegate(), null); + + return unsafeSetValueDelegate; + } + + /// + /// Creates a delegate capable of performing an unsafe set operation on the field. + /// + /// + Delegate CreateUnsafeSetDelegate() + { + DeclaringType.Finish(); + FieldTypeWrapper.Finish(); + ResolveField(); + + var ft = FieldTypeWrapper.IsPrimitive ? FieldTypeWrapper.TypeAsSignatureType : typeof(object); + var dm = DynamicMethodUtil.Create($"____{DeclaringType.Name.Replace(".", "_")}__{Name}", DeclaringType.TypeAsTBD, true, typeof(void), new[] { typeof(object), ft }); + var il = CodeEmitter.Create(dm); + + if (IsStatic == false) + il.Emit(OpCodes.Ldarg_0); + + il.Emit(OpCodes.Ldarg_1); + EmitUnsafeSet(il); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + return dm.CreateDelegate(typeof(Action<,>).MakeGenericType(typeof(object), ft)); + } + + /// + /// Performs an unsafe volatile get operation on the field. + /// + /// + /// + /// + internal virtual TField UnsafeVolatileGet(object obj) + { + return ((Func)GetUnsafeVolatileGetDelegate())(obj); + } + + /// + /// Gets a delegate capable of performing an unsafe volatile get operation on the field. + /// + /// + Delegate GetUnsafeVolatileGetDelegate() + { + if (unsafeVolatileGetDelegate == null) + Interlocked.CompareExchange(ref unsafeVolatileGetDelegate, CreateUnsafeVolatileGetDelegate(), null); + + return unsafeVolatileGetDelegate; + } + + /// + /// Creates a delegate capable of performing an unsafe volatile get operation on the field. + /// + /// + Delegate CreateUnsafeVolatileGetDelegate() + { + ResolveField(); + var ft = FieldTypeWrapper.IsPrimitive ? FieldTypeWrapper.TypeAsSignatureType : typeof(object); + var dm = new DynamicMethod($"____{DeclaringType.Name.Replace(".", "_")}__{Name}", ft, new[] { typeof(object) }, DeclaringType.TypeAsTBD.Module, true); + var il = CodeEmitter.Create(dm); + + if (IsStatic == false) + il.Emit(OpCodes.Ldarg_0); + + EmitUnsafeVolatileGet(il); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + return dm.CreateDelegate(typeof(Func<,>).MakeGenericType(typeof(object), ft)); + } + + /// + /// Performs an unsafe volatile set operation on the field. + /// + /// + /// + /// + /// + internal virtual void UnsafeVolatileSet(object obj, TField value) + { + ((Action)GetUnsafeVolatileSetDelegate())(obj, value); + } + + /// + /// Gets a delegate capable of performing an unsafe volatile set operation on the field. + /// + /// + Delegate GetUnsafeVolatileSetDelegate() + { + if (unsafeVolatileSetDelegate == null) + Interlocked.CompareExchange(ref unsafeVolatileSetDelegate, CreateUnsafeVolatileSetDelegate(), null); + + return unsafeVolatileSetDelegate; + } + + /// + /// Creates a delegate capable of performing an unsafe volatile set operation on the field. + /// + /// + Delegate CreateUnsafeVolatileSetDelegate() + { + ResolveField(); + var ft = FieldTypeWrapper.IsPrimitive ? FieldTypeWrapper.TypeAsSignatureType : typeof(object); + var dm = DynamicMethodUtil.Create($"____{DeclaringType.Name.Replace(".", "_")}__{Name}", DeclaringType.TypeAsTBD, true, typeof(void), new[] { typeof(object), ft }); + var il = CodeEmitter.Create(dm); + + if (IsStatic == false) + il.Emit(OpCodes.Ldarg_0); + + il.Emit(OpCodes.Ldarg_1); + EmitUnsafeVolatileSet(il); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + return dm.CreateDelegate(typeof(Action<,>).MakeGenericType(typeof(object), ft)); + } + + /// + /// Unsafe compare and swap implementation for the field. + /// + /// + /// + /// + /// + /// + internal virtual bool UnsafeCompareAndSwap(object obj, TField expected, TField value) + { + return ((Func)GetUnsafeCompareAndSwapDelegate())(obj, expected, value); + } + + + /// + /// Gets a delegate implementing the unsafe compare and swap operation on the field. + /// + /// + Delegate GetUnsafeCompareAndSwapDelegate() + { + if (unsafeCompareExchangeDelegate == null) + Interlocked.CompareExchange(ref unsafeCompareExchangeDelegate, CreateUnsafeCompareAndSwapDelegate(), null); + + return unsafeCompareExchangeDelegate; + } + + /// + /// Creates a delegate implementing the unsafe compare and swap operation on the field. + /// + /// + Delegate CreateUnsafeCompareAndSwapDelegate() + { + var ft = FieldTypeWrapper.IsPrimitive ? FieldTypeWrapper.TypeAsSignatureType : typeof(object); + var dm = DynamicMethodUtil.Create($"____{DeclaringType.Name.Replace(".", "_")}__{Name}", DeclaringType.TypeAsTBD, true, typeof(bool), new[] { typeof(object), ft, ft }); + var il = CodeEmitter.Create(dm); + + if (IsStatic == false) + il.Emit(OpCodes.Ldarg_0); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + EmitUnsafeCompareAndSwap(il); + + il.Emit(OpCodes.Ret); + il.DoEmit(); + return dm.CreateDelegate(typeof(Func<,,,>).MakeGenericType(typeof(object), ft, ft, typeof(bool))); + } + +#endif + +#endif + + } + +} diff --git a/src/IKVM.Runtime/GenericClassLoaderWrapper.cs b/src/IKVM.Runtime/GenericClassLoaderWrapper.cs index 81e759db8e..81e4e9f366 100644 --- a/src/IKVM.Runtime/GenericClassLoaderWrapper.cs +++ b/src/IKVM.Runtime/GenericClassLoaderWrapper.cs @@ -27,7 +27,7 @@ Jeroen Frijters using System.Runtime.Loader; #endif -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -103,7 +103,7 @@ internal string GetName() return sb.ToString(); } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal java.util.Enumeration GetResources(string name) { #if FIRST_PASS diff --git a/src/IKVM.Runtime/GhostMethodWrapper.cs b/src/IKVM.Runtime/GhostMethodWrapper.cs new file mode 100644 index 0000000000..15beff69d1 --- /dev/null +++ b/src/IKVM.Runtime/GhostMethodWrapper.cs @@ -0,0 +1,117 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System.Diagnostics; + +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace IKVM.Internal +{ + + sealed class GhostMethodWrapper : SmartMethodWrapper + { + + MethodInfo ghostMethod; + MethodInfo defaultImpl; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal GhostMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, MethodInfo ghostMethod, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) + { + // make sure we weren't handed the ghostMethod in the wrapper value type + Debug.Assert(method == null || method.DeclaringType.IsInterface); + this.ghostMethod = ghostMethod; + } + +#if EMITTERS + + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, defaultImpl); + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, ghostMethod); + } + +#endif + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + [HideFromJava] + internal override object Invoke(object obj, object[] args) + { + return InvokeAndUnwrapException(ghostMethod, DeclaringType.GhostWrap(obj), args); + } + +#endif + + internal void SetDefaultImpl(MethodInfo impl) + { + this.defaultImpl = impl; + } + + internal MethodInfo GetDefaultImpl() + { + return defaultImpl; + } + +#if IMPORTER + + internal void SetGhostMethod(MethodBuilder mb) + { + this.ghostMethod = mb; + } + + internal MethodBuilder GetGhostMethod() + { + return (MethodBuilder)ghostMethod; + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/GhostTag.cs b/src/IKVM.Runtime/GhostTag.cs new file mode 100644 index 0000000000..1e60bd338f --- /dev/null +++ b/src/IKVM.Runtime/GhostTag.cs @@ -0,0 +1,108 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +using IKVM.Attributes; +using IKVM.Internal; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +namespace IKVM.Runtime +{ + + static class GhostTag + { + + static volatile ConditionalWeakTable dict; + + internal static void SetTag(object obj, RuntimeTypeHandle typeHandle) + { +#if FIRST_PASS || IMPORTER + throw new NotImplementedException(); +#else + SetTag(obj, ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle))); +#endif + } + + internal static void SetTag(object obj, TypeWrapper wrapper) + { + if (dict == null) + { + ConditionalWeakTable newDict = new ConditionalWeakTable(); + Interlocked.CompareExchange(ref dict, newDict, null); + } + dict.Add(obj, wrapper); + } + + internal static TypeWrapper GetTag(object obj) + { + if (dict != null) + { + TypeWrapper tw; + dict.TryGetValue(obj, out tw); + return tw; + } + return null; + } + + // this method is called from .IsInstanceArray() + internal static bool IsGhostArrayInstance(object obj, RuntimeTypeHandle typeHandle, int rank) + { +#if FIRST_PASS || IMPORTER + throw new NotImplementedException(); +#else + TypeWrapper tw1 = GhostTag.GetTag(obj); + if (tw1 != null) + { + TypeWrapper tw2 = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle)).MakeArrayType(rank); + return tw1.IsAssignableTo(tw2); + } + return false; +#endif + } + + // this method is called from .CastArray() + [HideFromJava] + internal static void ThrowClassCastException(object obj, RuntimeTypeHandle typeHandle, int rank) + { +#if FIRST_PASS || IMPORTER + throw new NotImplementedException(); +#else + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + sb.Append(global::ikvm.runtime.Util.getClassFromObject(obj).getName()).Append(" cannot be cast to ") + .Append('[', rank).Append('L').Append(global::ikvm.runtime.Util.getClassFromTypeHandle(typeHandle).getName()).Append(';'); + throw new global::java.lang.ClassCastException(sb.ToString()); +#endif + } + } + +} diff --git a/src/IKVM.Runtime/IKVM.Runtime.csproj b/src/IKVM.Runtime/IKVM.Runtime.csproj index 3aa09e20b8..8502e879a1 100644 --- a/src/IKVM.Runtime/IKVM.Runtime.csproj +++ b/src/IKVM.Runtime/IKVM.Runtime.csproj @@ -1,20 +1,306 @@ - + + - + net461;netcoreapp3.1 $(DefineConstants);EMITTERS true + true + + + + + + + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)JNI\Trampolines\ + $(MSBuildThisFileDirectory)$(IntermediateOutputPath)jni-trampolines\ + $(OpenJdkDir)jdk\src\share\javavm\export + $(OpenJdkDir)jdk\src\windows\javavm\export + $(OpenJdkDir)jdk\src\solaris\javavm\export + clang + llvm-objdump + + + + + + + + + + + + + + + + <_JNITrampolinesIncludes Include="%(JNITrampolineRuntimeIdentifier.Includes)" /> + + + + + + + + + + + + + + + + + + + + + + + \:\w*$", RegexOptions.IgnoreCase); + static readonly Regex CodeLineRegex = new Regex(@"^([0-9a-fA-F ]{8})\: ([0-9a-fA-F ]+)\t.+$", RegexOptions.IgnoreCase); + + public string Namespace { get; set; } + + public string ClassName { get; set; } + + public string AssemblerFile { get; set; } + + public string BuildCodeFile { get; set; } + + + public override bool Execute() + { + using var o = new StreamWriter(File.Create(BuildCodeFile)); + using var s = new StreamReader(File.OpenRead(AssemblerFile)); + + o.Write("namespace "); + o.Write(Namespace); + o.Write(" {"); + o.WriteLine(); + o.Write("class "); + o.Write(ClassName); + o.Write(" : IKVM.Runtime.JNI.Trampolines.FunctionTable {"); + o.WriteLine(); + + var currentName = (string)null; + var currentIndx = (int)-1; + var text = new List(16384); + var syms = new Dictionary(); + + void InsertText(int pos, byte val) + { + // ensure capacity + while (text.Count < pos) + text.Add(0); + + text.Insert(pos, val); + } + + do + { + var line = s.ReadLine(); + if (line == null) + break; + + if (currentName == null) + { + var m = SymbolLineRegex.Match(line); + if (m.Success == false) + continue; + + var z = m.Groups[1].Value.Trim().PadLeft(16, '0'); + var i = m.Groups[2].Value.TrimStart('_'); + currentName = DemangleName(i); + currentIndx = (int)ParseInt64BigEndian(ParseHex(z)); + syms.Add(currentName, currentIndx); + continue; + } + else + { + var m = CodeLineRegex.Match(line); + if (m.Success == false) + { + currentName = null; + continue; + } + + var z = m.Groups[1].Value.Trim().PadLeft(16, '0'); + var i = string.Join("", m.Groups[2].Value.Split(' ').Select(j => j.Trim()).Where(j => j != "").ToArray()); + currentIndx = (int)ParseInt64BigEndian(ParseHex(z)); + + var b = ParseHex(i); + for (int p = 0; p < b.Length; p++) + InsertText(currentIndx + p, b[p]); + } + } + while (true); + + WriteText(o, text); + WriteSyms(o, syms); + + o.WriteLine("}"); + o.WriteLine("}"); + + return true; + } + + string DemangleName(string name) + { + if (name.StartsWith("\\1")) + name = name.Substring(2); + + var at = name.IndexOf('@'); + if (at != -1) + name = name.Substring(0, at); + + return name; + } + + void WriteText(StreamWriter writer, List text) + { + writer.WriteLine("static readonly byte[] text = new byte[]"); + writer.WriteLine("{"); + + for (int i = 0; i < text.Count; i++) + { + writer.Write("0x"); + writer.Write(text[i].ToString("X2")); + writer.Write(", "); + + if ((i + 1) % 32 == 0) + writer.WriteLine(); + } + + writer.WriteLine(); + writer.WriteLine("};"); + writer.WriteLine(); + + writer.WriteLine("protected override byte[] Text => text;"); + } + + void WriteSyms(StreamWriter writer, Dictionary syms) + { + foreach (var kvp in syms) + { + var name = kvp.Key; + var offs = kvp.Value; + + if (name.StartsWith("$") || name.StartsWith(".")) + continue; + + writer.Write("static readonly int _idx_"); + writer.Write(name); + writer.Write(" = 0x"); + writer.Write(offs.ToString("x2")); + writer.WriteLine(";"); + + writer.Write("protected override int idx_"); + writer.Write(name); + writer.Write(" => _idx_"); + writer.Write(name); + writer.WriteLine(";"); + } + } + + static byte[] ParseHex(string hex) + { + static int GetHexVal(char hex) => hex - (hex < 58 ? 48 : (hex < 97 ? 55 : 87)); + + var arr = new byte[hex.Length >> 1]; + for (int i = 0; i < hex.Length >> 1; ++i) + arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + GetHexVal(hex[(i << 1) + 1])); + + return arr; + } + + static int ParseInt32BigEndian(byte[] bytes, int off = 0) + { + return ((bytes[off + 0] & 0xff) << 24) | ((bytes[off + 1] & 0xff) << 16) | ((bytes[off + 2] & 0xff) << 8) | (bytes[off + 3] & 0xff); + } + + static long ParseInt64BigEndian(byte[] bytes, int off = 0) + { + var a = (long)ParseInt32BigEndian(bytes, off); + var b = (long)ParseInt32BigEndian(bytes, off + 4); + var i = (a << 32) | b; + return i; + } + + } + + ]]> + + + + + + + + + + + + + + + + + + GenerateJNITrampolines; + $(CompileDependsOn); + + + + + + GenerateJNITrampolines; + $(GetCopyToOutputDirectoryItemsDependsOn); + + + diff --git a/src/IKVM.Runtime/JNI/JNIEnv.cs b/src/IKVM.Runtime/JNI/JNIEnv.cs index 4da0f153f2..f0f6dff850 100644 --- a/src/IKVM.Runtime/JNI/JNIEnv.cs +++ b/src/IKVM.Runtime/JNI/JNIEnv.cs @@ -27,8 +27,8 @@ Jeroen Frijters using System.Reflection; using System.Runtime.InteropServices; +using IKVM.ByteCode.Text; using IKVM.Internal; -using IKVM.Runtime.Text; namespace IKVM.Runtime.JNI { @@ -58,11 +58,14 @@ namespace IKVM.Runtime.JNI using jstring = System.IntPtr; using jthrowable = System.IntPtr; using jweak = System.IntPtr; + using jobjectArray = System.IntPtr; [StructLayout(LayoutKind.Sequential)] unsafe partial struct JNIEnv { + static readonly MUTF8Encoding MUTF8 = MUTF8Encoding.GetMUTF8(52); + internal const int JNI_OK = 0; internal const int JNI_ERR = -1; internal const int JNI_EDETACHED = -2; @@ -356,7 +359,7 @@ internal void DeleteLocalRef(jobject obj) ManagedJNIEnv env = new ManagedJNIEnv(); TlsHack.ManagedJNIEnv = env; JNIEnv* pJNIEnv = env.pJNIEnv; - pJNIEnv->vtable = VtableBuilder.vtable; + pJNIEnv->vtable = JNINativeInterface.Handle; pJNIEnv->managedJNIEnv = GCHandle.Alloc(env, GCHandleType.WeakTrackResurrection); pJNIEnv->pinHandles = null; pJNIEnv->pinHandleMaxCount = 0; @@ -380,11 +383,11 @@ internal static string DecodeMUTF8Argument(byte* psz, string arg) if (psz is null) return null; - var l = MUTF8Encoding.IndexOfNull(psz); + var l = MUTF8.IndexOfNull(psz); if (l < 0) throw new java.lang.IllegalArgumentException(arg); - var v = MUTF8Encoding.MUTF8.GetString(psz, l); + var v = MUTF8.GetString(psz, l); return v; } @@ -399,25 +402,21 @@ internal static string DecodeMUTF8(byte* psz) if (psz is null) return null; - var l = MUTF8Encoding.IndexOfNull(psz); + var l = MUTF8.IndexOfNull(psz); if (l < 0) throw new java.lang.IllegalArgumentException(); - var v = MUTF8Encoding.MUTF8.GetString(psz, l); + var v = MUTF8.GetString(psz, l); return v; } /// - /// Returns the number of characters of the modified UTF-8 string. + /// Outputs an encoded signature of the arguments available to the method. /// - /// + /// + /// + /// /// - /// - internal int GetMUTF8ArgumentLength(byte* psz) - { - return psz is null ? -1 : MUTF8Encoding.IndexOfNull(psz); - } - internal static int GetMethodArgs(JNIEnv* pEnv, nint method, byte* sig) { var args = MethodWrapper.FromCookie(method).GetParameters(); @@ -960,7 +959,7 @@ static jmethodID FindMethodID(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig, tw.Finish(); // if name == NULL, the JDK returns the constructor - var methodname = (jshortArray)name == IntPtr.Zero ? "" : DecodeMUTF8Argument(name, nameof(name)); + var methodname = (IntPtr)name == IntPtr.Zero ? "" : DecodeMUTF8Argument(name, nameof(name)); var methodsig = DecodeMUTF8Argument(sig, nameof(sig)); MethodWrapper mw = null; @@ -1507,13 +1506,13 @@ internal static jobject NewStringUTF(JNIEnv* pEnv, byte* bytes) internal static jint GetStringUTFLength(JNIEnv* pEnv, jstring str) { - return MUTF8Encoding.MUTF8.GetByteCount((string)pEnv->UnwrapRef(str)); + return MUTF8.GetByteCount((string)pEnv->UnwrapRef(str)); } internal static byte* GetStringUTFChars(JNIEnv* pEnv, jstring @string, jboolean* isCopy) { var s = (string)pEnv->UnwrapRef(@string); - var buf = (byte*)JNIMemory.Alloc(MUTF8Encoding.MUTF8.GetByteCount(s) + 1); + var buf = (byte*)JNIMemory.Alloc(MUTF8.GetByteCount(s) + 1); int j = 0; for (int i = 0; i < s.Length; i++) @@ -1554,7 +1553,7 @@ internal static jsize GetArrayLength(JNIEnv* pEnv, jarray array) return ((Array)pEnv->UnwrapRef(array)).Length; } - internal static jobject NewObjectArray(JNIEnv* pEnv, jsize len, jclass clazz, jobject init) + internal static jobjectArray NewObjectArray(JNIEnv* pEnv, jsize len, jclass clazz, jobject init) { try { @@ -1921,7 +1920,7 @@ internal static void ReleaseDoubleArrayElements(JNIEnv* pEnv, jdoubleArray array } } - internal static void GetBooleanArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetBooleanArrayRegion(JNIEnv* pEnv, jbooleanArray array, int start, int len, jboolean* buf) { try { @@ -1938,7 +1937,7 @@ internal static void GetBooleanArrayRegion(JNIEnv* pEnv, IntPtr array, int start } } - internal static void GetByteArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetByteArrayRegion(JNIEnv* pEnv, jbyteArray array, int start, int len, jbyte* buf) { try { @@ -1955,12 +1954,12 @@ internal static void GetByteArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void GetCharArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetCharArrayRegion(JNIEnv* pEnv, jcharArray array, int start, int len, jchar* buf) { try { char[] b = (char[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -1968,12 +1967,12 @@ internal static void GetCharArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void GetShortArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetShortArrayRegion(JNIEnv* pEnv, jshortArray array, int start, int len, jshort* buf) { try { short[] b = (short[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -1981,12 +1980,12 @@ internal static void GetShortArrayRegion(JNIEnv* pEnv, IntPtr array, int start, } } - internal static void GetIntArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetIntArrayRegion(JNIEnv* pEnv, jintArray array, int start, int len, jint* buf) { try { int[] b = (int[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -1994,12 +1993,12 @@ internal static void GetIntArrayRegion(JNIEnv* pEnv, IntPtr array, int start, in } } - internal static void GetLongArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetLongArrayRegion(JNIEnv* pEnv, jlongArray array, int start, int len, jlong* buf) { try { long[] b = (long[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -2007,12 +2006,12 @@ internal static void GetLongArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void GetFloatArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetFloatArrayRegion(JNIEnv* pEnv, jfloatArray array, int start, int len, jfloat* buf) { try { float[] b = (float[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -2020,12 +2019,12 @@ internal static void GetFloatArrayRegion(JNIEnv* pEnv, IntPtr array, int start, } } - internal static void GetDoubleArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void GetDoubleArrayRegion(JNIEnv* pEnv, jdoubleArray array, int start, int len, jdouble* buf) { try { double[] b = (double[])pEnv->UnwrapRef(array); - Marshal.Copy(b, start, buf, len); + Marshal.Copy(b, start, (IntPtr)buf, len); } catch (ArgumentOutOfRangeException) { @@ -2033,7 +2032,7 @@ internal static void GetDoubleArrayRegion(JNIEnv* pEnv, IntPtr array, int start, } } - internal static void SetBooleanArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetBooleanArrayRegion(JNIEnv* pEnv, jbooleanArray array, int start, int len, jboolean* buf) { try { @@ -2050,7 +2049,7 @@ internal static void SetBooleanArrayRegion(JNIEnv* pEnv, IntPtr array, int start } } - internal static void SetByteArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetByteArrayRegion(JNIEnv* pEnv, jbyteArray array, int start, int len, jbyte* buf) { try { @@ -2067,12 +2066,12 @@ internal static void SetByteArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void SetCharArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetCharArrayRegion(JNIEnv* pEnv, jcharArray array, int start, int len, jchar* buf) { try { char[] b = (char[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2080,12 +2079,12 @@ internal static void SetCharArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void SetShortArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetShortArrayRegion(JNIEnv* pEnv, jshortArray array, int start, int len, jshort* buf) { try { short[] b = (short[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2093,12 +2092,12 @@ internal static void SetShortArrayRegion(JNIEnv* pEnv, IntPtr array, int start, } } - internal static void SetIntArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetIntArrayRegion(JNIEnv* pEnv, jintArray array, int start, int len, jint* buf) { try { int[] b = (int[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2106,12 +2105,12 @@ internal static void SetIntArrayRegion(JNIEnv* pEnv, IntPtr array, int start, in } } - internal static void SetLongArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetLongArrayRegion(JNIEnv* pEnv, jlongArray array, int start, int len, jlong* buf) { try { long[] b = (long[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2119,12 +2118,12 @@ internal static void SetLongArrayRegion(JNIEnv* pEnv, IntPtr array, int start, i } } - internal static void SetFloatArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetFloatArrayRegion(JNIEnv* pEnv, jfloatArray array, int start, int len, jfloat* buf) { try { float[] b = (float[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2132,12 +2131,12 @@ internal static void SetFloatArrayRegion(JNIEnv* pEnv, IntPtr array, int start, } } - internal static void SetDoubleArrayRegion(JNIEnv* pEnv, IntPtr array, int start, int len, IntPtr buf) + internal static void SetDoubleArrayRegion(JNIEnv* pEnv, jdoubleArray array, int start, int len, jdouble* buf) { try { double[] b = (double[])pEnv->UnwrapRef(array); - Marshal.Copy(buf, b, start, len); + Marshal.Copy((IntPtr)buf, b, start, len); } catch (ArgumentOutOfRangeException) { @@ -2153,7 +2152,7 @@ unsafe internal struct JNINativeMethod public void* fnPtr; } - internal static int RegisterNatives(JNIEnv* pEnv, IntPtr clazz, JNINativeMethod* methods, int nMethods) + internal static jint RegisterNatives(JNIEnv* pEnv, jclass clazz, JNINativeMethod* methods, jint nMethods) { try { @@ -2193,7 +2192,7 @@ internal static int RegisterNatives(JNIEnv* pEnv, IntPtr clazz, JNINativeMethod* } } - internal static int UnregisterNatives(JNIEnv* pEnv, IntPtr clazz) + internal static jint UnregisterNatives(JNIEnv* pEnv, jclass clazz) { try { @@ -2223,7 +2222,7 @@ internal static int UnregisterNatives(JNIEnv* pEnv, IntPtr clazz) } } - internal static int MonitorEnter(JNIEnv* pEnv, IntPtr obj) + internal static jint MonitorEnter(JNIEnv* pEnv, jobject obj) { try { @@ -2241,7 +2240,7 @@ internal static int MonitorEnter(JNIEnv* pEnv, IntPtr obj) } } - internal static int MonitorExit(JNIEnv* pEnv, IntPtr obj) + internal static jint MonitorExit(JNIEnv* pEnv, jobject obj) { try { @@ -2255,13 +2254,13 @@ internal static int MonitorExit(JNIEnv* pEnv, IntPtr obj) } } - internal static int GetJavaVM(JNIEnv* pEnv, JavaVM** ppJavaVM) + internal static jint GetJavaVM(JNIEnv* pEnv, JavaVM** ppJavaVM) { *ppJavaVM = JavaVM.pJavaVM; return JNI_OK; } - internal static void GetStringRegion(JNIEnv* pEnv, IntPtr str, int start, int len, IntPtr buf) + internal static void GetStringRegion(JNIEnv* pEnv, jstring str, jsize start, jsize len, jchar* buf) { string s = (string)pEnv->UnwrapRef(str); if (s != null) @@ -2288,7 +2287,7 @@ internal static void GetStringRegion(JNIEnv* pEnv, IntPtr str, int start, int le } } - internal static void GetStringUTFRegion(JNIEnv* pEnv, IntPtr str, int start, int len, IntPtr buf) + internal static void GetStringUTFRegion(JNIEnv* pEnv, jstring str, jsize start, jsize len, byte* buf) { string s = (string)pEnv->UnwrapRef(str); if (s != null) @@ -2459,16 +2458,16 @@ internal static jobject NewDirectByteBuffer(JNIEnv* pEnv, IntPtr address, jlong } } - internal static IntPtr GetDirectBufferAddress(JNIEnv* pEnv, jobject buf) + internal static void* GetDirectBufferAddress(JNIEnv* pEnv, jobject buf) { try { - return (IntPtr)((sun.nio.ch.DirectBuffer)pEnv->UnwrapRef(buf)).address(); + return (void*)(IntPtr)((sun.nio.ch.DirectBuffer)pEnv->UnwrapRef(buf)).address(); } catch (Exception x) { SetPendingException(pEnv, ikvm.runtime.Util.mapException(x)); - return IntPtr.Zero; + return (void*)IntPtr.Zero; } } @@ -2485,21 +2484,21 @@ internal static jlong GetDirectBufferCapacity(JNIEnv* pEnv, jobject buf) } } - internal static int GetObjectRefType(JNIEnv* pEnv, jobject obj) + internal static jobjectRefType GetObjectRefType(JNIEnv* pEnv, jobject obj) { int i = obj.ToInt32(); if (i >= 0) { - return JNILocalRefType; + return jobjectRefType.JNILocalRefType; } i = -i; if ((i & (1 << 30)) != 0) { - return JNIWeakGlobalRefType; + return jobjectRefType.JNIWeakGlobalRefType; } else { - return JNIGlobalRefType; + return jobjectRefType.JNIGlobalRefType; } } diff --git a/src/IKVM.Runtime/JNI/JNINativeInterface.cs b/src/IKVM.Runtime/JNI/JNINativeInterface.cs new file mode 100644 index 0000000000..a575613c61 --- /dev/null +++ b/src/IKVM.Runtime/JNI/JNINativeInterface.cs @@ -0,0 +1,1115 @@ +using System; +using System.Runtime.InteropServices; + +using IKVM.Runtime.JNI.Trampolines; + +namespace IKVM.Runtime.JNI +{ + + using jarray = System.IntPtr; + using jboolean = System.SByte; + using jbooleanArray = System.IntPtr; + using jbyte = System.SByte; + using jbyteArray = System.IntPtr; + using jchar = System.UInt16; + using jcharArray = System.IntPtr; + using jclass = System.IntPtr; + using jdouble = System.Double; + using jdoubleArray = System.IntPtr; + using jfieldID = System.IntPtr; + using jfloat = System.Single; + using jfloatArray = System.IntPtr; + using jint = System.Int32; + using jintArray = System.IntPtr; + using jlong = System.Int64; + using jlongArray = System.IntPtr; + using jmethodID = System.IntPtr; + using jobject = System.IntPtr; + using jobjectArray = System.IntPtr; + using jshort = System.Int16; + using jshortArray = System.IntPtr; + using jsize = System.Int32; + using jstring = System.IntPtr; + using jthrowable = System.IntPtr; + using jweak = System.IntPtr; + + + /// + /// Manged implementation of the JNINativeInterface structure. + /// + [StructLayout(LayoutKind.Sequential)] + unsafe struct JNINativeInterface + { + + /// + /// Maintains a structure in memory. + /// + class JNINativeInterfaceMemory + { + + internal readonly JNINativeInterface* handle; + + /// + /// Initializes a new instance. + /// + public JNINativeInterfaceMemory() + { + handle = (JNINativeInterface*)Marshal.AllocHGlobal(sizeof(JNINativeInterface)); + } + + /// + /// Finalizes the instance. + /// + ~JNINativeInterfaceMemory() + { + Marshal.FreeHGlobal((nint)handle); + } + + } + + #region Delegates + + delegate int GetMethodArgsDelegateType(JNIEnv* pEnv, nint methodID, byte* sig); + + delegate jint GetVersionDelegateType(JNIEnv* pEnv); + delegate jclass DefineClassDelegateType(JNIEnv* pEnv, byte* name, jobject loader, jbyte* pbuf, jint length); + delegate jclass FindClassDelegateType(JNIEnv* pEnv, byte* name); + + delegate jmethodID FromReflectedMethodDelegateType(JNIEnv* pEnv, jmethodID methodID); + delegate jfieldID FromReflectedFieldDelegateType(JNIEnv* pEnv, jfieldID fieldID); + + delegate jobject ToReflectedMethodDelegateType(JNIEnv* pEnv, jclass clazz_ignored, jmethodID method, jboolean isStatic); + + delegate jclass GetSuperclassDelegateType(JNIEnv* pEnv, jclass sub); + delegate jboolean IsAssignableFromDelegateType(JNIEnv* pEnv, jclass sub, jclass super); + + delegate jobject ToReflectedFieldDelegateType(JNIEnv* pEnv, jclass clazz_ignored, jfieldID field, jboolean isStatic); + + delegate jint ThrowDelegateType(JNIEnv* pEnv, jthrowable throwable); + delegate jint ThrowNewDelegateType(JNIEnv* pEnv, jclass clazz, byte* msg); + delegate jthrowable ExceptionOccurredDelegateType(JNIEnv* pEnv); + delegate void ExceptionDescribeDelegateType(JNIEnv* pEnv); + delegate void ExceptionClearDelegateType(JNIEnv* pEnv); + delegate void FatalErrorDelegateType(JNIEnv* pEnv, byte* msg); + + delegate jint PushLocalFrameDelegateType(JNIEnv* pEnv, jint capacity); + delegate jobject PopLocalFrameDelegateType(JNIEnv* pEnv, jobject result); + + delegate jobject NewGlobalRefDelegateType(JNIEnv* pEnv, jobject obj); + delegate void DeleteGlobalRefDelegateType(JNIEnv* pEnv, jobject obj); + delegate void DeleteLocalRefDelegateType(JNIEnv* pEnv, jobject obj); + delegate jboolean IsSameObjectDelegateType(JNIEnv* pEnv, jobject obj1, jobject obj2); + delegate jobject NewLocalRefDelegateType(JNIEnv* pEnv, jobject obj); + delegate jint EnsureLocalCapacityDelegateType(JNIEnv* pEnv, jint capacity); + + delegate jobject AllocObjectDelegateType(JNIEnv* pEnv, jclass clazz); + delegate jobject NewObjectADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + + delegate jclass GetObjectClassDelegateType(JNIEnv* pEnv, jobject obj); + delegate jboolean IsInstanceOfDelegateType(JNIEnv* pEnv, jobject obj, jclass clazz); + + delegate jmethodID GetMethodIDDelegateType(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig); + + delegate jobject CallObjectMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jboolean CallBooleanMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jbyte CallByteMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jchar CallCharMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jshort CallShortMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jint CallIntMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jlong CallLongMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jfloat CallFloatMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate jdouble CallDoubleMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + delegate void CallVoidMethodADelegateType(JNIEnv* pEnv, jobject obj, jmethodID methodID, jvalue* args); + + delegate jobject CallNonvirtualObjectMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jboolean CallNonvirtualBooleanMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jbyte CallNonvirtualByteMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jchar CallNonvirtualCharMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jshort CallNonvirtualShortMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jint CallNonvirtualIntMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jlong CallNonvirtualLongMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jfloat CallNonvirtualFloatMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate jdouble CallNonvirtualDoubleMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + delegate void CallNonvirtualVoidMethodADelegateType(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, jvalue* args); + + delegate jfieldID GetFieldIDDelegateType(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig); + + delegate jobject GetObjectFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jboolean GetBooleanFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jbyte GetByteFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jchar GetCharFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jshort GetShortFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jint GetIntFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jlong GetLongFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jfloat GetFloatFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + delegate jdouble GetDoubleFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID); + + delegate void SetObjectFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jobject val); + delegate void SetBooleanFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jboolean val); + delegate void SetByteFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jbyte val); + delegate void SetCharFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jchar val); + delegate void SetShortFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jshort val); + delegate void SetIntFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jint val); + delegate void SetLongFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jlong val); + delegate void SetFloatFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jfloat val); + delegate void SetDoubleFieldDelegateType(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jdouble val); + + delegate jmethodID GetStaticMethodIDDelegateType(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig); + + delegate jobject CallStaticObjectMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jboolean CallStaticBooleanMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jbyte CallStaticByteMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jchar CallStaticCharMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jshort CallStaticShortMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jint CallStaticIntMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jlong CallStaticLongMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jfloat CallStaticFloatMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate jdouble CallStaticDoubleMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + delegate void CallStaticVoidMethodADelegateType(JNIEnv* pEnv, jclass clazz, jmethodID methodID, jvalue* args); + + delegate jfieldID GetStaticFieldIDDelegateType(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig); + + delegate jobject GetStaticObjectFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jboolean GetStaticBooleanFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jbyte GetStaticByteFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jchar GetStaticCharFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jshort GetStaticShortFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jint GetStaticIntFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jlong GetStaticLongFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jfloat GetStaticFloatFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + delegate jdouble GetStaticDoubleFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID); + + delegate void SetStaticObjectFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jobject val); + delegate void SetStaticBooleanFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jboolean val); + delegate void SetStaticByteFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jbyte val); + delegate void SetStaticCharFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jchar val); + delegate void SetStaticShortFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jshort val); + delegate void SetStaticIntFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jint val); + delegate void SetStaticLongFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jlong val); + delegate void SetStaticFloatFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jfloat val); + delegate void SetStaticDoubleFieldDelegateType(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jdouble val); + + delegate jstring NewStringDelegateType(JNIEnv* pEnv, jchar* unicode, int len); + delegate jint GetStringLengthDelegateType(JNIEnv* pEnv, jstring str); + delegate jchar* GetStringCharsDelegateType(JNIEnv* pEnv, jstring str, jboolean* isCopy); + delegate void ReleaseStringCharsDelegateType(JNIEnv* pEnv, jstring str, jchar* chars); + + delegate jobject NewStringUTFDelegateType(JNIEnv* pEnv, byte* bytes); + delegate jint GetStringUTFLengthDelegateType(JNIEnv* pEnv, jstring str); + delegate byte* GetStringUTFCharsDelegateType(JNIEnv* pEnv, jstring @string, jboolean* isCopy); + delegate void ReleaseStringUTFCharsDelegateType(JNIEnv* pEnv, jstring @string, byte* utf); + + delegate jsize GetArrayLengthDelegateType(JNIEnv* pEnv, jarray array); + + delegate jobjectArray NewObjectArrayDelegateType(JNIEnv* pEnv, jsize len, jclass clazz, jobject init); + delegate jobject GetObjectArrayElementDelegateType(JNIEnv* pEnv, jarray array, jsize index); + delegate void SetObjectArrayElementDelegateType(JNIEnv* pEnv, jarray array, jsize index, jobject val); + + delegate jbooleanArray NewBooleanArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jbyteArray NewByteArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jcharArray NewCharArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jshortArray NewShortArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jintArray NewIntArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jlongArray NewLongArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jfloatArray NewFloatArrayDelegateType(JNIEnv* pEnv, jsize len); + delegate jdoubleArray NewDoubleArrayDelegateType(JNIEnv* pEnv, jsize len); + + delegate jboolean* GetBooleanArrayElementsDelegateType(JNIEnv* pEnv, jbooleanArray array, jboolean* isCopy); + delegate jbyte* GetByteArrayElementsDelegateType(JNIEnv* pEnv, jbyteArray array, jboolean* isCopy); + delegate jchar* GetCharArrayElementsDelegateType(JNIEnv* pEnv, jcharArray array, jboolean* isCopy); + delegate jshort* GetShortArrayElementsDelegateType(JNIEnv* pEnv, jshortArray array, jboolean* isCopy); + delegate jint* GetIntArrayElementsDelegateType(JNIEnv* pEnv, jintArray array, jboolean* isCopy); + delegate jlong* GetLongArrayElementsDelegateType(JNIEnv* pEnv, jlongArray array, jboolean* isCopy); + delegate jfloat* GetFloatArrayElementsDelegateType(JNIEnv* pEnv, jfloatArray array, jboolean* isCopy); + delegate jdouble* GetDoubleArrayElementsDelegateType(JNIEnv* pEnv, jdoubleArray array, jboolean* isCopy); + + delegate void ReleaseBooleanArrayElementsDelegateType(JNIEnv* pEnv, jbooleanArray array, jboolean* elems, jint mode); + delegate void ReleaseByteArrayElementsDelegateType(JNIEnv* pEnv, jbyteArray array, jbyte* elems, jint mode); + delegate void ReleaseCharArrayElementsDelegateType(JNIEnv* pEnv, jcharArray array, jchar* elems, jint mode); + delegate void ReleaseShortArrayElementsDelegateType(JNIEnv* pEnv, jshortArray array, jshort* elems, jint mode); + delegate void ReleaseIntArrayElementsDelegateType(JNIEnv* pEnv, jintArray array, jint* elems, jint mode); + delegate void ReleaseLongArrayElementsDelegateType(JNIEnv* pEnv, jlongArray array, jlong* elems, jint mode); + delegate void ReleaseFloatArrayElementsDelegateType(JNIEnv* pEnv, jfloatArray array, jfloat* elems, jint mode); + delegate void ReleaseDoubleArrayElementsDelegateType(JNIEnv* pEnv, jdoubleArray array, jdouble* elems, jint mode); + + delegate void GetBooleanArrayRegionDelegateType(JNIEnv* pEnv, jbooleanArray array, jsize start, jsize len, jboolean* buf); + delegate void GetByteArrayRegionDelegateType(JNIEnv* pEnv, jbyteArray array, jsize start, jsize len, jbyte* buf); + delegate void GetCharArrayRegionDelegateType(JNIEnv* pEnv, jcharArray array, jsize start, jsize len, jchar* buf); + delegate void GetShortArrayRegionDelegateType(JNIEnv* pEnv, jshortArray array, jsize start, jsize len, jshort* buf); + delegate void GetIntArrayRegionDelegateType(JNIEnv* pEnv, jintArray array, jsize start, jsize len, jint* buf); + delegate void GetLongArrayRegionDelegateType(JNIEnv* pEnv, jlongArray array, jsize start, jsize len, jlong* buf); + delegate void GetFloatArrayRegionDelegateType(JNIEnv* pEnv, jfloatArray array, jsize start, jsize len, jfloat* buf); + delegate void GetDoubleArrayRegionDelegateType(JNIEnv* pEnv, jdoubleArray array, jsize start, jsize len, jdouble* buf); + + delegate void SetBooleanArrayRegionDelegateType(JNIEnv* pEnv, jbooleanArray array, jsize start, jsize len, jboolean* buf); + delegate void SetByteArrayRegionDelegateType(JNIEnv* pEnv, jbyteArray array, jsize start, jsize len, jbyte* buf); + delegate void SetCharArrayRegionDelegateType(JNIEnv* pEnv, jcharArray array, jsize start, jsize len, jchar* buf); + delegate void SetShortArrayRegionDelegateType(JNIEnv* pEnv, jshortArray array, jsize start, jsize len, jshort* buf); + delegate void SetIntArrayRegionDelegateType(JNIEnv* pEnv, jintArray array, jsize start, jsize len, jint* buf); + delegate void SetLongArrayRegionDelegateType(JNIEnv* pEnv, jlongArray array, jsize start, jsize len, jlong* buf); + delegate void SetFloatArrayRegionDelegateType(JNIEnv* pEnv, jfloatArray array, jsize start, jsize len, jfloat* buf); + delegate void SetDoubleArrayRegionDelegateType(JNIEnv* pEnv, jdoubleArray array, jsize start, jsize len, jdouble* buf); + + delegate jint RegisterNativesDelegateType(JNIEnv* pEnv, jclass clazz, JNIEnv.JNINativeMethod* methods, jint nMethods); + delegate jint UnregisterNativesDelegateType(JNIEnv* pEnv, jclass clazz); + + delegate jint MonitorEnterDelegateType(JNIEnv* pEnv, jobject obj); + delegate jint MonitorExitDelegateType(JNIEnv* pEnv, jobject obj); + + delegate jint GetJavaVMDelegateType(JNIEnv* pEnv, JavaVM** ppJavaVM); + + delegate void GetStringRegionDelegateType(JNIEnv* pEnv, jstring str, jsize start, jsize len, jchar* buf); + delegate void GetStringUTFRegionDelegateType(JNIEnv* pEnv, jstring str, jsize start, jsize len, byte* buf); + + delegate void* GetPrimitiveArrayCriticalDelegateType(JNIEnv* pEnv, jarray array, jboolean* isCopy); + delegate void ReleasePrimitiveArrayCriticalDelegateType(JNIEnv* pEnv, jarray array, void* carray, jint mode); + + delegate jchar* GetStringCriticalDelegateType(JNIEnv* pEnv, jstring str, jboolean* isCopy); + delegate void ReleaseStringCriticalDelegateType(JNIEnv* pEnv, jstring str, jchar* cstring); + + delegate jweak NewWeakGlobalRefDelegateType(JNIEnv* pEnv, jobject obj); + delegate void DeleteWeakGlobalRefDelegateType(JNIEnv* pEnv, jweak obj); + + delegate jboolean ExceptionCheckDelegateType(JNIEnv* pEnv); + + delegate jobject NewDirectByteBufferDelegateType(JNIEnv* pEnv, IntPtr address, jlong capacity); + delegate void* GetDirectBufferAddressDelegateType(JNIEnv* pEnv, jobject buf); + delegate jlong GetDirectBufferCapacityDelegateType(JNIEnv* pEnv, jobject buf); + + delegate jobjectRefType GetObjectRefTypeDelegateType(JNIEnv* pEnv, jobject obj); + + #endregion + + static readonly JNINativeInterfaceMemory memory = new(); + static readonly JNINativeInterface* handle = memory.handle; + + /// + /// Gets a pointer to the JNINativeInterface table. + /// + public static JNINativeInterface* Handle => handle; + + #region Delegates + + readonly static GetMethodArgsDelegateType GetMethodArgsDelegate = JNIEnv.GetMethodArgs; + + readonly static GetVersionDelegateType GetVersionDelegate = JNIEnv.GetVersion; + readonly static DefineClassDelegateType DefineClassDelegate = JNIEnv.DefineClass; + readonly static FindClassDelegateType FindClassDelegate = JNIEnv.FindClass; + + readonly static FromReflectedMethodDelegateType FromReflectedMethodDelegate = JNIEnv.FromReflectedMethod; + readonly static FromReflectedFieldDelegateType FromReflectedFieldDelegate = JNIEnv.FromReflectedField; + + readonly static ToReflectedMethodDelegateType ToReflectedMethodDelegate = JNIEnv.ToReflectedMethod; + + readonly static GetSuperclassDelegateType GetSuperclassDelegate = JNIEnv.GetSuperclass; + readonly static IsAssignableFromDelegateType IsAssignableFromDelegate = JNIEnv.IsAssignableFrom; + + readonly static ToReflectedFieldDelegateType ToReflectedFieldDelegate = JNIEnv.ToReflectedField; + + readonly static ThrowDelegateType ThrowDelegate = JNIEnv.Throw; + readonly static ThrowNewDelegateType ThrowNewDelegate = JNIEnv.ThrowNew; + readonly static ExceptionOccurredDelegateType ExceptionOccurredDelegate = JNIEnv.ExceptionOccurred; + readonly static ExceptionDescribeDelegateType ExceptionDescribeDelegate = JNIEnv.ExceptionDescribe; + readonly static ExceptionClearDelegateType ExceptionClearDelegate = JNIEnv.ExceptionClear; + readonly static FatalErrorDelegateType FatalErrorDelegate = JNIEnv.FatalError; + + readonly static PushLocalFrameDelegateType PushLocalFrameDelegate = JNIEnv.PushLocalFrame; + readonly static PopLocalFrameDelegateType PopLocalFrameDelegate = JNIEnv.PopLocalFrame; + + readonly static NewGlobalRefDelegateType NewGlobalRefDelegate = JNIEnv.NewGlobalRef; + readonly static DeleteGlobalRefDelegateType DeleteGlobalRefDelegate = JNIEnv.DeleteGlobalRef; + readonly static DeleteLocalRefDelegateType DeleteLocalRefDelegate = JNIEnv.DeleteLocalRef; + readonly static IsSameObjectDelegateType IsSameObjectDelegate = JNIEnv.IsSameObject; + readonly static NewLocalRefDelegateType NewLocalRefDelegate = JNIEnv.NewLocalRef; + readonly static EnsureLocalCapacityDelegateType EnsureLocalCapacityDelegate = JNIEnv.EnsureLocalCapacity; + + readonly static AllocObjectDelegateType AllocObjectDelegate = JNIEnv.AllocObject; + readonly static NewObjectADelegateType NewObjectADelegate = JNIEnv.NewObjectA; + + readonly static GetObjectClassDelegateType GetObjectClassDelegate = JNIEnv.GetObjectClass; + readonly static IsInstanceOfDelegateType IsInstanceOfDelegate = JNIEnv.IsInstanceOf; + + readonly static GetMethodIDDelegateType GetMethodIDDelegate = JNIEnv.GetMethodID; + + readonly static CallObjectMethodADelegateType CallObjectMethodADelegate = JNIEnv.CallObjectMethodA; + readonly static CallBooleanMethodADelegateType CallBooleanMethodADelegate = JNIEnv.CallBooleanMethodA; + readonly static CallByteMethodADelegateType CallByteMethodADelegate = JNIEnv.CallByteMethodA; + readonly static CallCharMethodADelegateType CallCharMethodADelegate = JNIEnv.CallCharMethodA; + readonly static CallShortMethodADelegateType CallShortMethodADelegate = JNIEnv.CallShortMethodA; + readonly static CallIntMethodADelegateType CallIntMethodADelegate = JNIEnv.CallIntMethodA; + readonly static CallLongMethodADelegateType CallLongMethodADelegate = JNIEnv.CallLongMethodA; + readonly static CallFloatMethodADelegateType CallFloatMethodADelegate = JNIEnv.CallFloatMethodA; + readonly static CallDoubleMethodADelegateType CallDoubleMethodADelegate = JNIEnv.CallDoubleMethodA; + readonly static CallVoidMethodADelegateType CallVoidMethodADelegate = JNIEnv.CallVoidMethodA; + + readonly static CallNonvirtualObjectMethodADelegateType CallNonvirtualObjectMethodADelegate = JNIEnv.CallNonvirtualObjectMethodA; + readonly static CallNonvirtualBooleanMethodADelegateType CallNonvirtualBooleanMethodADelegate = JNIEnv.CallNonvirtualBooleanMethodA; + readonly static CallNonvirtualByteMethodADelegateType CallNonvirtualByteMethodADelegate = JNIEnv.CallNonvirtualByteMethodA; + readonly static CallNonvirtualCharMethodADelegateType CallNonvirtualCharMethodADelegate = JNIEnv.CallNonvirtualCharMethodA; + readonly static CallNonvirtualShortMethodADelegateType CallNonvirtualShortMethodADelegate = JNIEnv.CallNonvirtualShortMethodA; + readonly static CallNonvirtualIntMethodADelegateType CallNonvirtualIntMethodADelegate = JNIEnv.CallNonvirtualIntMethodA; + readonly static CallNonvirtualLongMethodADelegateType CallNonvirtualLongMethodADelegate = JNIEnv.CallNonvirtualLongMethodA; + readonly static CallNonvirtualFloatMethodADelegateType CallNonvirtualFloatMethodADelegate = JNIEnv.CallNonvirtualFloatMethodA; + readonly static CallNonvirtualDoubleMethodADelegateType CallNonvirtualDoubleMethodADelegate = JNIEnv.CallNonvirtualDoubleMethodA; + readonly static CallNonvirtualVoidMethodADelegateType CallNonvirtualVoidMethodADelegate = JNIEnv.CallNonvirtualVoidMethodA; + + readonly static GetFieldIDDelegateType GetFieldIDDelegate = JNIEnv.GetFieldID; + + readonly static GetObjectFieldDelegateType GetObjectFieldDelegate = JNIEnv.GetObjectField; + readonly static GetBooleanFieldDelegateType GetBooleanFieldDelegate = JNIEnv.GetBooleanField; + readonly static GetByteFieldDelegateType GetByteFieldDelegate = JNIEnv.GetByteField; + readonly static GetCharFieldDelegateType GetCharFieldDelegate = JNIEnv.GetCharField; + readonly static GetShortFieldDelegateType GetShortFieldDelegate = JNIEnv.GetShortField; + readonly static GetIntFieldDelegateType GetIntFieldDelegate = JNIEnv.GetIntField; + readonly static GetLongFieldDelegateType GetLongFieldDelegate = JNIEnv.GetLongField; + readonly static GetFloatFieldDelegateType GetFloatFieldDelegate = JNIEnv.GetFloatField; + readonly static GetDoubleFieldDelegateType GetDoubleFieldDelegate = JNIEnv.GetDoubleField; + + readonly static SetObjectFieldDelegateType SetObjectFieldDelegate = JNIEnv.SetObjectField; + readonly static SetBooleanFieldDelegateType SetBooleanFieldDelegate = JNIEnv.SetBooleanField; + readonly static SetByteFieldDelegateType SetByteFieldDelegate = JNIEnv.SetByteField; + readonly static SetCharFieldDelegateType SetCharFieldDelegate = JNIEnv.SetCharField; + readonly static SetShortFieldDelegateType SetShortFieldDelegate = JNIEnv.SetShortField; + readonly static SetIntFieldDelegateType SetIntFieldDelegate = JNIEnv.SetIntField; + readonly static SetLongFieldDelegateType SetLongFieldDelegate = JNIEnv.SetLongField; + readonly static SetFloatFieldDelegateType SetFloatFieldDelegate = JNIEnv.SetFloatField; + readonly static SetDoubleFieldDelegateType SetDoubleFieldDelegate = JNIEnv.SetDoubleField; + + readonly static GetStaticMethodIDDelegateType GetStaticMethodIDDelegate = JNIEnv.GetStaticMethodID; + + readonly static CallStaticObjectMethodADelegateType CallStaticObjectMethodADelegate = JNIEnv.CallStaticObjectMethodA; + readonly static CallStaticBooleanMethodADelegateType CallStaticBooleanMethodADelegate = JNIEnv.CallStaticBooleanMethodA; + readonly static CallStaticByteMethodADelegateType CallStaticByteMethodADelegate = JNIEnv.CallStaticByteMethodA; + readonly static CallStaticCharMethodADelegateType CallStaticCharMethodADelegate = JNIEnv.CallStaticCharMethodA; + readonly static CallStaticShortMethodADelegateType CallStaticShortMethodADelegate = JNIEnv.CallStaticShortMethodA; + readonly static CallStaticIntMethodADelegateType CallStaticIntMethodADelegate = JNIEnv.CallStaticIntMethodA; + readonly static CallStaticLongMethodADelegateType CallStaticLongMethodADelegate = JNIEnv.CallStaticLongMethodA; + readonly static CallStaticFloatMethodADelegateType CallStaticFloatMethodADelegate = JNIEnv.CallStaticFloatMethodA; + readonly static CallStaticDoubleMethodADelegateType CallStaticDoubleMethodADelegate = JNIEnv.CallStaticDoubleMethodA; + readonly static CallStaticVoidMethodADelegateType CallStaticVoidMethodADelegate = JNIEnv.CallStaticVoidMethodA; + + readonly static GetStaticFieldIDDelegateType GetStaticFieldIDDelegate = JNIEnv.GetStaticFieldID; + readonly static GetStaticObjectFieldDelegateType GetStaticObjectFieldDelegate = JNIEnv.GetStaticObjectField; + readonly static GetStaticBooleanFieldDelegateType GetStaticBooleanFieldDelegate = JNIEnv.GetStaticBooleanField; + readonly static GetStaticByteFieldDelegateType GetStaticByteFieldDelegate = JNIEnv.GetStaticByteField; + readonly static GetStaticCharFieldDelegateType GetStaticCharFieldDelegate = JNIEnv.GetStaticCharField; + readonly static GetStaticShortFieldDelegateType GetStaticShortFieldDelegate = JNIEnv.GetStaticShortField; + readonly static GetStaticIntFieldDelegateType GetStaticIntFieldDelegate = JNIEnv.GetStaticIntField; + readonly static GetStaticLongFieldDelegateType GetStaticLongFieldDelegate = JNIEnv.GetStaticLongField; + readonly static GetStaticFloatFieldDelegateType GetStaticFloatFieldDelegate = JNIEnv.GetStaticFloatField; + readonly static GetStaticDoubleFieldDelegateType GetStaticDoubleFieldDelegate = JNIEnv.GetStaticDoubleField; + + readonly static SetStaticObjectFieldDelegateType SetStaticObjectFieldDelegate = JNIEnv.SetStaticObjectField; + readonly static SetStaticBooleanFieldDelegateType SetStaticBooleanFieldDelegate = JNIEnv.SetStaticBooleanField; + readonly static SetStaticByteFieldDelegateType SetStaticByteFieldDelegate = JNIEnv.SetStaticByteField; + readonly static SetStaticCharFieldDelegateType SetStaticCharFieldDelegate = JNIEnv.SetStaticCharField; + readonly static SetStaticShortFieldDelegateType SetStaticShortFieldDelegate = JNIEnv.SetStaticShortField; + readonly static SetStaticIntFieldDelegateType SetStaticIntFieldDelegate = JNIEnv.SetStaticIntField; + readonly static SetStaticLongFieldDelegateType SetStaticLongFieldDelegate = JNIEnv.SetStaticLongField; + readonly static SetStaticFloatFieldDelegateType SetStaticFloatFieldDelegate = JNIEnv.SetStaticFloatField; + readonly static SetStaticDoubleFieldDelegateType SetStaticDoubleFieldDelegate = JNIEnv.SetStaticDoubleField; + + readonly static NewStringDelegateType NewStringDelegate = JNIEnv.NewString; + readonly static GetStringLengthDelegateType GetStringLengthDelegate = JNIEnv.GetStringLength; + readonly static GetStringCharsDelegateType GetStringCharsDelegate = JNIEnv.GetStringChars; + readonly static ReleaseStringCharsDelegateType ReleaseStringCharsDelegate = JNIEnv.ReleaseStringChars; + + readonly static NewStringUTFDelegateType NewStringUTFDelegate = JNIEnv.NewStringUTF; + readonly static GetStringUTFLengthDelegateType GetStringUTFLengthDelegate = JNIEnv.GetStringUTFLength; + readonly static GetStringUTFCharsDelegateType GetStringUTFCharsDelegate = JNIEnv.GetStringUTFChars; + readonly static ReleaseStringUTFCharsDelegateType ReleaseStringUTFCharsDelegate = JNIEnv.ReleaseStringUTFChars; + + readonly static GetArrayLengthDelegateType GetArrayLengthDelegate = JNIEnv.GetArrayLength; + + readonly static NewObjectArrayDelegateType NewObjectArrayDelegate = JNIEnv.NewObjectArray; + readonly static GetObjectArrayElementDelegateType GetObjectArrayElementDelegate = JNIEnv.GetObjectArrayElement; + readonly static SetObjectArrayElementDelegateType SetObjectArrayElementDelegate = JNIEnv.SetObjectArrayElement; + + readonly static NewBooleanArrayDelegateType NewBooleanArrayDelegate = JNIEnv.NewBooleanArray; + readonly static NewByteArrayDelegateType NewByteArrayDelegate = JNIEnv.NewByteArray; + readonly static NewCharArrayDelegateType NewCharArrayDelegate = JNIEnv.NewCharArray; + readonly static NewShortArrayDelegateType NewShortArrayDelegate = JNIEnv.NewShortArray; + readonly static NewIntArrayDelegateType NewIntArrayDelegate = JNIEnv.NewIntArray; + readonly static NewLongArrayDelegateType NewLongArrayDelegate = JNIEnv.NewLongArray; + readonly static NewFloatArrayDelegateType NewFloatArrayDelegate = JNIEnv.NewFloatArray; + readonly static NewDoubleArrayDelegateType NewDoubleArrayDelegate = JNIEnv.NewDoubleArray; + + readonly static GetBooleanArrayElementsDelegateType GetBooleanArrayElementsDelegate = JNIEnv.GetBooleanArrayElements; + readonly static GetByteArrayElementsDelegateType GetByteArrayElementsDelegate = JNIEnv.GetByteArrayElements; + readonly static GetCharArrayElementsDelegateType GetCharArrayElementsDelegate = JNIEnv.GetCharArrayElements; + readonly static GetShortArrayElementsDelegateType GetShortArrayElementsDelegate = JNIEnv.GetShortArrayElements; + readonly static GetIntArrayElementsDelegateType GetIntArrayElementsDelegate = JNIEnv.GetIntArrayElements; + readonly static GetLongArrayElementsDelegateType GetLongArrayElementsDelegate = JNIEnv.GetLongArrayElements; + readonly static GetFloatArrayElementsDelegateType GetFloatArrayElementsDelegate = JNIEnv.GetFloatArrayElements; + readonly static GetDoubleArrayElementsDelegateType GetDoubleArrayElementsDelegate = JNIEnv.GetDoubleArrayElements; + + readonly static ReleaseBooleanArrayElementsDelegateType ReleaseBooleanArrayElementsDelegate = JNIEnv.ReleaseBooleanArrayElements; + readonly static ReleaseByteArrayElementsDelegateType ReleaseByteArrayElementsDelegate = JNIEnv.ReleaseByteArrayElements; + readonly static ReleaseCharArrayElementsDelegateType ReleaseCharArrayElementsDelegate = JNIEnv.ReleaseCharArrayElements; + readonly static ReleaseShortArrayElementsDelegateType ReleaseShortArrayElementsDelegate = JNIEnv.ReleaseShortArrayElements; + readonly static ReleaseIntArrayElementsDelegateType ReleaseIntArrayElementsDelegate = JNIEnv.ReleaseIntArrayElements; + readonly static ReleaseLongArrayElementsDelegateType ReleaseLongArrayElementsDelegate = JNIEnv.ReleaseLongArrayElements; + readonly static ReleaseFloatArrayElementsDelegateType ReleaseFloatArrayElementsDelegate = JNIEnv.ReleaseFloatArrayElements; + readonly static ReleaseDoubleArrayElementsDelegateType ReleaseDoubleArrayElementsDelegate = JNIEnv.ReleaseDoubleArrayElements; + + readonly static GetBooleanArrayRegionDelegateType GetBooleanArrayRegionDelegate = JNIEnv.GetBooleanArrayRegion; + readonly static GetByteArrayRegionDelegateType GetByteArrayRegionDelegate = JNIEnv.GetByteArrayRegion; + readonly static GetCharArrayRegionDelegateType GetCharArrayRegionDelegate = JNIEnv.GetCharArrayRegion; + readonly static GetShortArrayRegionDelegateType GetShortArrayRegionDelegate = JNIEnv.GetShortArrayRegion; + readonly static GetIntArrayRegionDelegateType GetIntArrayRegionDelegate = JNIEnv.GetIntArrayRegion; + readonly static GetLongArrayRegionDelegateType GetLongArrayRegionDelegate = JNIEnv.GetLongArrayRegion; + readonly static GetFloatArrayRegionDelegateType GetFloatArrayRegionDelegate = JNIEnv.GetFloatArrayRegion; + readonly static GetDoubleArrayRegionDelegateType GetDoubleArrayRegionDelegate = JNIEnv.GetDoubleArrayRegion; + + readonly static SetBooleanArrayRegionDelegateType SetBooleanArrayRegionDelegate = JNIEnv.SetBooleanArrayRegion; + readonly static SetByteArrayRegionDelegateType SetByteArrayRegionDelegate = JNIEnv.SetByteArrayRegion; + readonly static SetCharArrayRegionDelegateType SetCharArrayRegionDelegate = JNIEnv.SetCharArrayRegion; + readonly static SetShortArrayRegionDelegateType SetShortArrayRegionDelegate = JNIEnv.SetShortArrayRegion; + readonly static SetIntArrayRegionDelegateType SetIntArrayRegionDelegate = JNIEnv.SetIntArrayRegion; + readonly static SetLongArrayRegionDelegateType SetLongArrayRegionDelegate = JNIEnv.SetLongArrayRegion; + readonly static SetFloatArrayRegionDelegateType SetFloatArrayRegionDelegate = JNIEnv.SetFloatArrayRegion; + readonly static SetDoubleArrayRegionDelegateType SetDoubleArrayRegionDelegate = JNIEnv.SetDoubleArrayRegion; + + readonly static RegisterNativesDelegateType RegisterNativesDelegate = JNIEnv.RegisterNatives; + readonly static UnregisterNativesDelegateType UnregisterNativesDelegate = JNIEnv.UnregisterNatives; + + readonly static MonitorEnterDelegateType MonitorEnterDelegate = JNIEnv.MonitorEnter; + readonly static MonitorExitDelegateType MonitorExitDelegate = JNIEnv.MonitorExit; + + readonly static GetJavaVMDelegateType GetJavaVMDelegate = JNIEnv.GetJavaVM; + + readonly static GetStringRegionDelegateType GetStringRegionDelegate = JNIEnv.GetStringRegion; + readonly static GetStringUTFRegionDelegateType GetStringUTFRegionDelegate = JNIEnv.GetStringUTFRegion; + + readonly static GetPrimitiveArrayCriticalDelegateType GetPrimitiveArrayCriticalDelegate = JNIEnv.GetPrimitiveArrayCritical; + readonly static ReleasePrimitiveArrayCriticalDelegateType ReleasePrimitiveArrayCriticalDelegate = JNIEnv.ReleasePrimitiveArrayCritical; + + readonly static GetStringCriticalDelegateType GetStringCriticalDelegate = JNIEnv.GetStringCritical; + readonly static ReleaseStringCriticalDelegateType ReleaseStringCriticalDelegate = JNIEnv.ReleaseStringCritical; + + readonly static NewWeakGlobalRefDelegateType NewWeakGlobalRefDelegate = JNIEnv.NewWeakGlobalRef; + readonly static DeleteWeakGlobalRefDelegateType DeleteWeakGlobalRefDelegate = JNIEnv.DeleteWeakGlobalRef; + + readonly static ExceptionCheckDelegateType ExceptionCheckDelegate = JNIEnv.ExceptionCheck; + + readonly static NewDirectByteBufferDelegateType NewDirectByteBufferDelegate = JNIEnv.NewDirectByteBuffer; + readonly static GetDirectBufferAddressDelegateType GetDirectBufferAddressDelegate = JNIEnv.GetDirectBufferAddress; + readonly static GetDirectBufferCapacityDelegateType GetDirectBufferCapacityDelegate = JNIEnv.GetDirectBufferCapacity; + + readonly static GetObjectRefTypeDelegateType GetObjectRefTypeDelegate = JNIEnv.GetObjectRefType; + + #endregion + + /// + /// Initializes the static instance. + /// + static JNINativeInterface() + { + JNIVM.jvmCreated = true; + + handle->GetMethodArgs = (void*)Marshal.GetFunctionPointerForDelegate(GetMethodArgsDelegate); + handle->reserved1 = null; + handle->reserved2 = null; + + handle->reserved3 = null; + handle->GetVersion = (void*)Marshal.GetFunctionPointerForDelegate(GetVersionDelegate); + + handle->DefineClass = (void*)Marshal.GetFunctionPointerForDelegate(DefineClassDelegate); + handle->FindClass = (void*)Marshal.GetFunctionPointerForDelegate(FindClassDelegate); + + handle->FromReflectedMethod = (void*)Marshal.GetFunctionPointerForDelegate(FromReflectedMethodDelegate); + handle->FromReflectedField = (void*)Marshal.GetFunctionPointerForDelegate(FromReflectedFieldDelegate); + handle->ToReflectedMethod = (void*)Marshal.GetFunctionPointerForDelegate(ToReflectedMethodDelegate); + + handle->GetSuperclass = (void*)Marshal.GetFunctionPointerForDelegate(GetSuperclassDelegate); + handle->IsAssignableFrom = (void*)Marshal.GetFunctionPointerForDelegate(IsAssignableFromDelegate); + + handle->ToReflectedField = (void*)Marshal.GetFunctionPointerForDelegate(ToReflectedFieldDelegate); + + handle->Throw = (void*)Marshal.GetFunctionPointerForDelegate(ThrowDelegate); + handle->ThrowNew = (void*)Marshal.GetFunctionPointerForDelegate(ThrowNewDelegate); + handle->ExceptionOccurred = (void*)Marshal.GetFunctionPointerForDelegate(ExceptionOccurredDelegate); + handle->ExceptionDescribe = (void*)Marshal.GetFunctionPointerForDelegate(ExceptionDescribeDelegate); + handle->ExceptionClear = (void*)Marshal.GetFunctionPointerForDelegate(ExceptionClearDelegate); + handle->FatalError = (void*)Marshal.GetFunctionPointerForDelegate(FatalErrorDelegate); + + handle->PushLocalFrame = (void*)Marshal.GetFunctionPointerForDelegate(PushLocalFrameDelegate); + handle->PopLocalFrame = (void*)Marshal.GetFunctionPointerForDelegate(PopLocalFrameDelegate); + + handle->NewGlobalRef = (void*)Marshal.GetFunctionPointerForDelegate(NewGlobalRefDelegate); + handle->DeleteGlobalRef = (void*)Marshal.GetFunctionPointerForDelegate(DeleteGlobalRefDelegate); + handle->DeleteLocalRef = (void*)Marshal.GetFunctionPointerForDelegate(DeleteLocalRefDelegate); + handle->IsSameObject = (void*)Marshal.GetFunctionPointerForDelegate(IsSameObjectDelegate); + + handle->NewLocalRef = (void*)Marshal.GetFunctionPointerForDelegate(NewLocalRefDelegate); + handle->EnsureLocalCapacity = (void*)Marshal.GetFunctionPointerForDelegate(EnsureLocalCapacityDelegate); + + handle->AllocObject = (void*)Marshal.GetFunctionPointerForDelegate(AllocObjectDelegate); + handle->NewObject = (void*)FunctionTable.Instance.JNI_NewObject; + handle->NewObjectV = (void*)FunctionTable.Instance.JNI_NewObjectV; + handle->NewObjectA = (void*)Marshal.GetFunctionPointerForDelegate(NewObjectADelegate); + + handle->GetObjectClass = (void*)Marshal.GetFunctionPointerForDelegate(GetObjectClassDelegate); + handle->IsInstanceOf = (void*)Marshal.GetFunctionPointerForDelegate(IsInstanceOfDelegate); + + handle->GetMethodID = (void*)Marshal.GetFunctionPointerForDelegate(GetMethodIDDelegate); + + handle->CallObjectMethod = (void*)FunctionTable.Instance.JNI_CallObjectMethod; + handle->CallObjectMethodV = (void*)FunctionTable.Instance.JNI_CallObjectMethodV; + handle->CallObjectMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallObjectMethodADelegate); + + handle->CallBooleanMethod = (void*)FunctionTable.Instance.JNI_CallBooleanMethod; + handle->CallBooleanMethodV = (void*)FunctionTable.Instance.JNI_CallBooleanMethodV; + handle->CallBooleanMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallBooleanMethodADelegate); + + handle->CallByteMethod = (void*)FunctionTable.Instance.JNI_CallByteMethod; + handle->CallByteMethodV = (void*)FunctionTable.Instance.JNI_CallByteMethodV; + handle->CallByteMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallByteMethodADelegate); + + handle->CallCharMethod = (void*)FunctionTable.Instance.JNI_CallCharMethod; + handle->CallCharMethodV = (void*)FunctionTable.Instance.JNI_CallCharMethodV; + handle->CallCharMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallCharMethodADelegate); + + handle->CallShortMethod = (void*)FunctionTable.Instance.JNI_CallShortMethod; + handle->CallShortMethodV = (void*)FunctionTable.Instance.JNI_CallShortMethodV; + handle->CallShortMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallShortMethodADelegate); + + handle->CallIntMethod = (void*)FunctionTable.Instance.JNI_CallIntMethod; + handle->CallIntMethodV = (void*)FunctionTable.Instance.JNI_CallIntMethodV; + handle->CallIntMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallIntMethodADelegate); + + handle->CallLongMethod = (void*)FunctionTable.Instance.JNI_CallLongMethod; + handle->CallLongMethodV = (void*)FunctionTable.Instance.JNI_CallLongMethodV; + handle->CallLongMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallLongMethodADelegate); + + handle->CallFloatMethod = (void*)FunctionTable.Instance.JNI_CallFloatMethod; + handle->CallFloatMethodV = (void*)FunctionTable.Instance.JNI_CallFloatMethodV; + handle->CallFloatMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallFloatMethodADelegate); + + handle->CallDoubleMethod = (void*)FunctionTable.Instance.JNI_CallDoubleMethod; + handle->CallDoubleMethodV = (void*)FunctionTable.Instance.JNI_CallDoubleMethodV; + handle->CallDoubleMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallDoubleMethodADelegate); + + handle->CallVoidMethod = (void*)FunctionTable.Instance.JNI_CallVoidMethod; + handle->CallVoidMethodV = (void*)FunctionTable.Instance.JNI_CallVoidMethodV; + handle->CallVoidMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallVoidMethodADelegate); + + handle->CallNonvirtualObjectMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualObjectMethod; + handle->CallNonvirtualObjectMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualObjectMethodV; + handle->CallNonvirtualObjectMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualObjectMethodADelegate); + + handle->CallNonvirtualBooleanMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualBooleanMethod; + handle->CallNonvirtualBooleanMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualBooleanMethodV; + handle->CallNonvirtualBooleanMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualBooleanMethodADelegate); + + handle->CallNonvirtualByteMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualByteMethod; + handle->CallNonvirtualByteMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualByteMethodV; + handle->CallNonvirtualByteMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualByteMethodADelegate); + + handle->CallNonvirtualCharMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualCharMethod; + handle->CallNonvirtualCharMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualCharMethodV; + handle->CallNonvirtualCharMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualCharMethodADelegate); + + handle->CallNonvirtualShortMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualShortMethod; + handle->CallNonvirtualShortMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualShortMethodV; + handle->CallNonvirtualShortMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualShortMethodADelegate); + + handle->CallNonvirtualIntMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualIntMethod; + handle->CallNonvirtualIntMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualIntMethodV; + handle->CallNonvirtualIntMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualIntMethodADelegate); + + handle->CallNonvirtualLongMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualLongMethod; + handle->CallNonvirtualLongMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualLongMethodV; + handle->CallNonvirtualLongMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualLongMethodADelegate); + + handle->CallNonvirtualFloatMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualFloatMethod; + handle->CallNonvirtualFloatMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualFloatMethodV; + handle->CallNonvirtualFloatMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualFloatMethodADelegate); + + handle->CallNonvirtualDoubleMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualDoubleMethod; + handle->CallNonvirtualDoubleMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualDoubleMethodV; + handle->CallNonvirtualDoubleMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualDoubleMethodADelegate); + + handle->CallNonvirtualVoidMethod = (void*)FunctionTable.Instance.JNI_CallNonvirtualVoidMethod; + handle->CallNonvirtualVoidMethodV = (void*)FunctionTable.Instance.JNI_CallNonvirtualVoidMethodV; + handle->CallNonvirtualVoidMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallNonvirtualVoidMethodADelegate); + + handle->GetFieldID = (void*)Marshal.GetFunctionPointerForDelegate(GetFieldIDDelegate); + + handle->GetObjectField = (void*)Marshal.GetFunctionPointerForDelegate(GetObjectFieldDelegate); + handle->GetBooleanField = (void*)Marshal.GetFunctionPointerForDelegate(GetBooleanFieldDelegate); + handle->GetByteField = (void*)Marshal.GetFunctionPointerForDelegate(GetByteFieldDelegate); + handle->GetCharField = (void*)Marshal.GetFunctionPointerForDelegate(GetCharFieldDelegate); + handle->GetShortField = (void*)Marshal.GetFunctionPointerForDelegate(GetShortFieldDelegate); + handle->GetIntField = (void*)Marshal.GetFunctionPointerForDelegate(GetIntFieldDelegate); + handle->GetLongField = (void*)Marshal.GetFunctionPointerForDelegate(GetLongFieldDelegate); + handle->GetFloatField = (void*)Marshal.GetFunctionPointerForDelegate(GetFloatFieldDelegate); + handle->GetDoubleField = (void*)Marshal.GetFunctionPointerForDelegate(GetDoubleFieldDelegate); + + handle->SetObjectField = (void*)Marshal.GetFunctionPointerForDelegate(SetObjectFieldDelegate); + handle->SetBooleanField = (void*)Marshal.GetFunctionPointerForDelegate(SetBooleanFieldDelegate); + handle->SetByteField = (void*)Marshal.GetFunctionPointerForDelegate(SetByteFieldDelegate); + handle->SetCharField = (void*)Marshal.GetFunctionPointerForDelegate(SetCharFieldDelegate); + handle->SetShortField = (void*)Marshal.GetFunctionPointerForDelegate(SetShortFieldDelegate); + handle->SetIntField = (void*)Marshal.GetFunctionPointerForDelegate(SetIntFieldDelegate); + handle->SetLongField = (void*)Marshal.GetFunctionPointerForDelegate(SetLongFieldDelegate); + handle->SetFloatField = (void*)Marshal.GetFunctionPointerForDelegate(SetFloatFieldDelegate); + handle->SetDoubleField = (void*)Marshal.GetFunctionPointerForDelegate(SetDoubleFieldDelegate); + + handle->GetStaticMethodID = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticMethodIDDelegate); + + handle->CallStaticObjectMethod = (void*)FunctionTable.Instance.JNI_CallStaticObjectMethod; + handle->CallStaticObjectMethodV = (void*)FunctionTable.Instance.JNI_CallStaticObjectMethodV; + handle->CallStaticObjectMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticObjectMethodADelegate); + + handle->CallStaticBooleanMethod = (void*)FunctionTable.Instance.JNI_CallStaticBooleanMethod; + handle->CallStaticBooleanMethodV = (void*)FunctionTable.Instance.JNI_CallStaticBooleanMethodV; + handle->CallStaticBooleanMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticBooleanMethodADelegate); + + handle->CallStaticByteMethod = (void*)FunctionTable.Instance.JNI_CallStaticByteMethod; + handle->CallStaticByteMethodV = (void*)FunctionTable.Instance.JNI_CallStaticByteMethodV; + handle->CallStaticByteMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticByteMethodADelegate); + + handle->CallStaticCharMethod = (void*)FunctionTable.Instance.JNI_CallStaticCharMethod; + handle->CallStaticCharMethodV = (void*)FunctionTable.Instance.JNI_CallStaticCharMethodV; + handle->CallStaticCharMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticCharMethodADelegate); + + handle->CallStaticShortMethod = (void*)FunctionTable.Instance.JNI_CallStaticShortMethod; + handle->CallStaticShortMethodV = (void*)FunctionTable.Instance.JNI_CallStaticShortMethodV; + handle->CallStaticShortMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticShortMethodADelegate); + + handle->CallStaticIntMethod = (void*)FunctionTable.Instance.JNI_CallStaticIntMethod; + handle->CallStaticIntMethodV = (void*)FunctionTable.Instance.JNI_CallStaticIntMethodV; + handle->CallStaticIntMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticIntMethodADelegate); + + handle->CallStaticLongMethod = (void*)FunctionTable.Instance.JNI_CallStaticLongMethod; + handle->CallStaticLongMethodV = (void*)FunctionTable.Instance.JNI_CallStaticLongMethodV; + handle->CallStaticLongMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticLongMethodADelegate); + + handle->CallStaticFloatMethod = (void*)FunctionTable.Instance.JNI_CallStaticFloatMethod; + handle->CallStaticFloatMethodV = (void*)FunctionTable.Instance.JNI_CallStaticFloatMethodV; + handle->CallStaticFloatMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticFloatMethodADelegate); + + handle->CallStaticDoubleMethod = (void*)FunctionTable.Instance.JNI_CallStaticDoubleMethod; + handle->CallStaticDoubleMethodV = (void*)FunctionTable.Instance.JNI_CallStaticDoubleMethodV; + handle->CallStaticDoubleMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticDoubleMethodADelegate); + + handle->CallStaticVoidMethod = (void*)FunctionTable.Instance.JNI_CallStaticVoidMethod; + handle->CallStaticVoidMethodV = (void*)FunctionTable.Instance.JNI_CallStaticVoidMethodV; + handle->CallStaticVoidMethodA = (void*)Marshal.GetFunctionPointerForDelegate(CallStaticVoidMethodADelegate); + + handle->GetStaticFieldID = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticFieldIDDelegate); + + handle->GetStaticObjectField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticObjectFieldDelegate); + handle->GetStaticBooleanField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticBooleanFieldDelegate); + handle->GetStaticByteField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticByteFieldDelegate); + handle->GetStaticCharField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticCharFieldDelegate); + handle->GetStaticShortField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticShortFieldDelegate); + handle->GetStaticIntField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticIntFieldDelegate); + handle->GetStaticLongField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticLongFieldDelegate); + handle->GetStaticFloatField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticFloatFieldDelegate); + handle->GetStaticDoubleField = (void*)Marshal.GetFunctionPointerForDelegate(GetStaticDoubleFieldDelegate); + + handle->SetStaticObjectField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticObjectFieldDelegate); + handle->SetStaticBooleanField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticBooleanFieldDelegate); + handle->SetStaticByteField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticByteFieldDelegate); + handle->SetStaticCharField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticCharFieldDelegate); + handle->SetStaticShortField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticShortFieldDelegate); + handle->SetStaticIntField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticIntFieldDelegate); + handle->SetStaticLongField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticLongFieldDelegate); + handle->SetStaticFloatField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticFloatFieldDelegate); + handle->SetStaticDoubleField = (void*)Marshal.GetFunctionPointerForDelegate(SetStaticDoubleFieldDelegate); + + handle->NewString = (void*)Marshal.GetFunctionPointerForDelegate(NewStringDelegate); + handle->GetStringLength = (void*)Marshal.GetFunctionPointerForDelegate(GetStringLengthDelegate); + handle->GetStringChars = (void*)Marshal.GetFunctionPointerForDelegate(GetStringCharsDelegate); + handle->ReleaseStringChars = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseStringCharsDelegate); + + handle->NewStringUTF = (void*)Marshal.GetFunctionPointerForDelegate(NewStringUTFDelegate); + handle->GetStringUTFLength = (void*)Marshal.GetFunctionPointerForDelegate(GetStringUTFLengthDelegate); + handle->GetStringUTFChars = (void*)Marshal.GetFunctionPointerForDelegate(GetStringUTFCharsDelegate); + handle->ReleaseStringUTFChars = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseStringUTFCharsDelegate); + + handle->GetArrayLength = (void*)Marshal.GetFunctionPointerForDelegate(GetArrayLengthDelegate); + + handle->NewObjectArray = (void*)Marshal.GetFunctionPointerForDelegate(NewObjectArrayDelegate); + handle->GetObjectArrayElement = (void*)Marshal.GetFunctionPointerForDelegate(GetObjectArrayElementDelegate); + handle->SetObjectArrayElement = (void*)Marshal.GetFunctionPointerForDelegate(SetObjectArrayElementDelegate); + + handle->NewBooleanArray = (void*)Marshal.GetFunctionPointerForDelegate(NewBooleanArrayDelegate); + handle->NewByteArray = (void*)Marshal.GetFunctionPointerForDelegate(NewByteArrayDelegate); + handle->NewCharArray = (void*)Marshal.GetFunctionPointerForDelegate(NewCharArrayDelegate); + handle->NewShortArray = (void*)Marshal.GetFunctionPointerForDelegate(NewShortArrayDelegate); + handle->NewIntArray = (void*)Marshal.GetFunctionPointerForDelegate(NewIntArrayDelegate); + handle->NewLongArray = (void*)Marshal.GetFunctionPointerForDelegate(NewLongArrayDelegate); + handle->NewFloatArray = (void*)Marshal.GetFunctionPointerForDelegate(NewFloatArrayDelegate); + handle->NewDoubleArray = (void*)Marshal.GetFunctionPointerForDelegate(NewDoubleArrayDelegate); + + handle->GetBooleanArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetBooleanArrayElementsDelegate); + handle->GetByteArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetByteArrayElementsDelegate); + handle->GetCharArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetCharArrayElementsDelegate); + handle->GetShortArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetShortArrayElementsDelegate); + handle->GetIntArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetIntArrayElementsDelegate); + handle->GetLongArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetLongArrayElementsDelegate); + handle->GetFloatArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetFloatArrayElementsDelegate); + handle->GetDoubleArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(GetDoubleArrayElementsDelegate); + + handle->ReleaseBooleanArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseBooleanArrayElementsDelegate); + handle->ReleaseByteArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseByteArrayElementsDelegate); + handle->ReleaseCharArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseCharArrayElementsDelegate); + handle->ReleaseShortArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseShortArrayElementsDelegate); + handle->ReleaseIntArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseIntArrayElementsDelegate); + handle->ReleaseLongArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseLongArrayElementsDelegate); + handle->ReleaseFloatArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseFloatArrayElementsDelegate); + handle->ReleaseDoubleArrayElements = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseDoubleArrayElementsDelegate); + + handle->GetBooleanArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetBooleanArrayRegionDelegate); + handle->GetByteArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetByteArrayRegionDelegate); + handle->GetCharArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetCharArrayRegionDelegate); + handle->GetShortArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetShortArrayRegionDelegate); + handle->GetIntArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetIntArrayRegionDelegate); + handle->GetLongArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetLongArrayRegionDelegate); + handle->GetFloatArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetFloatArrayRegionDelegate); + handle->GetDoubleArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetDoubleArrayRegionDelegate); + + handle->SetBooleanArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetBooleanArrayRegionDelegate); + handle->SetByteArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetByteArrayRegionDelegate); + handle->SetCharArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetCharArrayRegionDelegate); + handle->SetShortArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetShortArrayRegionDelegate); + handle->SetIntArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetIntArrayRegionDelegate); + handle->SetLongArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetLongArrayRegionDelegate); + handle->SetFloatArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetFloatArrayRegionDelegate); + handle->SetDoubleArrayRegion = (void*)Marshal.GetFunctionPointerForDelegate(SetDoubleArrayRegionDelegate); + + handle->RegisterNatives = (void*)Marshal.GetFunctionPointerForDelegate(RegisterNativesDelegate); + handle->UnregisterNatives = (void*)Marshal.GetFunctionPointerForDelegate(UnregisterNativesDelegate); + + handle->MonitorEnter = (void*)Marshal.GetFunctionPointerForDelegate(MonitorEnterDelegate); + handle->MonitorExit = (void*)Marshal.GetFunctionPointerForDelegate(MonitorExitDelegate); + + handle->GetJavaVM = (void*)Marshal.GetFunctionPointerForDelegate(GetJavaVMDelegate); + + handle->GetStringRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetStringRegionDelegate); + handle->GetStringUTFRegion = (void*)Marshal.GetFunctionPointerForDelegate(GetStringUTFRegionDelegate); + + handle->GetPrimitiveArrayCritical = (void*)Marshal.GetFunctionPointerForDelegate(GetPrimitiveArrayCriticalDelegate); + handle->ReleasePrimitiveArrayCritical = (void*)Marshal.GetFunctionPointerForDelegate(ReleasePrimitiveArrayCriticalDelegate); + + handle->GetStringCritical = (void*)Marshal.GetFunctionPointerForDelegate(GetStringCriticalDelegate); + handle->ReleaseStringCritical = (void*)Marshal.GetFunctionPointerForDelegate(ReleaseStringCriticalDelegate); + + handle->NewWeakGlobalRef = (void*)Marshal.GetFunctionPointerForDelegate(NewWeakGlobalRefDelegate); + handle->DeleteWeakGlobalRef = (void*)Marshal.GetFunctionPointerForDelegate(DeleteWeakGlobalRefDelegate); + + handle->ExceptionCheck = (void*)Marshal.GetFunctionPointerForDelegate(ExceptionCheckDelegate); + + handle->NewDirectByteBuffer = (void*)Marshal.GetFunctionPointerForDelegate(NewDirectByteBufferDelegate); + handle->GetDirectBufferAddress = (void*)Marshal.GetFunctionPointerForDelegate(GetDirectBufferAddressDelegate); + handle->GetDirectBufferCapacity = (void*)Marshal.GetFunctionPointerForDelegate(GetDirectBufferCapacityDelegate); + + handle->GetObjectRefType = (void*)Marshal.GetFunctionPointerForDelegate(GetObjectRefTypeDelegate); + } + + public void* GetMethodArgs; + public void* reserved1; + public void* reserved2; + + public void* reserved3; + public void* GetVersion; + public void* DefineClass; + public void* FindClass; + + public void* FromReflectedMethod; + public void* FromReflectedField; + + public void* ToReflectedMethod; + + public void* GetSuperclass; + public void* IsAssignableFrom; + + public void* ToReflectedField; + + public void* Throw; + public void* ThrowNew; + public void* ExceptionOccurred; + public void* ExceptionDescribe; + public void* ExceptionClear; + public void* FatalError; + + public void* PushLocalFrame; + public void* PopLocalFrame; + + public void* NewGlobalRef; + public void* DeleteGlobalRef; + public void* DeleteLocalRef; + public void* IsSameObject; + public void* NewLocalRef; + public void* EnsureLocalCapacity; + + public void* AllocObject; + public void* NewObject; + public void* NewObjectV; + public void* NewObjectA; + + public void* GetObjectClass; + public void* IsInstanceOf; + + public void* GetMethodID; + + public void* CallObjectMethod; + public void* CallObjectMethodV; + public void* CallObjectMethodA; + + public void* CallBooleanMethod; + public void* CallBooleanMethodV; + public void* CallBooleanMethodA; + + public void* CallByteMethod; + public void* CallByteMethodV; + public void* CallByteMethodA; + + public void* CallCharMethod; + public void* CallCharMethodV; + public void* CallCharMethodA; + + public void* CallShortMethod; + public void* CallShortMethodV; + public void* CallShortMethodA; + + public void* CallIntMethod; + public void* CallIntMethodV; + public void* CallIntMethodA; + + public void* CallLongMethod; + public void* CallLongMethodV; + public void* CallLongMethodA; + + public void* CallFloatMethod; + public void* CallFloatMethodV; + public void* CallFloatMethodA; + + public void* CallDoubleMethod; + public void* CallDoubleMethodV; + public void* CallDoubleMethodA; + + public void* CallVoidMethod; + public void* CallVoidMethodV; + public void* CallVoidMethodA; + + public void* CallNonvirtualObjectMethod; + public void* CallNonvirtualObjectMethodV; + public void* CallNonvirtualObjectMethodA; + + public void* CallNonvirtualBooleanMethod; + public void* CallNonvirtualBooleanMethodV; + public void* CallNonvirtualBooleanMethodA; + + public void* CallNonvirtualByteMethod; + public void* CallNonvirtualByteMethodV; + public void* CallNonvirtualByteMethodA; + + public void* CallNonvirtualCharMethod; + public void* CallNonvirtualCharMethodV; + public void* CallNonvirtualCharMethodA; + + public void* CallNonvirtualShortMethod; + public void* CallNonvirtualShortMethodV; + public void* CallNonvirtualShortMethodA; + + public void* CallNonvirtualIntMethod; + public void* CallNonvirtualIntMethodV; + public void* CallNonvirtualIntMethodA; + + public void* CallNonvirtualLongMethod; + public void* CallNonvirtualLongMethodV; + public void* CallNonvirtualLongMethodA; + + public void* CallNonvirtualFloatMethod; + public void* CallNonvirtualFloatMethodV; + public void* CallNonvirtualFloatMethodA; + + public void* CallNonvirtualDoubleMethod; + public void* CallNonvirtualDoubleMethodV; + public void* CallNonvirtualDoubleMethodA; + + public void* CallNonvirtualVoidMethod; + public void* CallNonvirtualVoidMethodV; + public void* CallNonvirtualVoidMethodA; + + public void* GetFieldID; + + public void* GetObjectField; + public void* GetBooleanField; + public void* GetByteField; + public void* GetCharField; + public void* GetShortField; + public void* GetIntField; + public void* GetLongField; + public void* GetFloatField; + public void* GetDoubleField; + + public void* SetObjectField; + public void* SetBooleanField; + public void* SetByteField; + public void* SetCharField; + public void* SetShortField; + public void* SetIntField; + public void* SetLongField; + public void* SetFloatField; + public void* SetDoubleField; + + public void* GetStaticMethodID; + + public void* CallStaticObjectMethod; + public void* CallStaticObjectMethodV; + public void* CallStaticObjectMethodA; + + public void* CallStaticBooleanMethod; + public void* CallStaticBooleanMethodV; + public void* CallStaticBooleanMethodA; + + public void* CallStaticByteMethod; + public void* CallStaticByteMethodV; + public void* CallStaticByteMethodA; + + public void* CallStaticCharMethod; + public void* CallStaticCharMethodV; + public void* CallStaticCharMethodA; + + public void* CallStaticShortMethod; + public void* CallStaticShortMethodV; + public void* CallStaticShortMethodA; + + public void* CallStaticIntMethod; + public void* CallStaticIntMethodV; + public void* CallStaticIntMethodA; + + public void* CallStaticLongMethod; + public void* CallStaticLongMethodV; + public void* CallStaticLongMethodA; + + public void* CallStaticFloatMethod; + public void* CallStaticFloatMethodV; + public void* CallStaticFloatMethodA; + + public void* CallStaticDoubleMethod; + public void* CallStaticDoubleMethodV; + public void* CallStaticDoubleMethodA; + + public void* CallStaticVoidMethod; + public void* CallStaticVoidMethodV; + public void* CallStaticVoidMethodA; + + public void* GetStaticFieldID; + public void* GetStaticObjectField; + public void* GetStaticBooleanField; + public void* GetStaticByteField; + public void* GetStaticCharField; + public void* GetStaticShortField; + public void* GetStaticIntField; + public void* GetStaticLongField; + public void* GetStaticFloatField; + public void* GetStaticDoubleField; + + public void* SetStaticObjectField; + public void* SetStaticBooleanField; + public void* SetStaticByteField; + public void* SetStaticCharField; + public void* SetStaticShortField; + public void* SetStaticIntField; + public void* SetStaticLongField; + public void* SetStaticFloatField; + public void* SetStaticDoubleField; + + public void* NewString; + public void* GetStringLength; + public void* GetStringChars; + public void* ReleaseStringChars; + + public void* NewStringUTF; + public void* GetStringUTFLength; + public void* GetStringUTFChars; + public void* ReleaseStringUTFChars; + + public void* GetArrayLength; + + public void* NewObjectArray; + public void* GetObjectArrayElement; + public void* SetObjectArrayElement; + + public void* NewBooleanArray; + public void* NewByteArray; + public void* NewCharArray; + public void* NewShortArray; + public void* NewIntArray; + public void* NewLongArray; + public void* NewFloatArray; + public void* NewDoubleArray; + + public void* GetBooleanArrayElements; + public void* GetByteArrayElements; + public void* GetCharArrayElements; + public void* GetShortArrayElements; + public void* GetIntArrayElements; + public void* GetLongArrayElements; + public void* GetFloatArrayElements; + public void* GetDoubleArrayElements; + + public void* ReleaseBooleanArrayElements; + public void* ReleaseByteArrayElements; + public void* ReleaseCharArrayElements; + public void* ReleaseShortArrayElements; + public void* ReleaseIntArrayElements; + public void* ReleaseLongArrayElements; + public void* ReleaseFloatArrayElements; + public void* ReleaseDoubleArrayElements; + + public void* GetBooleanArrayRegion; + public void* GetByteArrayRegion; + public void* GetCharArrayRegion; + public void* GetShortArrayRegion; + public void* GetIntArrayRegion; + public void* GetLongArrayRegion; + public void* GetFloatArrayRegion; + public void* GetDoubleArrayRegion; + + public void* SetBooleanArrayRegion; + public void* SetByteArrayRegion; + public void* SetCharArrayRegion; + public void* SetShortArrayRegion; + public void* SetIntArrayRegion; + public void* SetLongArrayRegion; + public void* SetFloatArrayRegion; + public void* SetDoubleArrayRegion; + + public void* RegisterNatives; + public void* UnregisterNatives; + + public void* MonitorEnter; + public void* MonitorExit; + + public void* GetJavaVM; + + public void* GetStringRegion; + public void* GetStringUTFRegion; + + public void* GetPrimitiveArrayCritical; + public void* ReleasePrimitiveArrayCritical; + + public void* GetStringCritical; + public void* ReleaseStringCritical; + + public void* NewWeakGlobalRef; + public void* DeleteWeakGlobalRef; + + public void* ExceptionCheck; + + public void* NewDirectByteBuffer; + public void* GetDirectBufferAddress; + public void* GetDirectBufferCapacity; + + public void* GetObjectRefType; + + } + +} diff --git a/src/IKVM.Runtime/JNI/JNINativeMethod.cs b/src/IKVM.Runtime/JNI/JNINativeMethod.cs new file mode 100644 index 0000000000..688034fc5f --- /dev/null +++ b/src/IKVM.Runtime/JNI/JNINativeMethod.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace IKVM.Runtime.JNI +{ + + [StructLayout(LayoutKind.Sequential)] + unsafe struct JNINativeMethod + { + + public byte* name; + public byte* signature; + void* fnPtr; + + } + +} diff --git a/src/IKVM.Runtime/JNI/JNIVM.cs b/src/IKVM.Runtime/JNI/JNIVM.cs index 8f3bff71b1..7b51580f5c 100644 --- a/src/IKVM.Runtime/JNI/JNIVM.cs +++ b/src/IKVM.Runtime/JNI/JNIVM.cs @@ -25,7 +25,7 @@ Jeroen Frijters using System.Collections.Generic; using System.Text; -using IKVM.Runtime.Text; +using IKVM.ByteCode.Text; namespace IKVM.Runtime.JNI { @@ -33,6 +33,8 @@ namespace IKVM.Runtime.JNI public sealed unsafe class JNIVM { + static readonly MUTF8Encoding MUTF8 = MUTF8Encoding.GetMUTF8(52); + internal static volatile bool jvmCreated; internal static volatile bool jvmDestroyed; internal const string METHOD_PTR_FIELD_PREFIX = "__"; @@ -60,7 +62,7 @@ internal static bool IsSupportedJniVersion(int version) /// static string DecodePlatformString(byte* psz) { - var p = psz is not null ? MUTF8Encoding.IndexOfNull(psz) : -1; + var p = psz is not null ? MUTF8.IndexOfNull(psz) : -1; return p < 0 ? null : platformEncoding.GetString(psz, p); } @@ -109,8 +111,10 @@ public static int CreateJavaVM(void* p_vm, void* p_env, void* vm_args) } } - // initialize the JVM - IKVM.Java.Externs.java.lang.VMSystemProperties.ImportProperties = properties; + // initialize the JVM properties + foreach (var kvp in properties) + JVM.Properties.User[kvp.Key] = kvp.Value; + java.lang.Thread.currentThread(); *(void**)p_vm = JavaVM.pJavaVM; diff --git a/src/IKVM.Runtime/JNI/JavaVM.cs b/src/IKVM.Runtime/JNI/JavaVM.cs index b4f5a5dbab..6562af8654 100644 --- a/src/IKVM.Runtime/JNI/JavaVM.cs +++ b/src/IKVM.Runtime/JNI/JavaVM.cs @@ -24,7 +24,7 @@ Jeroen Frijters using System; using System.Runtime.InteropServices; -using IKVM.Runtime.Text; +using IKVM.ByteCode.Text; namespace IKVM.Runtime.JNI { @@ -35,6 +35,8 @@ namespace IKVM.Runtime.JNI unsafe struct JavaVM { + static readonly MUTF8Encoding MUTF8 = MUTF8Encoding.GetMUTF8(52); + internal static JavaVM* pJavaVM; void** vtable; void* firstVtableEntry; @@ -112,11 +114,11 @@ internal static jint AttachCurrentThreadImpl(JavaVM* pJVM, void** penv, JavaVMAt { try { - var l = MUTF8Encoding.IndexOfNull(pAttachArgs->name); + var l = MUTF8.IndexOfNull(pAttachArgs->name); if (l < 0) return JNIEnv.JNI_ERR; - System.Threading.Thread.CurrentThread.Name = MUTF8Encoding.MUTF8.GetString(pAttachArgs->name, l); + System.Threading.Thread.CurrentThread.Name = MUTF8.GetString(pAttachArgs->name, l); } catch (InvalidOperationException) { diff --git a/src/IKVM.Runtime/JNI/Trampolines/FunctionTable.cs b/src/IKVM.Runtime/JNI/Trampolines/FunctionTable.cs new file mode 100644 index 0000000000..14f8e57f65 --- /dev/null +++ b/src/IKVM.Runtime/JNI/Trampolines/FunctionTable.cs @@ -0,0 +1,357 @@ +using System; +using System.Runtime.InteropServices; + +using IKVM.Runtime.JNI.Memory; + +namespace IKVM.Runtime.JNI.Trampolines +{ + + /// + /// Maintains a set of LLIR function pointers. + /// + abstract class FunctionTable + { + + static FunctionTable instance; + + /// + /// Gets the instance of the function table based on the current platform. + /// + /// + public static FunctionTable Instance => instance ??= CreateInstance(); + + /// + /// Gets the instance of the function table based on the current platform. + /// + /// + static FunctionTable CreateInstance() + { +#if FIRST_PASS + return null; +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X86) + return new FunctionTable_win7_x86(); + else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + return new FunctionTable_win7_x64(); + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + return new FunctionTable_win81_arm(); + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + return new FunctionTable_win10_arm64(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + return new FunctionTable_linux_x64(); + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + return new FunctionTable_linux_arm(); + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + return new FunctionTable_linux_arm64(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + return new FunctionTable_osx_x64(); + } + + throw new PlatformNotSupportedException(); +#endif + } + + /// + /// Allocates the given function code in executable memory and returns a reference. + /// + /// + /// + static ExecutableMemory AllocateMemory(byte[] code) + { + if (code is null) + throw new ArgumentNullException(nameof(code)); + + var m = ExecutableMemory.Allocate(code.Length); + Marshal.Copy(code, 0, m.DangerousGetHandle(), m.Size); + return m; + } + + readonly ExecutableMemory memory; + + /// + /// Initializes a new instance. + /// + public FunctionTable() + { + memory = AllocateMemory(Text); + } + + /// + /// Gets a pointer to the function at teh specified offset. + /// + /// + /// + IntPtr GetFunctionPointerFromOffset(int offset) => memory.DangerousGetHandle() + offset; + + #region Function Pointers + + public IntPtr JNI_NewObject => GetFunctionPointerFromOffset(idx_JNI_NewObject); + + public IntPtr JNI_NewObjectV => GetFunctionPointerFromOffset(idx_JNI_NewObjectV); + + public IntPtr JNI_CallObjectMethod => GetFunctionPointerFromOffset(idx_JNI_CallObjectMethod); + + public IntPtr JNI_CallObjectMethodV => GetFunctionPointerFromOffset(idx_JNI_CallObjectMethodV); + + public IntPtr JNI_CallNonvirtualObjectMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualObjectMethod); + + public IntPtr JNI_CallNonvirtualObjectMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualObjectMethodV); + + public IntPtr JNI_CallStaticObjectMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticObjectMethod); + + public IntPtr JNI_CallStaticObjectMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticObjectMethodV); + + public IntPtr JNI_CallBooleanMethod => GetFunctionPointerFromOffset(idx_JNI_CallBooleanMethod); + + public IntPtr JNI_CallBooleanMethodV => GetFunctionPointerFromOffset(idx_JNI_CallBooleanMethodV); + + public IntPtr JNI_CallNonvirtualBooleanMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualBooleanMethod); + + public IntPtr JNI_CallNonvirtualBooleanMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualBooleanMethodV); + + public IntPtr JNI_CallStaticBooleanMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticBooleanMethod); + + public IntPtr JNI_CallStaticBooleanMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticBooleanMethodV); + + public IntPtr JNI_CallByteMethod => GetFunctionPointerFromOffset(idx_JNI_CallByteMethod); + + public IntPtr JNI_CallByteMethodV => GetFunctionPointerFromOffset(idx_JNI_CallByteMethodV); + + public IntPtr JNI_CallNonvirtualByteMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualByteMethod); + + public IntPtr JNI_CallNonvirtualByteMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualByteMethodV); + + public IntPtr JNI_CallStaticByteMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticByteMethod); + + public IntPtr JNI_CallStaticByteMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticByteMethodV); + + public IntPtr JNI_CallCharMethod => GetFunctionPointerFromOffset(idx_JNI_CallCharMethod); + + public IntPtr JNI_CallCharMethodV => GetFunctionPointerFromOffset(idx_JNI_CallCharMethodV); + + public IntPtr JNI_CallNonvirtualCharMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualCharMethod); + + public IntPtr JNI_CallNonvirtualCharMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualCharMethodV); + + public IntPtr JNI_CallStaticCharMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticCharMethod); + + public IntPtr JNI_CallStaticCharMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticCharMethodV); + + public IntPtr JNI_CallShortMethod => GetFunctionPointerFromOffset(idx_JNI_CallShortMethod); + + public IntPtr JNI_CallShortMethodV => GetFunctionPointerFromOffset(idx_JNI_CallShortMethodV); + + public IntPtr JNI_CallNonvirtualShortMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualShortMethod); + + public IntPtr JNI_CallNonvirtualShortMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualShortMethodV); + + public IntPtr JNI_CallStaticShortMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticShortMethod); + + public IntPtr JNI_CallStaticShortMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticShortMethodV); + + public IntPtr JNI_CallIntMethod => GetFunctionPointerFromOffset(idx_JNI_CallIntMethod); + + public IntPtr JNI_CallIntMethodV => GetFunctionPointerFromOffset(idx_JNI_CallIntMethodV); + + public IntPtr JNI_CallNonvirtualIntMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualIntMethod); + + public IntPtr JNI_CallNonvirtualIntMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualIntMethodV); + + public IntPtr JNI_CallStaticIntMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticIntMethod); + + public IntPtr JNI_CallStaticIntMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticIntMethodV); + + public IntPtr JNI_CallLongMethod => GetFunctionPointerFromOffset(idx_JNI_CallLongMethod); + + public IntPtr JNI_CallLongMethodV => GetFunctionPointerFromOffset(idx_JNI_CallLongMethodV); + + public IntPtr JNI_CallNonvirtualLongMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualLongMethod); + + public IntPtr JNI_CallNonvirtualLongMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualLongMethodV); + + public IntPtr JNI_CallStaticLongMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticLongMethod); + + public IntPtr JNI_CallStaticLongMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticLongMethodV); + + public IntPtr JNI_CallFloatMethod => GetFunctionPointerFromOffset(idx_JNI_CallFloatMethod); + + public IntPtr JNI_CallFloatMethodV => GetFunctionPointerFromOffset(idx_JNI_CallFloatMethodV); + + public IntPtr JNI_CallNonvirtualFloatMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualFloatMethod); + + public IntPtr JNI_CallNonvirtualFloatMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualFloatMethodV); + + public IntPtr JNI_CallStaticFloatMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticFloatMethod); + + public IntPtr JNI_CallStaticFloatMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticFloatMethodV); + + public IntPtr JNI_CallDoubleMethod => GetFunctionPointerFromOffset(idx_JNI_CallDoubleMethod); + + public IntPtr JNI_CallDoubleMethodV => GetFunctionPointerFromOffset(idx_JNI_CallDoubleMethodV); + + public IntPtr JNI_CallNonvirtualDoubleMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualDoubleMethod); + + public IntPtr JNI_CallNonvirtualDoubleMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualDoubleMethodV); + + public IntPtr JNI_CallStaticDoubleMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticDoubleMethod); + + public IntPtr JNI_CallStaticDoubleMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticDoubleMethodV); + + public IntPtr JNI_CallVoidMethod => GetFunctionPointerFromOffset(idx_JNI_CallVoidMethod); + + public IntPtr JNI_CallVoidMethodV => GetFunctionPointerFromOffset(idx_JNI_CallVoidMethodV); + + public IntPtr JNI_CallNonvirtualVoidMethod => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualVoidMethod); + + public IntPtr JNI_CallNonvirtualVoidMethodV => GetFunctionPointerFromOffset(idx_JNI_CallNonvirtualVoidMethodV); + + public IntPtr JNI_CallStaticVoidMethod => GetFunctionPointerFromOffset(idx_JNI_CallStaticVoidMethod); + + public IntPtr JNI_CallStaticVoidMethodV => GetFunctionPointerFromOffset(idx_JNI_CallStaticVoidMethodV); + + #endregion + + /// + /// Overridden to supply the LLIR generated .text for the platform. + /// + protected abstract byte[] Text { get; } + + #region Function Indexes + + protected abstract int idx_JNI_CallObjectMethod { get; } + + protected abstract int idx_JNI_CallObjectMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualObjectMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualObjectMethodV { get; } + + protected abstract int idx_JNI_CallStaticObjectMethod { get; } + + protected abstract int idx_JNI_CallStaticObjectMethodV { get; } + + protected abstract int idx_JNI_CallBooleanMethod { get; } + + protected abstract int idx_JNI_CallBooleanMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualBooleanMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualBooleanMethodV { get; } + + protected abstract int idx_JNI_CallStaticBooleanMethod { get; } + + protected abstract int idx_JNI_CallStaticBooleanMethodV { get; } + + protected abstract int idx_JNI_CallByteMethod { get; } + + protected abstract int idx_JNI_CallByteMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualByteMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualByteMethodV { get; } + + protected abstract int idx_JNI_CallStaticByteMethod { get; } + + protected abstract int idx_JNI_CallStaticByteMethodV { get; } + + protected abstract int idx_JNI_CallCharMethod { get; } + + protected abstract int idx_JNI_CallCharMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualCharMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualCharMethodV { get; } + + protected abstract int idx_JNI_CallStaticCharMethod { get; } + + protected abstract int idx_JNI_CallStaticCharMethodV { get; } + + protected abstract int idx_JNI_CallShortMethod { get; } + + protected abstract int idx_JNI_CallShortMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualShortMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualShortMethodV { get; } + + protected abstract int idx_JNI_CallStaticShortMethod { get; } + + protected abstract int idx_JNI_CallStaticShortMethodV { get; } + + protected abstract int idx_JNI_CallIntMethod { get; } + + protected abstract int idx_JNI_CallIntMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualIntMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualIntMethodV { get; } + + protected abstract int idx_JNI_CallStaticIntMethod { get; } + + protected abstract int idx_JNI_CallStaticIntMethodV { get; } + + protected abstract int idx_JNI_CallLongMethod { get; } + + protected abstract int idx_JNI_CallLongMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualLongMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualLongMethodV { get; } + + protected abstract int idx_JNI_CallStaticLongMethod { get; } + + protected abstract int idx_JNI_CallStaticLongMethodV { get; } + + protected abstract int idx_JNI_CallFloatMethod { get; } + + protected abstract int idx_JNI_CallFloatMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualFloatMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualFloatMethodV { get; } + + protected abstract int idx_JNI_CallStaticFloatMethod { get; } + + protected abstract int idx_JNI_CallStaticFloatMethodV { get; } + + protected abstract int idx_JNI_CallDoubleMethod { get; } + + protected abstract int idx_JNI_CallDoubleMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualDoubleMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualDoubleMethodV { get; } + + protected abstract int idx_JNI_CallStaticDoubleMethod { get; } + + protected abstract int idx_JNI_CallStaticDoubleMethodV { get; } + + protected abstract int idx_JNI_NewObject { get; } + + protected abstract int idx_JNI_NewObjectV { get; } + + protected abstract int idx_JNI_CallVoidMethod { get; } + + protected abstract int idx_JNI_CallVoidMethodV { get; } + + protected abstract int idx_JNI_CallNonvirtualVoidMethod { get; } + + protected abstract int idx_JNI_CallNonvirtualVoidMethodV { get; } + + protected abstract int idx_JNI_CallStaticVoidMethod { get; } + + protected abstract int idx_JNI_CallStaticVoidMethodV { get; } + + #endregion + + } + +} diff --git a/src/IKVM.Runtime/JNI/Trampolines/README.md b/src/IKVM.Runtime/JNI/Trampolines/README.md new file mode 100644 index 0000000000..2044b045ff --- /dev/null +++ b/src/IKVM.Runtime/JNI/Trampolines/README.md @@ -0,0 +1,7 @@ +The .c files are built to .o files. The .o files are then disassembled to generate .s files. The .s files are then parsed to generate .cs files that declare the function bodies in source. + +At runtime these can be loaded into executable memory and invoked. + +LLIR code must be PIC without external references. + +Entire .text region is mapped into memory, and the offsets to the symbols are converted to pointers in memory. No relocation is done. diff --git a/src/IKVM.Runtime/JNI/Trampolines/ikvm.c b/src/IKVM.Runtime/JNI/Trampolines/ikvm.c new file mode 100644 index 0000000000..bb639c25ed --- /dev/null +++ b/src/IKVM.Runtime/JNI/Trampolines/ikvm.c @@ -0,0 +1,72 @@ +#include "ikvm.h" + +MAKE_METHOD(Object, jobject) +MAKE_METHOD(Boolean, jboolean) +MAKE_METHOD(Byte, jbyte) +MAKE_METHOD(Char, jchar) +MAKE_METHOD(Short, jshort) +MAKE_METHOD(Int, jint) +MAKE_METHOD(Long, jlong) +MAKE_METHOD(Float, jfloat) +MAKE_METHOD(Double, jdouble) + +JNIEXPORT jobject JNICALL JNI_NewObject(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + MAKE_ARG_ARRAY(pEnv, args); + jobject o = (*pEnv)->NewObjectA(pEnv, clazz, methodID, argv); + va_end(args); + return o; +} + +JNIEXPORT jobject JNICALL JNI_NewObjectV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args) +{ + MAKE_ARG_ARRAY(pEnv, args); + return (*pEnv)->NewObjectA(pEnv, clazz, methodID, argv); +} + +JNIEXPORT void JNICALL JNI_CallVoidMethod(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallVoidMethodA(pEnv, obj, methodID, argv); + va_end(args); +} + +JNIEXPORT void JNICALL JNI_CallVoidMethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args) +{ + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallVoidMethodA(pEnv, obj, methodID, argv); +} + +JNIEXPORT void JNICALL JNI_CallNonvirtualVoidMethod(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallNonvirtualVoidMethodA(pEnv, obj, clazz, methodID, argv); + va_end(args); +} + +JNIEXPORT void JNICALL JNI_CallNonvirtualVoidMethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args) +{ + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallNonvirtualVoidMethodA(pEnv, obj, clazz, methodID, argv); +} + +JNIEXPORT void JNICALL JNI_CallStaticVoidMethod(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallStaticVoidMethodA(pEnv, clazz, methodID, argv); + va_end(args); +} + +JNIEXPORT void JNICALL JNI_CallStaticVoidMethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args) +{ + MAKE_ARG_ARRAY(pEnv, args); + (*pEnv)->CallStaticVoidMethodA(pEnv, clazz, methodID, argv); +} diff --git a/src/IKVM.Runtime/JNI/Trampolines/ikvm.h b/src/IKVM.Runtime/JNI/Trampolines/ikvm.h new file mode 100644 index 0000000000..a4c2e0e699 --- /dev/null +++ b/src/IKVM.Runtime/JNI/Trampolines/ikvm.h @@ -0,0 +1,108 @@ +#ifndef IKVM_H_INCLUDED +#define IKVM_H_INCLUDED + +#include +#include + +typedef int (*GetMethodArgs_t)(JNIEnv* pEnv, jmethodID method, char* sig); +#define GET_METHOD_ARGS(pEnv, method, sig) (((GetMethodArgs_t)((*pEnv)->reserved0))(pEnv, methodID, sig)) + +#define MAKE_ARG_ARRAY(pEnv, args) \ + char sig[256];\ + jvalue argv[256];\ + int argc = GET_METHOD_ARGS(pEnv, methodID, sig);\ + for (int i = 0; i < argc; i++)\ + {\ + if (sig[i] == 'Z')\ + argv[i].z = (jboolean)va_arg(args, int);\ + else if (sig[i] == 'B')\ + argv[i].b = (jbyte)va_arg(args, int);\ + else if (sig[i] == 'C')\ + argv[i].c = (jchar)va_arg(args, int);\ + else if (sig[i] == 'S')\ + argv[i].s = (jshort)va_arg(args, int);\ + else if (sig[i] == 'I')\ + argv[i].i = (jint)va_arg(args, int);\ + else if (sig[i] == 'J')\ + argv[i].j = (jlong)va_arg(args, long);\ + else if (sig[i] == 'F')\ + argv[i].f = (jfloat)va_arg(args, double);\ + else if (sig[i] == 'D')\ + argv[i].d = (jdouble)va_arg(args, double);\ + else if (sig[i] == 'L')\ + argv[i].l = (jobject)va_arg(args, void*);\ + } + + +#define MAKE_METHOD_SIGNATURE(Type, type) \ +JNIEXPORT type JNICALL JNI_Call##Type##Method(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...);\ +JNIEXPORT type JNICALL JNI_Call##Type##MethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args);\ +JNIEXPORT type JNICALL JNI_CallNonvirtual##Type##Method(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...);\ +JNIEXPORT type JNICALL JNI_CallNonvirtual##Type##MethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args);\ +JNIEXPORT type JNICALL JNI_CallStatic##Type##Method(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...);\ +JNIEXPORT type JNICALL JNI_CallStatic##Type##MethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args); + +MAKE_METHOD_SIGNATURE(Object, jobject) +MAKE_METHOD_SIGNATURE(Boolean, jboolean) +MAKE_METHOD_SIGNATURE(Byte, jbyte) +MAKE_METHOD_SIGNATURE(Char, jchar) +MAKE_METHOD_SIGNATURE(Short, jshort) +MAKE_METHOD_SIGNATURE(Int, jint) +MAKE_METHOD_SIGNATURE(Long, jlong) +MAKE_METHOD_SIGNATURE(Float, jfloat) +MAKE_METHOD_SIGNATURE(Double, jdouble) + +#define MAKE_METHOD(Type, type) \ +JNIEXPORT type JNICALL JNI_Call##Type##Method(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...)\ +{\ + va_list args;\ + va_start(args, methodID);\ + MAKE_ARG_ARRAY(pEnv, args);\ + type ret = (*pEnv)->Call##Type##MethodA(pEnv, obj, methodID, argv);\ + va_end(args);\ + return ret;\ +}\ +JNIEXPORT type JNICALL JNI_Call##Type##MethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args)\ +{\ + MAKE_ARG_ARRAY(pEnv, args);\ + return (*pEnv)->Call##Type##MethodA(pEnv, obj, methodID, argv);\ +}\ +JNIEXPORT type JNICALL JNI_CallNonvirtual##Type##Method(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...)\ +{\ + va_list args;\ + va_start(args, methodID);\ + MAKE_ARG_ARRAY(pEnv, args);\ + type ret = (*pEnv)->CallNonvirtual##Type##MethodA(pEnv, obj, clazz, methodID, argv);\ + va_end(args);\ + return ret;\ +}\ +JNIEXPORT type JNICALL JNI_CallNonvirtual##Type##MethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args)\ +{\ + MAKE_ARG_ARRAY(pEnv, args);\ + return (*pEnv)->CallNonvirtual##Type##MethodA(pEnv, obj, clazz, methodID, argv);\ +}\ +JNIEXPORT type JNICALL JNI_CallStatic##Type##Method(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...)\ +{\ + va_list args;\ + va_start(args, methodID);\ + MAKE_ARG_ARRAY(pEnv, args);\ + type ret = (*pEnv)->CallStatic##Type##MethodA(pEnv, clazz, methodID, argv);\ + va_end(args);\ + return ret;\ +}\ +JNIEXPORT type JNICALL JNI_CallStatic##Type##MethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args)\ +{\ + MAKE_ARG_ARRAY(pEnv, args);\ + return (*pEnv)->CallStatic##Type##MethodA(pEnv, clazz, methodID, argv);\ +} + +JNIEXPORT jobject JNICALL JNI_NewObject(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...); +JNIEXPORT jobject JNICALL JNI_NewObjectV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args); +JNIEXPORT void JNICALL JNI_CallVoidMethod(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...); +JNIEXPORT void JNICALL JNI_CallVoidMethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args); +JNIEXPORT void JNICALL JNI_CallNonvirtualVoidMethod(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...); +JNIEXPORT void JNICALL JNI_CallNonvirtualVoidMethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args); +JNIEXPORT void JNICALL JNI_CallStaticVoidMethod(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...); +JNIEXPORT void JNICALL JNI_CallStaticVoidMethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args); + +#endif diff --git a/src/IKVM.Runtime/JNI/Trampolines/include/stdio.h b/src/IKVM.Runtime/JNI/Trampolines/include/stdio.h new file mode 100644 index 0000000000..a588ab4cc2 --- /dev/null +++ b/src/IKVM.Runtime/JNI/Trampolines/include/stdio.h @@ -0,0 +1 @@ +// fake header, jni doesn't actually use anything in stdio \ No newline at end of file diff --git a/src/IKVM.Runtime/JNI/VtableBuilder.cs b/src/IKVM.Runtime/JNI/VtableBuilder.cs deleted file mode 100644 index 93871f3dcf..0000000000 --- a/src/IKVM.Runtime/JNI/VtableBuilder.cs +++ /dev/null @@ -1,456 +0,0 @@ -/* - Copyright (C) 2002-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; -using System.Runtime.InteropServices; - -namespace IKVM.Runtime.JNI -{ - - using jboolean = System.SByte; - using jbyte = System.SByte; - using jchar = System.UInt16; - using jdouble = System.Double; - using jfloat = System.Single; - using jint = System.Int32; - using jlong = System.Int64; - using jshort = System.Int16; - - /// - /// Populates the JNIEnv table of methods with implementations that call back into managed code. - /// - unsafe class VtableBuilder - { - - delegate int GetMethodArgs(JNIEnv* pEnv, nint p1, byte* p2); - - delegate int pf_int_nint(JNIEnv* pEnv, nint p); - delegate nint pf_nint_nint(JNIEnv* pEnv, nint p); - delegate void pf_void_nint(JNIEnv* pEnv, nint p); - delegate nint pf_nint(JNIEnv* pEnv); - delegate void pf_void(JNIEnv* pEnv); - delegate sbyte pf_sbyte(JNIEnv* pEnv); - delegate nint pf_nint_pbyte(JNIEnv* pEnv, byte* p); - delegate int pf_int(JNIEnv* pEnv); - delegate nint pf_nint_pbyte_nint_psbyte_nint(JNIEnv* pEnv, byte* p1, nint p2, sbyte* p3, int p4); - delegate nint pf_nint_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate jchar* pf_pjchar_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate void pf_void_nint_pvoid_int(JNIEnv* pEnv, nint p1, void* p2, int p3); - delegate void* pf_pvoid_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate int pf_int_nint_pbyte(JNIEnv* pEnv, nint p1, byte* p2); - delegate void pf_void_pbyte(JNIEnv* pEnv, byte* p1); - delegate nint pf_nint_nint_pbyte_pbyte(JNIEnv* pEnv, nint p1, byte* p2, byte* p3); - delegate int pf_int_nint_pJNINativeMethod_int(JNIEnv* pEnv, nint p1, JNIEnv.JNINativeMethod* p2, int p3); - delegate int pf_int_ppJavaVM(JNIEnv* pEnv, JavaVM** ppJavaVM); - delegate sbyte pf_sbyte_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate short pf_short_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate ushort pf_ushort_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate int pf_int_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate long pf_long_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate float pf_float_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate double pf_double_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate void pf_void_nint_nint_nint(JNIEnv* pEnv, nint p1, nint p2, nint p3); - delegate void pf_void_nint_nint_sbyte(JNIEnv* pEnv, nint p1, nint p2, sbyte p3); - delegate void pf_void_nint_nint_short(JNIEnv* pEnv, nint p1, nint p2, short p3); - delegate void pf_void_nint_nint_ushort(JNIEnv* pEnv, nint p1, nint p2, ushort p3); - delegate void pf_void_nint_nint_int(JNIEnv* pEnv, nint p1, nint p2, int p3); - delegate void pf_void_nint_nint_long(JNIEnv* pEnv, nint p1, nint p2, long p3); - delegate void pf_void_nint_nint_float(JNIEnv* pEnv, nint p1, nint p2, float p3); - delegate void pf_void_nint_nint_double(JNIEnv* pEnv, nint p1, nint p2, double p3); - delegate nint pf_nint_pjchar_int(JNIEnv* pEnv, jchar* p1, int p2); - delegate void pf_void_nint_nint(JNIEnv* pEnv, nint p1, nint p2); - delegate void pf_void_nint_pjchar(JNIEnv* pEnv, nint p1, jchar* p2); - delegate nint pf_nint_int_nint_nint(JNIEnv* pEnv, int p1, nint p2, nint p3); - delegate nint pf_nint_nint_int(JNIEnv* pEnv, nint p1, int p2); - delegate void pf_void_nint_int_nint(JNIEnv* pEnv, nint p1, int p2, nint p3); - delegate nint pf_nint_int(JNIEnv* pEnv, int p1); - delegate void pf_void_nint_int_int_nint(JNIEnv* pEnv, nint p1, int p2, int p3, nint p4); - delegate nint pf_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate sbyte pf_sbyte_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate short pf_short_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate ushort pf_ushort_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate int pf_int_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate long pf_long_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate float pf_float_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate double pf_double_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate void pf_void_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, jvalue* p3); - delegate nint pf_nint_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate sbyte pf_sbyte_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate ushort pf_ushort_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate short pf_short_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate int pf_int_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate long pf_long_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate float pf_float_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate double pf_double_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate void pf_void_nint_nint_nint_pjvalue(JNIEnv* pEnv, nint p1, nint p2, nint p3, jvalue* p4); - delegate byte* pf_pbyte_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate void pf_void_nint_pbyte(JNIEnv* pEnv, nint p1, byte* p2); - delegate jboolean* pf_pjboolean_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jbyte* pf_pjbyte_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jshort* pf_pjshort_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jint* pf_pjint_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jlong* pf_pjlong_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jfloat* pf_pjfloat_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate jdouble* pf_pjdouble_nint_pjboolean(JNIEnv* pEnv, nint p1, jboolean* p2); - delegate void pf_void_nint_pjboolean_int(JNIEnv* pEnv, nint p1, jboolean* p2, int p3); - delegate void pf_void_nint_pjbyte_int(JNIEnv* pEnv, nint p1, jbyte* p2, int p3); - delegate void pf_void_nint_pjchar_int(JNIEnv* pEnv, nint p1, jchar* p2, int p3); - delegate void pf_void_nint_pjshort_int(JNIEnv* pEnv, nint p1, jshort* p2, int p3); - delegate void pf_void_nint_pjint_int(JNIEnv* pEnv, nint p1, jint* p2, int p3); - delegate void pf_void_nint_pjlong_int(JNIEnv* pEnv, nint p1, jlong* p2, int p3); - delegate void pf_void_nint_pjfloat_int(JNIEnv* pEnv, nint p1, jfloat* p2, int p3); - delegate void pf_void_nint_pjdouble_int(JNIEnv* pEnv, nint p1, jdouble* p2, int p3); - delegate int pf_int_int(JNIEnv* pEnv, int p1); - delegate nint pf_nint_nint_long(JNIEnv* pEnv, nint p1, long p2); - delegate long pf_long_nint(JNIEnv* pEnv, nint p1); - delegate nint pf_nint_nint_nint_sbyte(JNIEnv* pEnv, nint p1, nint p2, sbyte p3); - - internal static void* vtable; - - /// - /// Initializes the static instance. - /// - static VtableBuilder() - { - JNIVM.jvmCreated = true; - - // native library provides an initial template with functions for variadic implementations - var pmcpp = Native.ikvm_GetJNIEnvVTable(); - var p = (void**)JNIMemory.Alloc(sizeof(nint) * delegates.Length); - for (int i = 0; i < delegates.Length; i++) - p[i] = delegates[i] != null ? (void*)Marshal.GetFunctionPointerForDelegate(delegates[i]) : pmcpp[i]; - - vtable = p; - } - - /// - /// Set of delegates for the JNIEnv method table. - /// - static Delegate[] delegates = - { - new GetMethodArgs(JNIEnv.GetMethodArgs), - null, - null, - null, - - new pf_int(JNIEnv.GetVersion), //virtual jint JNICALL GetVersion(); - - new pf_nint_pbyte_nint_psbyte_nint(JNIEnv.DefineClass), //virtual jclass JNICALL DefineClass(const char *name, jobject loader, const jbyte *buf, jsize len); - new pf_nint_pbyte(JNIEnv.FindClass), //virtual jclass JNICALL FindClass(const char *name); - - new pf_nint_nint(JNIEnv.FromReflectedMethod), //virtual jmethodID JNICALL FromReflectedMethod(jobject method); - new pf_nint_nint(JNIEnv.FromReflectedField), //virtual jfieldID JNICALL FromReflectedField(jobject field); - new pf_nint_nint_nint_sbyte(JNIEnv.ToReflectedMethod), //virtual jobject JNICALL ToReflectedMethod(jclass clazz, jmethodID methodID, jboolean isStatic); - - new pf_nint_nint(JNIEnv.GetSuperclass), //virtual jclass JNICALL GetSuperclass(jclass sub); - new pf_sbyte_nint_nint(JNIEnv.IsAssignableFrom), //virtual jboolean JNICALL IsAssignableFrom(jclass sub, jclass sup); - - new pf_nint_nint_nint_sbyte(JNIEnv.ToReflectedField), //virtual jobject JNICALL ToReflectedField(jclass clazz, jfieldID fieldID, jboolean isStatic); - - new pf_int_nint(JNIEnv.Throw), //virtual jint JNICALL Throw(jthrowable obj); - new pf_int_nint_pbyte(JNIEnv.ThrowNew), //virtual jint JNICALL ThrowNew(jclass clazz, const char *msg); - new pf_nint(JNIEnv.ExceptionOccurred), //virtual jthrowable JNICALL ExceptionOccurred(); - new pf_void(JNIEnv.ExceptionDescribe), //virtual void JNICALL ExceptionDescribe(); - new pf_void(JNIEnv.ExceptionClear), //virtual void JNICALL ExceptionClear(); - new pf_void_pbyte(JNIEnv.FatalError), //virtual void JNICALL FatalError(const char *msg); - - new pf_int_int(JNIEnv.PushLocalFrame), //virtual jint JNICALL PushLocalFrame(jint capacity); - new pf_nint_nint(JNIEnv.PopLocalFrame), //virtual jobject JNICALL PopLocalFrame(jobject result); - - new pf_nint_nint(JNIEnv.NewGlobalRef), //virtual jobject JNICALL NewGlobalRef(jobject lobj); - new pf_void_nint(JNIEnv.DeleteGlobalRef), //virtual void JNICALL DeleteGlobalRef(jobject gref); - new pf_void_nint(JNIEnv.DeleteLocalRef), //virtual void JNICALL DeleteLocalRef(jobject obj); - new pf_sbyte_nint_nint(JNIEnv.IsSameObject), //virtual jboolean JNICALL IsSameObject(jobject obj1, jobject obj2); - - new pf_nint_nint(JNIEnv.NewLocalRef), //virtual jobject JNICALL NewLocalRef(jobject ref); - new pf_int_int(JNIEnv.EnsureLocalCapacity), //virtual jint JNICALL EnsureLocalCapacity(jint capacity); - - new pf_nint_nint(JNIEnv.AllocObject), //virtual jobject JNICALL AllocObject(jclass clazz); - null, //virtual jobject JNICALL NewObject(jclass clazz, jmethodID methodID, ...); - null, //virtual jobject JNICALL NewObjectV(jclass clazz, jmethodID methodID, va_list args); - new pf_nint_nint_nint_pjvalue(JNIEnv.NewObjectA), //virtual jobject JNICALL NewObjectA(jclass clazz, jmethodID methodID, jvalue *args); - - new pf_nint_nint(JNIEnv.GetObjectClass), //virtual jclass JNICALL GetObjectClass(jobject obj); - new pf_sbyte_nint_nint(JNIEnv.IsInstanceOf), //virtual jboolean JNICALL IsInstanceOf(jobject obj, jclass clazz); - - new pf_nint_nint_pbyte_pbyte(JNIEnv.GetMethodID), //virtual jmethodID JNICALL GetMethodID(jclass clazz, const char *name, const char *sig); - - null, //virtual jobject JNICALL CallObjectMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jobject JNICALL CallObjectMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_nint_nint_nint_pjvalue(JNIEnv.CallObjectMethodA), //virtual jobject JNICALL CallObjectMethodA(jobject obj, jmethodID methodID, jvalue * args); - - null, //virtual jboolean JNICALL CallBooleanMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jboolean JNICALL CallBooleanMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_pjvalue(JNIEnv.CallBooleanMethodA), //virtual jboolean JNICALL CallBooleanMethodA(jobject obj, jmethodID methodID, jvalue * args); - - null, //virtual jbyte JNICALL CallByteMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jbyte JNICALL CallByteMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_pjvalue(JNIEnv.CallByteMethodA), //virtual jbyte JNICALL CallByteMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jchar JNICALL CallCharMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jchar JNICALL CallCharMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_ushort_nint_nint_pjvalue(JNIEnv.CallCharMethodA), //virtual jchar JNICALL CallCharMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jshort JNICALL CallShortMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jshort JNICALL CallShortMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_short_nint_nint_pjvalue(JNIEnv.CallShortMethodA), //virtual jshort JNICALL CallShortMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jint JNICALL CallIntMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jint JNICALL CallIntMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_int_nint_nint_pjvalue(JNIEnv.CallIntMethodA), //virtual jint JNICALL CallIntMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jlong JNICALL CallLongMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jlong JNICALL CallLongMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_long_nint_nint_pjvalue(JNIEnv.CallLongMethodA), //virtual jlong JNICALL CallLongMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jfloat JNICALL CallFloatMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jfloat JNICALL CallFloatMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_float_nint_nint_pjvalue(JNIEnv.CallFloatMethodA), //virtual jfloat JNICALL CallFloatMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual jdouble JNICALL CallDoubleMethod(jobject obj, jmethodID methodID, ...); - null, //virtual jdouble JNICALL CallDoubleMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_double_nint_nint_pjvalue(JNIEnv.CallDoubleMethodA), //virtual jdouble JNICALL CallDoubleMethodA(jobject obj, jmethodID methodID, jvalue *args); - - null, //virtual void JNICALL CallVoidMethod(jobject obj, jmethodID methodID, ...); - null, //virtual void JNICALL CallVoidMethodV(jobject obj, jmethodID methodID, va_list args); - new pf_void_nint_nint_pjvalue(JNIEnv.CallVoidMethodA), //virtual void JNICALL CallVoidMethodA(jobject obj, jmethodID methodID, jvalue * args); - - null, //virtual jobject JNICALL CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jobject JNICALL CallNonvirtualObjectMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_nint_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualObjectMethodA), //virtual jobject JNICALL CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - null, //virtual jboolean JNICALL CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jboolean JNICALL CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualBooleanMethodA), //virtual jboolean JNICALL CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - null, //virtual jbyte JNICALL CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jbyte JNICALL CallNonvirtualByteMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualByteMethodA), //virtual jbyte JNICALL CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jchar JNICALL CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jchar JNICALL CallNonvirtualCharMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_ushort_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualCharMethodA), //virtual jchar JNICALL CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jshort JNICALL CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jshort JNICALL CallNonvirtualShortMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_short_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualShortMethodA), //virtual jshort JNICALL CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jint JNICALL CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jint JNICALL CallNonvirtualIntMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_int_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualIntMethodA), //virtual jint JNICALL CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jlong JNICALL CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jlong JNICALL CallNonvirtualLongMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_long_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualLongMethodA), //virtual jlong JNICALL CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jfloat JNICALL CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jfloat JNICALL CallNonvirtualFloatMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_float_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualFloatMethodA), //virtual jfloat JNICALL CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jdouble JNICALL CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual jdouble JNICALL CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_double_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualDoubleMethodA), //virtual jdouble JNICALL CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual void JNICALL CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - null, //virtual void JNICALL CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - new pf_void_nint_nint_nint_pjvalue(JNIEnv.CallNonvirtualVoidMethodA), //virtual void JNICALL CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - new pf_nint_nint_pbyte_pbyte(JNIEnv.GetFieldID), //virtual jfieldID JNICALL GetFieldID(jclass clazz, const char *name, const char *sig); - - new pf_nint_nint_nint(JNIEnv.GetObjectField), //virtual jobject JNICALL GetObjectField(jobject obj, jfieldID fieldID); - new pf_sbyte_nint_nint(JNIEnv.GetBooleanField), //virtual jboolean JNICALL GetBooleanField(jobject obj, jfieldID fieldID); - new pf_sbyte_nint_nint(JNIEnv.GetByteField), //virtual jbyte JNICALL GetByteField(jobject obj, jfieldID fieldID); - new pf_ushort_nint_nint(JNIEnv.GetCharField), //virtual jchar JNICALL GetCharField(jobject obj, jfieldID fieldID); - new pf_short_nint_nint(JNIEnv.GetShortField), //virtual jshort JNICALL GetShortField(jobject obj, jfieldID fieldID); - new pf_int_nint_nint(JNIEnv.GetIntField), //virtual jint JNICALL GetIntField(jobject obj, jfieldID fieldID); - new pf_long_nint_nint(JNIEnv.GetLongField), //virtual jlong JNICALL GetLongField(jobject obj, jfieldID fieldID); - new pf_float_nint_nint(JNIEnv.GetFloatField), //virtual jfloat JNICALL GetFloatField(jobject obj, jfieldID fieldID); - new pf_double_nint_nint(JNIEnv.GetDoubleField), //virtual jdouble JNICALL GetDoubleField(jobject obj, jfieldID fieldID); - - new pf_void_nint_nint_nint(JNIEnv.SetObjectField), //virtual void JNICALL SetObjectField(jobject obj, jfieldID fieldID, jobject val); - new pf_void_nint_nint_sbyte(JNIEnv.SetBooleanField), //virtual void JNICALL SetBooleanField(jobject obj, jfieldID fieldID, jboolean val); - new pf_void_nint_nint_sbyte(JNIEnv.SetByteField), //virtual void JNICALL SetByteField(jobject obj, jfieldID fieldID, jbyte val); - new pf_void_nint_nint_ushort(JNIEnv.SetCharField), //virtual void JNICALL SetCharField(jobject obj, jfieldID fieldID, jchar val); - new pf_void_nint_nint_short(JNIEnv.SetShortField), //virtual void JNICALL SetShortField(jobject obj, jfieldID fieldID, jshort val); - new pf_void_nint_nint_int(JNIEnv.SetIntField), //virtual void JNICALL SetIntField(jobject obj, jfieldID fieldID, jint val); - new pf_void_nint_nint_long(JNIEnv.SetLongField), //virtual void JNICALL SetLongField(jobject obj, jfieldID fieldID, jlong val); - new pf_void_nint_nint_float(JNIEnv.SetFloatField), //virtual void JNICALL SetFloatField(jobject obj, jfieldID fieldID, jfloat val); - new pf_void_nint_nint_double(JNIEnv.SetDoubleField), //virtual void JNICALL SetDoubleField(jobject obj, jfieldID fieldID, jdouble val); - - new pf_nint_nint_pbyte_pbyte(JNIEnv.GetStaticMethodID), //virtual jmethodID JNICALL GetStaticMethodID(jclass clazz, const char *name, const char *sig); - - null, //virtual jobject JNICALL CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jobject JNICALL CallStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_nint_nint_nint_pjvalue(JNIEnv.CallStaticObjectMethodA), //virtual jobject JNICALL CallStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jboolean JNICALL CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jboolean JNICALL CallStaticBooleanMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_pjvalue(JNIEnv.CallStaticBooleanMethodA), //virtual jboolean JNICALL CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jbyte JNICALL CallStaticByteMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jbyte JNICALL CallStaticByteMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_sbyte_nint_nint_pjvalue(JNIEnv.CallStaticByteMethodA), //virtual jbyte JNICALL CallStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jchar JNICALL CallStaticCharMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jchar JNICALL CallStaticCharMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_ushort_nint_nint_pjvalue(JNIEnv.CallStaticCharMethodA), //virtual jchar JNICALL CallStaticCharMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jshort JNICALL CallStaticShortMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jshort JNICALL CallStaticShortMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_short_nint_nint_pjvalue(JNIEnv.CallStaticShortMethodA), //virtual jshort JNICALL CallStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jint JNICALL CallStaticIntMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jint JNICALL CallStaticIntMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_int_nint_nint_pjvalue(JNIEnv.CallStaticIntMethodA), //virtual jint JNICALL CallStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jlong JNICALL CallStaticLongMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jlong JNICALL CallStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_long_nint_nint_pjvalue(JNIEnv.CallStaticLongMethodA), //virtual jlong JNICALL CallStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jfloat JNICALL CallStaticFloatMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jfloat JNICALL CallStaticFloatMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_float_nint_nint_pjvalue(JNIEnv.CallStaticFloatMethodA), //virtual jfloat JNICALL CallStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual jdouble JNICALL CallStaticDoubleMethod(jclass clazz, jmethodID methodID, ...); - null, //virtual jdouble JNICALL CallStaticDoubleMethodV(jclass clazz, jmethodID methodID, va_list args); - new pf_double_nint_nint_pjvalue(JNIEnv.CallStaticDoubleMethodA), //virtual jdouble JNICALL CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - null, //virtual void JNICALL CallStaticVoidMethod(jclass cls, jmethodID methodID, ...); - null, //virtual void JNICALL CallStaticVoidMethodV(jclass cls, jmethodID methodID, va_list args); - new pf_void_nint_nint_pjvalue(JNIEnv.CallStaticVoidMethodA), //virtual void JNICALL CallStaticVoidMethodA(jclass cls, jmethodID methodID, jvalue * args); - - new pf_nint_nint_pbyte_pbyte(JNIEnv.GetStaticFieldID), //virtual jfieldID JNICALL GetStaticFieldID(jclass clazz, const char *name, const char *sig); - - new pf_nint_nint_nint(JNIEnv.GetStaticObjectField), //virtual jobject JNICALL GetObjectField(jobject obj, jfieldID fieldID); - new pf_sbyte_nint_nint(JNIEnv.GetStaticBooleanField), //virtual jboolean JNICALL GetBooleanField(jobject obj, jfieldID fieldID); - new pf_sbyte_nint_nint(JNIEnv.GetStaticByteField), //virtual jbyte JNICALL GetByteField(jobject obj, jfieldID fieldID); - new pf_ushort_nint_nint(JNIEnv.GetStaticCharField), //virtual jchar JNICALL GetCharField(jobject obj, jfieldID fieldID); - new pf_short_nint_nint(JNIEnv.GetStaticShortField), //virtual jshort JNICALL GetShortField(jobject obj, jfieldID fieldID); - new pf_int_nint_nint(JNIEnv.GetStaticIntField), //virtual jint JNICALL GetIntField(jobject obj, jfieldID fieldID); - new pf_long_nint_nint(JNIEnv.GetStaticLongField), //virtual jlong JNICALL GetLongField(jobject obj, jfieldID fieldID); - new pf_float_nint_nint(JNIEnv.GetStaticFloatField), //virtual jfloat JNICALL GetFloatField(jobject obj, jfieldID fieldID); - new pf_double_nint_nint(JNIEnv.GetStaticDoubleField), //virtual jdouble JNICALL GetDoubleField(jobject obj, jfieldID fieldID); - - new pf_void_nint_nint_nint(JNIEnv.SetStaticObjectField), //virtual void JNICALL SetObjectField(jobject obj, jfieldID fieldID, jobject val); - new pf_void_nint_nint_sbyte(JNIEnv.SetStaticBooleanField), //virtual void JNICALL SetBooleanField(jobject obj, jfieldID fieldID, jboolean val); - new pf_void_nint_nint_sbyte(JNIEnv.SetStaticByteField), //virtual void JNICALL SetByteField(jobject obj, jfieldID fieldID, jbyte val); - new pf_void_nint_nint_ushort(JNIEnv.SetStaticCharField), //virtual void JNICALL SetCharField(jobject obj, jfieldID fieldID, jchar val); - new pf_void_nint_nint_short(JNIEnv.SetStaticShortField), //virtual void JNICALL SetShortField(jobject obj, jfieldID fieldID, jshort val); - new pf_void_nint_nint_int(JNIEnv.SetStaticIntField), //virtual void JNICALL SetIntField(jobject obj, jfieldID fieldID, jint val); - new pf_void_nint_nint_long(JNIEnv.SetStaticLongField), //virtual void JNICALL SetLongField(jobject obj, jfieldID fieldID, jlong val); - new pf_void_nint_nint_float(JNIEnv.SetStaticFloatField), //virtual void JNICALL SetFloatField(jobject obj, jfieldID fieldID, jfloat val); - new pf_void_nint_nint_double(JNIEnv.SetStaticDoubleField), //virtual void JNICALL SetDoubleField(jobject obj, jfieldID fieldID, jdouble val); - - new pf_nint_pjchar_int(JNIEnv.NewString), //virtual jstring JNICALL NewString(const jchar *unicode, jsize len); - new pf_int_nint(JNIEnv.GetStringLength), //virtual jsize JNICALL GetStringLength(jstring str); - new pf_pjchar_nint_pjboolean(JNIEnv.GetStringChars), //virtual const jchar *JNICALL GetStringChars(jstring str, jboolean *isCopy); - new pf_void_nint_pjchar(JNIEnv.ReleaseStringChars), //virtual void JNICALL ReleaseStringChars(jstring str, const jchar *chars); - - new pf_nint_pbyte(JNIEnv.NewStringUTF), //virtual jstring JNICALL NewStringUTF(const char *utf); - new pf_int_nint(JNIEnv.GetStringUTFLength), //virtual jsize JNICALL GetStringUTFLength(jstring str); - new pf_pbyte_nint_pjboolean(JNIEnv.GetStringUTFChars), //virtual const char* JNICALL GetStringUTFChars(jstring str, jboolean *isCopy); - new pf_void_nint_pbyte(JNIEnv.ReleaseStringUTFChars), //virtual void JNICALL ReleaseStringUTFChars(jstring str, const char* chars); - - new pf_int_nint(JNIEnv.GetArrayLength), //virtual jsize JNICALL GetArrayLength(jarray array); - - new pf_nint_int_nint_nint(JNIEnv.NewObjectArray), //virtual jobjectArray JNICALL NewObjectArray(jsize len, jclass clazz, jobject init); - new pf_nint_nint_int(JNIEnv.GetObjectArrayElement), //virtual jobject JNICALL GetObjectArrayElement(jobjectArray array, jsize index); - new pf_void_nint_int_nint(JNIEnv.SetObjectArrayElement), //virtual void JNICALL SetObjectArrayElement(jobjectArray array, jsize index, jobject val); - - new pf_nint_int(JNIEnv.NewBooleanArray), //virtual jbooleanArray JNICALL NewBooleanArray(jsize len); - new pf_nint_int(JNIEnv.NewByteArray), //virtual jbyteArray JNICALL NewByteArray(jsize len); - new pf_nint_int(JNIEnv.NewCharArray), //virtual jcharArray JNICALL NewCharArray(jsize len); - new pf_nint_int(JNIEnv.NewShortArray), //virtual jshortArray JNICALL NewShortArray(jsize len); - new pf_nint_int(JNIEnv.NewIntArray), //virtual jintArray JNICALL NewIntArray(jsize len); - new pf_nint_int(JNIEnv.NewLongArray), //virtual jlongArray JNICALL NewLongArray(jsize len); - new pf_nint_int(JNIEnv.NewFloatArray), //virtual jfloatArray JNICALL NewFloatArray(jsize len); - new pf_nint_int(JNIEnv.NewDoubleArray), //virtual jdoubleArray JNICALL NewDoubleArray(jsize len); - - new pf_pjboolean_nint_pjboolean(JNIEnv.GetBooleanArrayElements), //virtual jboolean * JNICALL GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy); - new pf_pjbyte_nint_pjboolean(JNIEnv.GetByteArrayElements), //virtual jbyte * JNICALL GetByteArrayElements(jbyteArray array, jboolean *isCopy); - new pf_pjchar_nint_pjboolean(JNIEnv.GetCharArrayElements), //virtual jchar * JNICALL GetCharArrayElements(jcharArray array, jboolean *isCopy); - new pf_pjshort_nint_pjboolean(JNIEnv.GetShortArrayElements), //virtual jshort * JNICALL GetShortArrayElements(jshortArray array, jboolean *isCopy); - new pf_pjint_nint_pjboolean(JNIEnv.GetIntArrayElements), //virtual jint * JNICALL GetIntArrayElements(jintArray array, jboolean *isCopy); - new pf_pjlong_nint_pjboolean(JNIEnv.GetLongArrayElements), //virtual jlong * JNICALL GetLongArrayElements(jlongArray array, jboolean *isCopy); - new pf_pjfloat_nint_pjboolean(JNIEnv.GetFloatArrayElements), //virtual jfloat * JNICALL GetFloatArrayElements(jfloatArray array, jboolean *isCopy); - new pf_pjdouble_nint_pjboolean(JNIEnv.GetDoubleArrayElements), //virtual jdouble * JNICALL GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy); - - new pf_void_nint_pjboolean_int(JNIEnv.ReleaseBooleanArrayElements), //virtual void JNICALL ReleaseBooleanArrayElements(jbooleanArray array, jboolean *elems, jint mode); - new pf_void_nint_pjbyte_int(JNIEnv.ReleaseByteArrayElements), //virtual void JNICALL ReleaseByteArrayElements(jbyteArray array, jbyte *elems, jint mode); - new pf_void_nint_pjchar_int(JNIEnv.ReleaseCharArrayElements), //virtual void JNICALL ReleaseCharArrayElements(jcharArray array, jchar *elems, jint mode); - new pf_void_nint_pjshort_int(JNIEnv.ReleaseShortArrayElements), //virtual void JNICALL ReleaseShortArrayElements(jshortArray array, jshort *elems, jint mode); - new pf_void_nint_pjint_int(JNIEnv.ReleaseIntArrayElements), //virtual void JNICALL ReleaseIntArrayElements(jintArray array, jint *elems, jint mode); - new pf_void_nint_pjlong_int(JNIEnv.ReleaseLongArrayElements), //virtual void JNICALL ReleaseLongArrayElements(jlongArray array, jlong *elems, jint mode); - new pf_void_nint_pjfloat_int(JNIEnv.ReleaseFloatArrayElements), //virtual void JNICALL ReleaseFloatArrayElements(jfloatArray array, jfloat *elems, jint mode); - new pf_void_nint_pjdouble_int(JNIEnv.ReleaseDoubleArrayElements), //virtual void JNICALL ReleaseDoubleArrayElements(jdoubleArray array, jdouble *elems, jint mode); - - new pf_void_nint_int_int_nint(JNIEnv.GetBooleanArrayRegion), //virtual void JNICALL GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetByteArrayRegion), //virtual void JNICALL GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetCharArrayRegion), //virtual void JNICALL GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetShortArrayRegion), //virtual void JNICALL GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetIntArrayRegion), //virtual void JNICALL GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetLongArrayRegion), //virtual void JNICALL GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetFloatArrayRegion), //virtual void JNICALL GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetDoubleArrayRegion), //virtual void JNICALL GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); - - new pf_void_nint_int_int_nint(JNIEnv.SetBooleanArrayRegion), //virtual void JNICALL SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetByteArrayRegion), //virtual void JNICALL SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetCharArrayRegion), //virtual void JNICALL SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetShortArrayRegion), //virtual void JNICALL SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetIntArrayRegion), //virtual void JNICALL SetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetLongArrayRegion), //virtual void JNICALL SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetFloatArrayRegion), //virtual void JNICALL SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); - new pf_void_nint_int_int_nint(JNIEnv.SetDoubleArrayRegion), //virtual void JNICALL SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); - - new pf_int_nint_pJNINativeMethod_int(JNIEnv.RegisterNatives), //virtual jint JNICALL RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods); - new pf_int_nint(JNIEnv.UnregisterNatives), //virtual jint JNICALL UnregisterNatives(jclass clazz); - - new pf_int_nint(JNIEnv.MonitorEnter), //virtual jint JNICALL MonitorEnter(jobject obj); - new pf_int_nint(JNIEnv.MonitorExit), //virtual jint JNICALL MonitorExit(jobject obj); - - new pf_int_ppJavaVM(JNIEnv.GetJavaVM), //virtual jint JNICALL GetJavaVM(JavaVM **vm); - - new pf_void_nint_int_int_nint(JNIEnv.GetStringRegion), //virtual void JNICALL GetStringRegion(jstring str, jsize start, jsize len, jchar *buf); - new pf_void_nint_int_int_nint(JNIEnv.GetStringUTFRegion), //virtual void JNICALL GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf); - - new pf_pvoid_nint_pjboolean(JNIEnv.GetPrimitiveArrayCritical), //virtual void* JNICALL GetPrimitiveArrayCritical(jarray array, jboolean *isCopy); - new pf_void_nint_pvoid_int(JNIEnv.ReleasePrimitiveArrayCritical), //virtual void JNICALL ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode); - - new pf_pjchar_nint_pjboolean(JNIEnv.GetStringCritical), //virtual const jchar* JNICALL GetStringCritical(jstring string, jboolean *isCopy); - new pf_void_nint_pjchar(JNIEnv.ReleaseStringCritical), //virtual void JNICALL ReleaseStringCritical(jstring string, const jchar *cstring); - - new pf_nint_nint(JNIEnv.NewWeakGlobalRef), //virtual jweak JNICALL NewWeakGlobalRef(jobject obj); - new pf_void_nint(JNIEnv.DeleteWeakGlobalRef), //virtual void JNICALL DeleteWeakGlobalRef(jweak ref); - - new pf_sbyte(JNIEnv.ExceptionCheck), //virtual jboolean JNICALL ExceptionCheck(); - - new pf_nint_nint_long(JNIEnv.NewDirectByteBuffer), //virtual jobject JNICALL NewDirectByteBuffer(void* address, jlong capacity); - new pf_nint_nint(JNIEnv.GetDirectBufferAddress), //virtual void* JNICALL GetDirectBufferAddress(jobject buf); - new pf_long_nint(JNIEnv.GetDirectBufferCapacity), //virtual jlong JNICALL GetDirectBufferCapacity(jobject buf); - - new pf_int_nint(JNIEnv.GetObjectRefType) // virtual jobjectRefType GetObjectRefType(jobject obj); - }; - - } - -} diff --git a/src/IKVM.Runtime/JNI/jobjectRefType.cs b/src/IKVM.Runtime/JNI/jobjectRefType.cs new file mode 100644 index 0000000000..943024ab55 --- /dev/null +++ b/src/IKVM.Runtime/JNI/jobjectRefType.cs @@ -0,0 +1,14 @@ +namespace IKVM.Runtime.JNI +{ + + enum jobjectRefType : int + { + + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 + + } + +} diff --git a/src/IKVM.Runtime/JNI/jvalue.cs b/src/IKVM.Runtime/JNI/jvalue.cs index 78acf3cc7f..3c6fc04dc3 100644 --- a/src/IKVM.Runtime/JNI/jvalue.cs +++ b/src/IKVM.Runtime/JNI/jvalue.cs @@ -19,24 +19,34 @@ namespace IKVM.Runtime.JNI [StructLayout(LayoutKind.Explicit)] struct jvalue { + [FieldOffset(0)] public jboolean z; + [FieldOffset(0)] public jbyte b; + [FieldOffset(0)] public jchar c; + [FieldOffset(0)] public jshort s; + [FieldOffset(0)] public jint i; + [FieldOffset(0)] public jlong j; + [FieldOffset(0)] public jfloat f; + [FieldOffset(0)] public jdouble d; + [FieldOffset(0)] public jobject l; + } } diff --git a/src/IKVM.Runtime/JVM.Constants.cs.tt b/src/IKVM.Runtime/JVM.Constants.cs.tt new file mode 100644 index 0000000000..9ecadd1bb7 --- /dev/null +++ b/src/IKVM.Runtime/JVM.Constants.cs.tt @@ -0,0 +1,36 @@ + +namespace IKVM.Runtime +{ + + static partial class JVM + { + + static class Constants + { + + public static string java_runtime_name = "OpenJDK Runtime Environment"; + public static string java_runtime_version = "#OpenJdkFullVersion#"; + + public static string java_vm_name = "#Name#"; + public static string java_vm_version = "#Version#"; + public static string java_vm_vendor = "#Name#"; + + public static string java_vm_specification_version = "#OpenJdkSpecificationVersion#"; + public static string java_vm_specification_vendor = "#OpenJdkSpecificationVendor#"; + + public static string java_version = "#OpenJdkImplementationVersion#"; + public static string java_vendor = "#Name#"; + public static string java_vendor_url = "#VendorUrl#"; + public static string java_vendor_url_bug = "#VendorUrlBug#"; + + public static string java_specification_version = "#OpenJdkSpecificationVersion#"; + public static string java_specification_vendor = "#OpenJdkSpecificationVendor#"; + + public static string openjdk_version = "#OpenJdkVersion#"; + public static string openjdk_vendor = "#OpenJdkVendor#"; + + } + + } + +} diff --git a/src/IKVM.Runtime/JVM.Properties.cs b/src/IKVM.Runtime/JVM.Properties.cs new file mode 100644 index 0000000000..897c0a2513 --- /dev/null +++ b/src/IKVM.Runtime/JVM.Properties.cs @@ -0,0 +1,766 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +using IKVM.Runtime.Vfs; + +namespace IKVM.Runtime +{ + + static partial class JVM + { + + /// + /// Property values loaded into the JVM from various sources. + /// + public static class Properties + { + + static readonly IDictionary user = new Dictionary(); + static readonly Lazy> ikvm = new Lazy>(GetIkvmProperties); + static readonly Lazy> init = new Lazy>(GetInitProperties); + static readonly Lazy homePath = new Lazy(GetHomePath); + + /// + /// Gets the set of properties that are set by the user before initialization. + /// + public static IDictionary User => user; + + /// + /// Gets the set of properties that are set in the 'ikvm.properties' file before initialization. + /// + internal static IDictionary Ikvm => ikvm.Value; + + /// + /// Gets the set of properties that are initialized with the JVM and provided to the JDK. + /// + internal static IReadOnlyDictionary Init => init.Value; + + /// + /// Gets the home path. + /// + internal static string HomePath => homePath.Value; + + /// + /// Gets the set of properties loaded from any companion 'ikvm.properties' file. + /// + /// + static Dictionary GetIkvmProperties() + { + var props = new Dictionary(); + + // the runtime assembly will set the root of various relative paths + var runtimePath = Path.GetDirectoryName(typeof(JVM).Assembly.Location); + + try + { + var ikvmPropertiesPath = Path.Combine(runtimePath, "ikvm.properties"); + if (File.Exists(ikvmPropertiesPath)) + LoadProperties(File.ReadAllLines(ikvmPropertiesPath), props); + } + catch (Exception) + { + + } + + return props; + } + + /// + /// Gets the home path for IKVM. + /// + /// + static string GetHomePath() + { + var rootPath = Path.GetDirectoryName(BaseAssembly.Location); + + // user value takes priority + if (User.TryGetValue("ikvm.home", out var homePath1)) + return Path.GetFullPath(Path.Combine(rootPath, homePath1)); + + // ikvm properties value comes next + if (Ikvm.TryGetValue("ikvm.home", out var homePath2)) + return Path.GetFullPath(Path.Combine(rootPath, homePath2)); + + // find first occurance of home root + if (User.TryGetValue("ikvm.home.root", out var homePathRoot) == false) + Ikvm.TryGetValue("ikvm.home.root", out homePathRoot); + + // make root path absolute + homePathRoot = Path.GetFullPath(Path.Combine(rootPath, homePathRoot ?? "ikvm")); + + // calculate ikvm.home from ikvm.home.root + if (Directory.Exists(homePathRoot)) + { + foreach (var rid in GetIkvmHomeRids()) + { + var ikvmHomePath = Path.GetFullPath(Path.Combine(homePathRoot, rid)); + if (Directory.Exists(ikvmHomePath)) + return ikvmHomePath; + } + } + + // fallback to local 'ikvm' directory next to IKVM.Runtime + return Path.GetFullPath(Path.Combine(rootPath, "ikvm")); + } + + /// + /// Reads the property lines from the specified file into the dictionary. + /// + /// + /// + static void LoadProperties(IEnumerable lines, IDictionary props) + { + foreach (var l in lines) + { + var a = l.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); + if (a.Length >= 2) + props[a[0].Trim()] = a[1]?.Trim() ?? ""; + } + } + + /// + /// Gets the set of init properties. + /// + /// + static Dictionary GetInitProperties() + { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else + var p = new Dictionary(); + p["openjdk.version"] = Constants.openjdk_version; + p["java.version"] = Constants.java_version; + p["java.vendor"] = Constants.java_vendor; + p["java.vendor.url"] = Constants.java_vendor_url; + p["java.vendor.url.bug"] = Constants.java_vendor_url_bug; + p["java.vm.name"] = Constants.java_vm_name; + p["java.vm.version"] = Constants.java_vm_version; + p["java.vm.vendor"] = Constants.java_vm_vendor; + p["java.vm.specification.name"] = "Java Virtual Machine Specification"; + p["java.vm.specification.version"] = Constants.java_vm_specification_version; + p["java.vm.specification.vendor"] = Constants.java_vm_specification_vendor; + p["java.vm.info"] = "compiled mode"; + p["java.runtime.name"] = Constants.java_runtime_name; + p["java.runtime.version"] = Constants.java_runtime_version; + p["java.specification.name"] = "Java Platform API Specification"; + p["java.specification.version"] = Constants.java_specification_version; + p["java.specification.vendor"] = Constants.java_specification_vendor; + p["java.class.version"] = "52.0"; + + // various directory paths + p["ikvm.home"] = HomePath; + p["java.home"] = HomePath; + p["java.io.tmpdir"] = GetTempPath(); + p["java.library.path"] = GetLibraryPath(); + p["java.ext.dirs"] = Path.Combine(HomePath, "lib", "ext"); + p["java.endorsed.dirs"] = Path.Combine(HomePath, "lib", "endorsed"); + p["sun.boot.library.path"] = Path.Combine(HomePath, "bin"); + p["sun.boot.class.path"] = VfsTable.Default.GetAssemblyClassesPath(BaseAssembly); + + // various OS information + GetOSProperties(out var osname, out var osversion); + p["os.name"] = osname; + p["os.version"] = osversion; + p["os.arch"] = GetArch(); + p["sun.os.patch.level"] = Environment.OSVersion.ServicePack; + p["sun.arch.data.model"] = (IntPtr.Size * 8).ToString(); + p["sun.cpu.endian"] = BitConverter.IsLittleEndian ? "little" : "big"; + + // text settings + p["file.separator"] = Path.DirectorySeparatorChar.ToString(); + p["file.encoding"] = Encoding.Default.WebName; + p["path.separator"] = Path.PathSeparator.ToString(); + p["line.separator"] = Environment.NewLine; + p["file.encoding.pkg"] = "sun.io"; + p["sun.jnu.encoding"] = RuntimeUtil.IsOSX ? "UTF-8" : Encoding.Default.WebName; + p["sun.stdout.encoding"] = RuntimeUtil.IsWindows && !Console.IsOutputRedirected ? GetWindowsConsoleEncoding() : null; + p["sun.stderr.encoding"] = RuntimeUtil.IsWindows && !Console.IsErrorRedirected ? GetWindowsConsoleEncoding() : null; + + // culture/language properties + GetCultureProperties(out var language, out var country, out var variant, out var script); + p["user.language"] = language; + p["user.country"] = country; + p["user.variant"] = variant; + p["user.script"] = script; + p["user.timezone"] = ""; + + try + { + p["user.name"] = Environment.UserName; + } + catch (SecurityException) + { + p["user.name"] = "(unknown)"; + } + + var home = SafeGetEnvironmentVariable("USERPROFILE"); + if (home == null) + { + // maybe we're on *nix + home = SafeGetEnvironmentVariable("HOME"); + if (home == null) + { + try + { + home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + } + catch (SecurityException) + { + home = "."; + } + } + } + + p["user.home"] = home; + + try + { + p["user.dir"] = Environment.CurrentDirectory; + } + catch (SecurityException) + { + p["user.dir"] = "."; + } + + // other various properties + p["java.awt.headless"] = "true"; + p["sun.nio.MaxDirectMemorySize"] = "-1"; + + // cacerts is mounted by the VFS into ikvmHome + p.Add("javax.net.ssl.trustStore", Path.Combine(HomePath, "lib", "security", "cacerts")); + +#if NETFRAMEWORK + + // read properties from app.config + try + { + foreach (string key in ConfigurationManager.AppSettings) + if (key.StartsWith("ikvm:")) + p.Add(key.Substring(5), ConfigurationManager.AppSettings[key]); + } + catch (ConfigurationException) + { + // app.config is invalid, ignore + } + +#endif + + // set the properties that were specfied + if (user != null) + foreach (var kvp in user) + p[kvp.Key] = kvp.Value; + + return p; +#endif + } + + /// + /// Gets the property values based on the culture. + /// + /// + /// + /// + /// + static void GetCultureProperties(out string language, out string country, out string variant, out string script) + { + var culture = CultureInfo.CurrentCulture.Name.Split('-'); + if (culture.Length == 2) + { + language = culture[0]; + if (culture[1].Length == 4) + { + script = culture[1]; + country = ""; + } + else + { + script = ""; + country = culture[1]; + } + } + else if (culture.Length == 3) + { + language = culture[0]; + script = culture[1]; + country = culture[2]; + } + else + { + language = "en"; + script = ""; + country = "US"; + } + + // Norwegian + if (language == "nb") + { + language = "no"; + country = "NO"; + variant = ""; + } + else if (language == "nn") + { + language = "no"; + country = "NO"; + variant = "NY"; + } + else + { + variant = ""; + } + } + + /// + /// Gets the OS name and OS version property values. + /// + /// + /// + static void GetOSProperties(out string osname, out string osversion) + { + osname = null; + osversion = null; + + if (RuntimeUtil.IsWindows) + { + const byte VER_NT_DOMAIN_CONTROLLER = 0x0000002; + const byte VER_NT_SERVER = 0x0000003; + const byte VER_NT_WORKSTATION = 0x0000001; + + var os = Environment.OSVersion; + int major = os.Version.Major; + int minor = os.Version.Minor; + int build = os.Version.Build; + + switch (os.Platform) + { + case PlatformID.Win32Windows: + if (major == 4) + { + osname = minor switch + { + 0 => "Windows 95", + 10 => "Windows 98", + 90 => "Windows Me", + _ => "Windows 9X (unknown)", + }; + } + else + { + osname = "Windows 9X (unknown)"; + } + break; + case PlatformID.Win32NT: + var kernel32 = GetKernel32FileVersionInfo(); + if (kernel32 != null) + { + major = kernel32.ProductMajorPart; + minor = kernel32.ProductMinorPart; + build = kernel32.ProductBuildPart; + } + + var productType = GetWindowsProductType(); + if (productType < 0) + // error + + osname = "Windows NT (unknown)"; + switch (major) + { + case 3: + case 4: + osname = "Windows NT"; + break; + case 5: + switch (minor) + { + case 0: + osname = "Windows 2000"; + break; + case 1: + osname = "Windows XP"; + break; + case 2: + if (productType == VER_NT_WORKSTATION && Environment.Is64BitOperatingSystem) + { + osname = "Windows XP"; + } + else + { + osname = "Windows 2003"; + } + break; + default: + osname = "Windows NT (unknown)"; + break; + } + break; + case 6: + if (productType == VER_NT_WORKSTATION) + { + switch (minor) + { + case 0: + osname = "Windows Vista"; + break; + case 1: + osname = "Windows 7"; + break; + case 2: + osname = "Windows 8"; + break; + case 3: + osname = "Windows 8.1"; + break; + default: + osname = "Windows NT (unknown)"; + break; + } + } + else + { + switch (minor) + { + case 0: + osname = "Windows Server 2008"; + break; + case 1: + osname = "Windows Server 2008 R2"; + break; + case 2: + osname = "Windows Server 2012"; + break; + case 3: + osname = "Windows Server 2012 R2"; + break; + default: + osname = "Windows NT (unknown)"; + break; + } + } + break; + case 10: + if (productType == VER_NT_WORKSTATION) + { + switch (minor) + { + case 0: + if (build >= 22000) + osname = "Windows 11"; + else + osname = "Windows 10"; + break; + default: + osname = "Windows NT (unknown)"; + break; + } + } + else + { + switch (minor) + { + case 0: + if (build > 20347) + osname = "Windows Server 2022"; + else if (build > 17676) + osname = "Windows Server 2019"; + else + osname = "Windows Server 2016"; + break; + default: + osname = "Windows NT (unknown)"; + break; + } + } + break; + default: + osname = "Windows (unknown)"; + break; + } + break; + } + + osversion = major + "." + minor; + } + else if (RuntimeUtil.IsLinux) + { + var sysname = GetLinuxSysnameAndRelease(); + osname = sysname[0]; + osversion = sysname[1]; + } + else if (RuntimeUtil.IsOSX) + { + osname = "Mac OS X"; + osversion = "10.15"; + + // OpenJDK collects the version from a number of different places + // we should do that in the future + } + + osname ??= Environment.OSVersion.ToString(); + } + + /// + /// Gets the platform architecture. + /// + /// + static string GetArch() + { + var arch = SafeGetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); + if (arch == null) + { + // guess based on OS and bit size + if (IntPtr.Size == 4) + return RuntimeUtil.IsWindows ? "x86" : "i386"; + else + return "amd64"; + } + + if (arch.Equals("AMD64", StringComparison.OrdinalIgnoreCase)) + return "amd64"; + + return null; + } + + /// + /// Gets the temporary path. + /// + /// + static string GetTempPath() + { + try + { + return Path.GetTempPath(); + } + catch (SecurityException) + { + return "."; + } + } + + /// + /// Gets the path string for loading native libraries. + /// + /// + static string GetLibraryPath() + { + var libraryPath = new List(); + + if (RuntimeUtil.IsWindows) + { + // see /hotspot/src/os/windows/vm/os_windows.cpp for the comment that describes how we build the path + var windir = SafeGetEnvironmentVariable("SystemRoot"); + if (windir != null) + libraryPath.Add(Path.Combine(windir, "Sun", "Java", "bin")); + + try + { + libraryPath.Add(Environment.SystemDirectory); + } + catch (SecurityException) + { + + } + + if (windir != null) + libraryPath.Add(windir); + + var path = SafeGetEnvironmentVariable("PATH"); + if (path != null) + libraryPath.Add(path); + } + + if (RuntimeUtil.IsLinux) + { + // on Linux we have some hardcoded paths (from /hotspot/src/os/linux/vm/os_linux.cpp) + // and we can only guess the cpu arch based on bitness (that means only x86 and x64) + libraryPath.Add(Path.Combine("/usr/java/packages/lib/", IntPtr.Size == 4 ? "i386" : "amd64")); + libraryPath.Add("/lib"); + libraryPath.Add("/usr/lib"); + + // prefix with LD_LIBRARY_PATH + var ld_library_path = SafeGetEnvironmentVariable("LD_LIBRARY_PATH"); + if (ld_library_path != null) + libraryPath.Insert(0, ld_library_path); + } + + if (RuntimeUtil.IsOSX) + { + libraryPath.Add("."); + } + + try + { + libraryPath.Insert(0, Path.GetDirectoryName(BaseAssembly.Location)); + } + catch (Exception) + { + // ignore + } + + if (RuntimeUtil.IsWindows) + libraryPath.Add("."); + + return string.Join(Path.PathSeparator.ToString(), libraryPath); + } + + /// + /// Gets the version information from the kernel32 file, if present. + /// + /// + static FileVersionInfo GetKernel32FileVersionInfo() + { + try + { + foreach (ProcessModule module in Process.GetCurrentProcess().Modules) + if (string.Compare(module.ModuleName, "kernel32.dll", StringComparison.OrdinalIgnoreCase) == 0) + return module.FileVersionInfo; + } + catch + { + + } + + return null; + } + + /// + /// VER_NT_* values. + /// + enum VER_NT : byte + { + + DOMAIN_CONTROLLER = 0x0000002, + SERVER = 0x0000003, + WORKSTATION = 0x0000001, + + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct OSVERSIONINFOEXW + { + + public int dwOSVersionInfoSize; + public int dwMajorVersion; + public int dwMinorVersion; + public int dwBuildNumber; + public int dwPlatformId; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string szCSDVersion; + public ushort wServicePackMajor; + public ushort wServicePackMinor; + public ushort wSuiteMask; + public VER_NT wProductType; + public byte wReserved; + + } + + [DllImport("ntdll.dll", SetLastError = true)] + static extern int RtlGetVersion(ref OSVERSIONINFOEXW versionInfo); + + /// + /// Gets the Windows ProductType. + /// + /// + static byte GetWindowsProductType() + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + if (RuntimeUtil.IsWindows == false) + throw new Exception("Cannot retrieve a Windows product type for this operating system."); + + var osvi = default(OSVERSIONINFOEXW); + osvi.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEXW)); + if (RtlGetVersion(ref osvi) != 0) + return 0; + + return (byte)osvi.wProductType; +#endif + } + + /// + /// Gets the 'sysname' on Linux. + /// + /// + static string[] GetLinuxSysnameAndRelease() + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + if (RuntimeUtil.IsLinux == false) + throw new Exception("Cannot retrieve sysname information for this operating system."); + + if (Mono.Unix.Native.Syscall.uname(out var utsname) != 0) + return null; + + return new[] { utsname.sysname, utsname.release }; +#endif + } + + /// + /// Returns the possible architecture names of the ikvm.home directory to use for this run. + /// + /// + static IEnumerable GetIkvmHomeRids() + { + var arch = RuntimeInformation.ProcessArchitecture switch + { + Architecture.X86 => "x86", + Architecture.X64 => "x64", + Architecture.Arm => "arm", + Architecture.Arm64 => "arm64", + _ => throw new NotSupportedException(), + }; + + if (RuntimeUtil.IsWindows) + { + var v = Environment.OSVersion.Version; + + // Windows 10 + if (v.Major > 10 || (v.Major == 10 && v.Minor >= 0)) + yield return $"win10-{arch}"; + + // Windows 8.1 + if (v.Major > 6 || (v.Major == 6 && v.Minor >= 3)) + yield return $"win81-{arch}"; + + // Windows 7 + if (v.Major > 6 || (v.Major == 6 && v.Minor >= 1)) + yield return $"win7-{arch}"; + + // fallback + yield return $"win-{arch}"; + } + + if (RuntimeUtil.IsLinux) + { + yield return $"linux-{arch}"; + } + + if (RuntimeUtil.IsOSX) + { + yield return $"osx-{arch}"; + } + } + + /// + /// Gets the console encoding. + /// + /// + static string GetWindowsConsoleEncoding() + { + var codepage = Console.InputEncoding.CodePage; + return codepage is >= 847 and <= 950 ? $"ms{codepage}" : $"cp{codepage}"; + } + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/vm.cs b/src/IKVM.Runtime/JVM.cs similarity index 63% rename from src/IKVM.Runtime/vm.cs rename to src/IKVM.Runtime/JVM.cs index 3d8eb7ab4c..a0bf961fb7 100644 --- a/src/IKVM.Runtime/vm.cs +++ b/src/IKVM.Runtime/JVM.cs @@ -27,77 +27,115 @@ Jeroen Frijters using System.Text; using System.Security; -#if STATIC_COMPILER || STUB_GENERATOR -using IKVM.Reflection; +using IKVM.Runtime.Accessors; +using IKVM.Runtime.Accessors.Java.Lang; +#if IMPORTER || EXPORTER +using IKVM.Reflection; using Type = IKVM.Reflection.Type; #else using System.Reflection; #endif -#if !STATIC_COMPILER && !STUB_GENERATOR - -namespace IKVM.Internal -{ - - public static class Starter - { - - } - -} - -#endif // !STATIC_COMPILER && !STUB_GENERATOR +#if IMPORTER +using IKVM.Tools.Importer; +#endif -namespace IKVM.Internal +namespace IKVM.Runtime { - static class JVM + static partial class JVM { internal const string JarClassList = "--ikvm-classes--/"; -#if !STUB_GENERATOR - private static int emitSymbols; -#if CLASSGC - internal static bool classUnloading = true; -#endif +#if !EXPORTER + static int emitSymbols; #endif - private static Assembly coreAssembly; -#if !STUB_GENERATOR + /// + /// Reference to the 'java.base' assembly. + /// + static Assembly baseAssembly; + +#if !EXPORTER internal static bool relaxedVerification = true; internal static bool AllowNonVirtualCalls; internal static readonly bool DisableEagerClassLoading = SafeGetEnvironmentVariable("IKVM_DISABLE_EAGER_CLASS_LOADING") != null; #endif -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS +#if !IMPORTER && !EXPORTER && !FIRST_PASS - static JVM() - { + readonly static object initializedLock = new object(); + static bool initialized; - } + static AccessorCache baseAccessors; + static ThreadGroupAccessor threadGroupAccessor; + static SystemAccessor systemAccessor; -#endif + static Lazy systemThreadGroup = new Lazy(MakeSystemThreadGroup); + static Lazy mainThreadGroup = new Lazy(MakeMainThreadGroup); + + internal static AccessorCache BaseAccessors => AccessorCache.Get(ref baseAccessors, BaseAssembly); + + static ThreadGroupAccessor ThreadGroupAccessor => BaseAccessors.Get(ref threadGroupAccessor); + + static SystemAccessor SystemAccessor => BaseAccessors.Get(ref systemAccessor); - internal static Version SafeGetAssemblyVersion(System.Reflection.Assembly asm) + /// + /// Gets the 'system' thread group. + /// + public static object SystemThreadGroup => systemThreadGroup.Value; + + /// + /// Gets the 'main' thread group. + /// + public static object MainThreadGroup => mainThreadGroup.Value; + + /// + /// Ensures the JVM is initialized. + /// + public static void EnsureInitialized() { - // Assembly.GetName().Version requires FileIOPermission, - // so we parse the FullName manually :-( - string name = asm.FullName; - int start = name.IndexOf(", Version="); - if (start >= 0) + if (initialized == false) { - start += 10; - int end = name.IndexOf(',', start); - if (end >= 0) + lock (initializedLock) { - return new Version(name.Substring(start, end - start)); + if (initialized == false) + { + initialized = true; + SystemAccessor.InvokeInitializeSystemClass(); + } } } - return new Version(); + + } + + /// + /// Creates the 'system' thread group. + /// + /// + static object MakeSystemThreadGroup() + { + return ThreadGroupAccessor.Init(); + } + + /// + /// Creates the 'main' thread group. + /// + /// + static object MakeMainThreadGroup() + { + return ThreadGroupAccessor.Init(null, SystemThreadGroup, "main"); } +#endif + + /// + /// Gets an environmental variable. + /// + /// + /// internal static string SafeGetEnvironmentVariable(string name) { try @@ -110,29 +148,29 @@ internal static string SafeGetEnvironmentVariable(string name) } } - internal static Assembly CoreAssembly + internal static Assembly BaseAssembly { get { -#if !STATIC_COMPILER && !STUB_GENERATOR - if (coreAssembly == null) +#if !IMPORTER && !EXPORTER + if (baseAssembly == null) { #if FIRST_PASS - throw new InvalidOperationException("This version of IKVM.Runtime.dll was compiled with FIRST_PASS defined."); + throw new InvalidOperationException("This version of IKVM.Runtime.dll was compiled with FIRST_PASS defined."); #else - coreAssembly = typeof(java.lang.Object).Assembly; + baseAssembly = typeof(java.lang.Object).Assembly; #endif } -#endif // !STATIC_COMPILER - return coreAssembly; +#endif + return baseAssembly; } set { - coreAssembly = value; + baseAssembly = value; } } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal static bool EmitSymbols { @@ -140,34 +178,33 @@ internal static bool EmitSymbols { if (emitSymbols == 0) { - int state; - string debug = System.Configuration.ConfigurationManager.AppSettings["ikvm-emit-symbols"]; - if (debug == null) - { - state = Debugger.IsAttached ? 1 : 2; - } - else - { - state = debug.Equals("True", StringComparison.OrdinalIgnoreCase) ? 1 : 2; - } + var state = 2; + +#if NETFRAMEWORK + // check app.config on Framework + if (string.Equals(System.Configuration.ConfigurationManager.AppSettings["ikvm-emit-symbols"] ?? "", "true", StringComparison.OrdinalIgnoreCase)) + state = 1; +#endif + + // respect the IKVM_EMIT_SYMBOLs environmental variable + if (string.Equals(Environment.GetEnvironmentVariable("IKVM_EMIT_SYMBOLS") ?? "", "true", StringComparison.OrdinalIgnoreCase)) + state = 1; + + // by default enable symbols if a debugger is attached + if (state == 2 && Debugger.IsAttached) + state = 1; // make sure we only set the value once, because it isn't allowed to changed as that could cause // the compiler to try emitting symbols into a ModuleBuilder that doesn't accept them (and would // throw an InvalidOperationException) Interlocked.CompareExchange(ref emitSymbols, state, 0); } + return emitSymbols == 1; } } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - internal static bool IsUnix - { - get - { - return Environment.OSVersion.Platform == PlatformID.Unix; - } - } +#endif internal static string MangleResourceName(string name) { @@ -215,69 +252,7 @@ internal static int PersistableHash(string str) return (int)key; } -#if !STATIC_COMPILER - - internal static void CriticalFailure(string message, Exception x) - { - try - { - Tracer.Error(Tracer.Runtime, "CRITICAL FAILURE: {0}", message); - System.Type messageBox = null; -#if !STUB_GENERATOR - // NOTE we use reflection to invoke MessageBox.Show, to make sure we run in environments where WinForms isn't available - Assembly winForms = IsUnix ? null : Assembly.Load("System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - if (winForms != null) - { - messageBox = winForms.GetType("System.Windows.Forms.MessageBox"); - } -#endif - message = String.Format("****** Critical Failure: {1} ******{0}{0}" + - "PLEASE FILE A BUG REPORT FOR IKVM.NET WHEN YOU SEE THIS MESSAGE{0}{0}" + - (messageBox != null ? "(on Windows you can use Ctrl+C to copy the contents of this message to the clipboard){0}{0}" : "") + - "{2}{0}" + - "{3}{0}" + - "{4} {5}-bit{0}{0}" + - "{6}{0}" + - "{7}{0}" + - "{8}", - Environment.NewLine, - message, - System.Reflection.Assembly.GetExecutingAssembly().FullName, - System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(), - Environment.Version, - IntPtr.Size * 8, - x, - x != null ? new StackTrace(x, true).ToString() : "", - new StackTrace(true)); - if (messageBox != null) - { - try - { - Version ver = SafeGetAssemblyVersion(typeof(JVM).Assembly); - messageBox.InvokeMember("Show", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public, null, null, new object[] { message, "IKVM.NET " + ver + " Critical Failure" }); - } - catch - { - Console.Error.WriteLine(message); - } - } - else - { - Console.Error.WriteLine(message); - } - } - catch (Exception ex) - { - Console.Error.WriteLine(ex); - } - finally - { - Environment.Exit(666); - } - } -#endif // !STATIC_COMPILER - -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER internal static Type LoadType(System.Type type) { return StaticCompiler.GetRuntimeType(type.FullName); @@ -289,7 +264,7 @@ internal static Type LoadType(System.Type type) // with can be different from the one we're compiling against.) internal static Type LoadType(Type type) { -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER return StaticCompiler.GetRuntimeType(type.FullName); #else return type; @@ -298,8 +273,8 @@ internal static Type LoadType(Type type) internal static object Box(object val) { -#if STATIC_COMPILER || FIRST_PASS || STUB_GENERATOR - return null; +#if IMPORTER || FIRST_PASS || EXPORTER + return null; #else if (val is byte) { @@ -342,8 +317,8 @@ internal static object Box(object val) internal static object Unbox(object val) { -#if STATIC_COMPILER || FIRST_PASS || STUB_GENERATOR - return null; +#if IMPORTER || FIRST_PASS || EXPORTER + return null; #else java.lang.Byte b = val as java.lang.Byte; if (b != null) @@ -392,7 +367,7 @@ internal static object Unbox(object val) #endif } -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal static object NewAnnotation(java.lang.ClassLoader classLoader, object definition) { #if !FIRST_PASS @@ -411,11 +386,11 @@ internal static object NewAnnotation(java.lang.ClassLoader classLoader, object d } #endif -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER internal static object NewAnnotationElementValue(java.lang.ClassLoader classLoader, java.lang.Class expectedClass, object definition) { #if FIRST_PASS - return null; + return null; #else try { @@ -430,12 +405,12 @@ internal static object NewAnnotationElementValue(java.lang.ClassLoader classLoad } #endif -#if !STATIC_COMPILER && !STUB_GENERATOR +#if !IMPORTER && !EXPORTER // helper for JNI (which doesn't have access to core library internals) internal static object NewDirectByteBuffer(long address, int capacity) { #if FIRST_PASS - return null; + return null; #else return java.nio.DirectByteBuffer.__new(address, capacity); #endif @@ -444,7 +419,7 @@ internal static object NewDirectByteBuffer(long address, int capacity) internal static Type Import(System.Type type) { -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER return StaticCompiler.Universe.Import(type); #else return type; @@ -453,9 +428,4 @@ internal static Type Import(System.Type type) } - static class Experimental - { - internal static readonly bool JDK_9 = JVM.SafeGetEnvironmentVariable("IKVM_EXPERIMENTAL_JDK_9") != null; - } - } diff --git a/src/IKVM.Runtime/Java/Externs/com/sun/security/auth/module/NTSystem.cs b/src/IKVM.Runtime/Java/Externs/com/sun/security/auth/module/NTSystem.cs index e0d9ba0482..343e2d7319 100644 --- a/src/IKVM.Runtime/Java/Externs/com/sun/security/auth/module/NTSystem.cs +++ b/src/IKVM.Runtime/Java/Externs/com/sun/security/auth/module/NTSystem.cs @@ -29,6 +29,7 @@ namespace IKVM.Java.Externs.com.sun.security.auth.module static class NTSystem { + public static void getCurrent(object thisObj, bool debug, ref string userName, ref string domain, ref string domainSID, ref string userSID, ref string[] groupIDs, ref string primaryGroupID) { WindowsIdentity id = WindowsIdentity.GetCurrent(); @@ -51,6 +52,7 @@ public static long getImpersonationToken0(object thisObj) { return WindowsIdentity.GetCurrent().Token.ToInt64(); } + } } diff --git a/src/IKVM.Runtime/Java/Externs/ikvm/internal/io/FileStreamExtensions.cs b/src/IKVM.Runtime/Java/Externs/ikvm/internal/io/FileStreamExtensions.cs index da437d845c..011d7c628c 100644 --- a/src/IKVM.Runtime/Java/Externs/ikvm/internal/io/FileStreamExtensions.cs +++ b/src/IKVM.Runtime/Java/Externs/ikvm/internal/io/FileStreamExtensions.cs @@ -9,7 +9,7 @@ static class FileStreamExtensions public static FileStream createInternal(string path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options) { -#if NET461 +#if NETFRAMEWORK return new FileStream(path, mode, rights, share, bufferSize, options); #else var access = 0; @@ -20,7 +20,7 @@ public static FileStream createInternal(string path, FileMode mode, FileSystemRi if (access == 0) access = (int)FileAccess.ReadWrite; - return new FileStream(path, mode, (FileAccess)access, share, bufferSize, options); + return new FileStream(path, mode, (FileAccess)access, share, bufferSize, options); #endif } diff --git a/src/IKVM.Runtime/Java/Externs/ikvm/runtime/AssemblyClassLoader.cs b/src/IKVM.Runtime/Java/Externs/ikvm/runtime/AssemblyClassLoader.cs index c96b643392..97484049a0 100644 --- a/src/IKVM.Runtime/Java/Externs/ikvm/runtime/AssemblyClassLoader.cs +++ b/src/IKVM.Runtime/Java/Externs/ikvm/runtime/AssemblyClassLoader.cs @@ -26,6 +26,7 @@ Jeroen Frijters using IKVM.Internal; using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; using AssemblyClassLoader_ = IKVM.Internal.AssemblyClassLoader; @@ -35,6 +36,14 @@ namespace IKVM.Java.Externs.ikvm.runtime static class AssemblyClassLoader { +#if FIRST_PASS == false + + static ClassLoaderAccessor classLoaderAccessor; + + static ClassLoaderAccessor ClassLoaderAccessor => JVM.BaseAccessors.Get(ref classLoaderAccessor); + +#endif + public static void setWrapper(global::java.lang.ClassLoader _this, Assembly assembly) { ClassLoaderWrapper.SetWrapperForClassLoader(_this, AssemblyClassLoader_.FromAssembly(assembly)); @@ -47,10 +56,9 @@ public static void setWrapper(global::java.lang.ClassLoader _this, Assembly asse #else try { - if (!global::java.lang.ClassLoader.checkName(name)) - { + if (ClassLoaderAccessor.InvokeCheckName(_this, name) == false) throw new ClassNotFoundException(name); - } + var wrapper = (AssemblyClassLoader_)ClassLoaderWrapper.GetClassLoaderWrapper(_this); var tw = wrapper.LoadClass(name); if (tw == null) diff --git a/src/IKVM.Runtime/Java/Externs/ikvm/runtime/Util.cs b/src/IKVM.Runtime/Java/Externs/ikvm/runtime/Util.cs index 83cf6e278a..2484f89224 100644 --- a/src/IKVM.Runtime/Java/Externs/ikvm/runtime/Util.cs +++ b/src/IKVM.Runtime/Java/Externs/ikvm/runtime/Util.cs @@ -152,8 +152,8 @@ public static Type getInstanceTypeFromClass(global::java.lang.Class classObject) var wrapper = TypeWrapper.FromClass(classObject); if (wrapper.IsRemapped && wrapper.IsFinal) return wrapper.TypeAsTBD; - - return wrapper.TypeAsBaseType; + else + return wrapper.TypeAsBaseType; } /// @@ -164,13 +164,12 @@ public static Type getInstanceTypeFromClass(global::java.lang.Class classObject) public static Type getRuntimeTypeFromClass(global::java.lang.Class classObject) { var wrapper = TypeWrapper.FromClass(classObject); + wrapper.Finish(); + if (wrapper.IsRemapped && wrapper.IsFinal) - { - wrapper.Finish(); return wrapper.TypeAsTBD; - } - - return wrapper.TypeAsBaseType; + else + return wrapper.TypeAsBaseType; } [HideFromJava] diff --git a/src/IKVM.Runtime/Java/Externs/java/io/FileDescriptor.cs b/src/IKVM.Runtime/Java/Externs/java/io/FileDescriptor.cs index e88f9d3727..b9492ced03 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/FileDescriptor.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/FileDescriptor.cs @@ -1,93 +1,163 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - +using System; using System.IO; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security; -using System.Security.AccessControl; -using IKVM.Runtime.Vfs; +using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; namespace IKVM.Java.Externs.java.io { + /// + /// Implements the native methods for 'FileDescriptor'. + /// static class FileDescriptor { - public static Stream open(string name, FileMode mode, FileAccess access) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + [DllImport("kernel32")] + static extern IntPtr GetStdHandle(int nStdHandle); + + /// + /// Implements the native method 'getHandle0'. + /// + /// + /// + public static int getFd(object self) { - if (VfsTable.Default.IsPath(name)) +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsWindows) { - return VfsTable.Default.Open(name, mode, access); + if (FileDescriptorAccessor.GetStream(self) is FileStream fs) + { + return fs.SafeFileHandle.DangerousGetHandle().ToInt32(); + } + else if (self == FileDescriptorAccessor.GetIn()) + { + return GetStdHandle(-10).ToInt32(); + } + else if (self == FileDescriptorAccessor.GetOut()) + { + return GetStdHandle(-11).ToInt32(); + } + else if (self == FileDescriptorAccessor.GetErr()) + { + return GetStdHandle(-12).ToInt32(); + } } - else if (mode == FileMode.Append) + else { -#if NETFRAMEWORK - // this is the way to get atomic append behavior for all writes - return new FileStream(name, mode, FileSystemRights.AppendData, FileShare.ReadWrite, 1, FileOptions.None); -#else - // the above constructor does not exist in .net core - // since the buffer size is 1 byte, it's always atomic - // if the buffer size needs to be bigger, find a way for the atomic append - return new FileStream(name, mode, access, FileShare.ReadWrite, 1, false); -#endif + + if (FileDescriptorAccessor.GetStream(self) is FileStream fs) + { + return fs.SafeFileHandle.DangerousGetHandle().ToInt32(); + } + else if (self == FileDescriptorAccessor.GetIn()) + { + return 0; + } + else if (self == FileDescriptorAccessor.GetOut()) + { + return 1; + } + else if (self == FileDescriptorAccessor.GetErr()) + { + return 2; + } } - else + + return -1; +#endif + } + + /// + /// Implements the native method 'getHandle0'. + /// + /// + /// + public static long getHandle(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsWindows) { - return new FileStream(name, mode, access, FileShare.ReadWrite, 1, false); + if (FileDescriptorAccessor.GetStream(self) is FileStream fs) + { + return fs.SafeFileHandle.DangerousGetHandle().ToInt64(); + } + else if (self == FileDescriptorAccessor.GetIn()) + { + return GetStdHandle(-10).ToInt64(); + } + else if (self == FileDescriptorAccessor.GetOut()) + { + return GetStdHandle(-11).ToInt64(); + } + else if (self == FileDescriptorAccessor.GetErr()) + { + return GetStdHandle(-12).ToInt64(); + } } + + return -1; +#endif } - [SecuritySafeCritical] - public static bool flushPosix(FileStream fs) + /// + /// Implements the native method 'standardStream'. + /// + /// + /// + public static object standardStream(int fd) { - // this whole method is a bit wonky, shouldn't exist, flush should be handled in .NET - // and not have separate POSIX and Win32 calls +#if FIRST_PASS + throw new NotImplementedException(); +#else + return FileDescriptorAccessor.FromStream(fd switch + { + 0 => System.Console.OpenStandardInput(), + 1 => System.Console.OpenStandardOutput(), + 2 => System.Console.OpenStandardError(), + _ => throw new NotImplementedException(), + }); +#endif + } - var success = false; - var handle = fs.SafeFileHandle; - RuntimeHelpers.PrepareConstrainedRegions(); + /// + /// Implements the native method 'sync'. + /// + /// + /// + public static void sync(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var stream = FileDescriptorAccessor.GetStream(self); + if (stream == null) + throw new global::java.io.SyncFailedException("Sync failed."); + if (stream.CanWrite == false) + return; try { - handle.DangerousAddRef(ref success); -#if NETCOREAPP - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return Mono.Unix.Native.Syscall.fsync(handle.DangerousGetHandle().ToInt32()) == 0; - else - // TODO OS X - return true; -#else - return true; -#endif + stream.Flush(); } - finally + catch (IOException e) { - if (success) - handle.DangerousRelease(); + throw new global::java.io.SyncFailedException(e.Message); } +#endif } } diff --git a/src/IKVM.Runtime/Java/Externs/java/io/FileInputStream.cs b/src/IKVM.Runtime/Java/Externs/java/io/FileInputStream.cs index 6a8dce0337..7d74cacd26 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/FileInputStream.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/FileInputStream.cs @@ -1,86 +1,294 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -#if !NO_REF_EMIT -#endif -using System.Runtime.InteropServices; +using System; +using System.IO; +using System.Security; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Vfs; namespace IKVM.Java.Externs.java.io { + /// + /// Implements the native methods for 'FileInputStream'. + /// static class FileInputStream { - public static void open0(object _this, string name, [In] global::java.io.FileDescriptor fd) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static FileInputStreamAccessor fileInputStreamAccessor; + static FileInputStreamAccessor FileInputStreamAccessor => JVM.BaseAccessors.Get(ref fileInputStreamAccessor); + +#endif + + /// + /// Implements the native method 'open0'. + /// + /// + /// + public static void open0(object self, string name) { -#if !FIRST_PASS - fd.openReadOnly(name); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + var fileMode = FileMode.Open; + var fileAccess = FileAccess.Read; + + if (VfsTable.Default.IsPath(name)) + { + FileDescriptorAccessor.SetStream(fd, VfsTable.Default.Open(name, fileMode, fileAccess)); + return; + } + + FileDescriptorAccessor.SetStream(fd, new FileStream(name, fileMode, fileAccess, FileShare.ReadWrite, 1, false)); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (ArgumentException e) + { + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (SecurityException e) + { + throw new global::java.lang.SecurityException(e.Message); + } + catch (UnauthorizedAccessException) + { + throw new global::java.io.FileNotFoundException(name + " (Access is denied)"); + } + catch (IOException e) + { + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.FileNotFoundException(e.Message); + } #endif } - public static int read0(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'read0'. + /// + /// + /// + public static int read0(object self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.read(); + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanRead == false) + throw new global::java.io.IOException("Read failed."); + + try + { + return stream.ReadByte(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static int readBytes(object _this, byte[] b, int off, int len, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'readBytes'. + /// + /// + /// + /// + /// + /// + public static int readBytes(object self, byte[] b, int off, int len) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.readBytes(b, off, len); + if ((off < 0) || (off > b.Length) || (len < 0) || (len > (b.Length - off))) + throw new global::java.lang.IndexOutOfBoundsException(); + + // user tried to read no bytes, we did + if (len == 0) + return 0; + + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanRead == false) + throw new global::java.io.IOException("Read failed."); + + try + { + var n = stream.Read(b, off, len); + return n == 0 ? -1 : n; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static long skip(object _this, long n, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'skip'. + /// + /// + /// + /// + public static long skip(object self, long n) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.skip(n); + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanSeek == false) + throw new global::java.io.IOException("The handle is invalid."); + + try + { + long cur = stream.Position; + long end = stream.Seek(n, SeekOrigin.Current); + return end - cur; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static int available(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'available'. + /// + /// + /// + public static int available(object self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.available(); + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanSeek == false) + return 0; + + try + { + return (int)Math.Min(int.MaxValue, Math.Max(0, stream.Length - stream.Position)); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void close0(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'close0'. + /// + /// + public static void close0(object self) { -#if !FIRST_PASS - fd.close(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = FileInputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + return; + + try + { + FileDescriptorAccessor.SetStream(fd, null); + stream.Close(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } + /// + /// Implements the native method 'initIDs'. + /// public static void initIDs() { diff --git a/src/IKVM.Runtime/Java/Externs/java/io/FileOutputStream.cs b/src/IKVM.Runtime/Java/Externs/java/io/FileOutputStream.cs index 8f90c19844..385bc7c535 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/FileOutputStream.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/FileOutputStream.cs @@ -1,28 +1,11 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters +using System; +using System.IO; +using System.Security; +using System.Security.AccessControl; - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Runtime.InteropServices; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Vfs; namespace IKVM.Java.Externs.java.io { @@ -30,44 +13,215 @@ namespace IKVM.Java.Externs.java.io static class FileOutputStream { - public static void open0(object _this, string name, bool append, [In] global::java.io.FileDescriptor fd) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static FileOutputStreamAccessor fileOutputStreamAccessor; + static FileOutputStreamAccessor FileOutputStreamAccessor => JVM.BaseAccessors.Get(ref fileOutputStreamAccessor); + +#endif + + /// + /// Implements the native method 'open0'. + /// + /// + /// + /// + public static void open0(object self, string name, bool append) { -#if !FIRST_PASS - if (append) +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = FileOutputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + var fileMode = append ? FileMode.Append : FileMode.Create; + var fileAccess = FileAccess.Write; + + if (VfsTable.Default.IsPath(name)) + { + FileDescriptorAccessor.SetStream(fd, VfsTable.Default.Open(name, fileMode, fileAccess)); + return; + } + + if (append) + { +#if NETFRAMEWORK + FileDescriptorAccessor.SetStream(fd, new FileStream(name, fileMode, FileSystemRights.AppendData, FileShare.ReadWrite, 1, FileOptions.None)); +#else + // the above constructor does not exist in .NET Core + FileDescriptorAccessor.SetStream(fd, new FileStream(name, fileMode, fileAccess, FileShare.ReadWrite, 1, false)); +#endif + } + else + { + FileDescriptorAccessor.SetStream(fd, new FileStream(name, fileMode, fileAccess, FileShare.ReadWrite, 1, false)); + } + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (ArgumentException e) + { + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (SecurityException e) + { + throw new global::java.lang.SecurityException(e.Message); + } + catch (UnauthorizedAccessException) { - fd.openAppend(name); + throw new global::java.io.FileNotFoundException(name + " (Access is denied)"); } - else + catch (IOException e) { - fd.openWriteOnly(name); + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.FileNotFoundException(e.Message); } #endif } - public static void write(object _this, int b, bool append, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'write'. + /// + /// + /// + /// + public static void write(object self, int b, bool append) { -#if !FIRST_PASS - fd.write(b); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = FileOutputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanWrite == false) + throw new global::java.io.IOException("Write failed."); + + try + { + stream.WriteByte((byte)b); + stream.Flush(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void writeBytes(object _this, byte[] b, int off, int len, bool append, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'writeBytes'. + /// + /// + /// + /// + /// + /// + public static void writeBytes(object self, byte[] b, int off, int len, bool append) { -#if !FIRST_PASS - fd.writeBytes(b, off, len); +#if FIRST_PASS + throw new NotImplementedException(); +#else + if ((off < 0) || (off > b.Length) || (len < 0) || (len > (b.Length - off))) + throw new global::java.lang.IndexOutOfBoundsException(); + + var fd = FileOutputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanWrite == false) + throw new global::java.io.IOException("Write failed."); + + try + { + stream.Write(b, off, len); + stream.Flush(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void close0(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'close0'. + /// + /// + public static void close0(object self) { -#if !FIRST_PASS - fd.close(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = FileOutputStreamAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + return; + + try + { + FileDescriptorAccessor.SetStream(fd, null); + stream.Close(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } + /// + /// Implements the native method 'initIDs'. + /// public static void initIDs() { + } + } } \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/java/io/ObjectInputStream.cs b/src/IKVM.Runtime/Java/Externs/java/io/ObjectInputStream.cs index 81289dbd61..c2df1ca765 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/ObjectInputStream.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/ObjectInputStream.cs @@ -57,6 +57,7 @@ public static void bytesToDoubles(byte[] src, int srcpos, double[] dst, int dstp dst[dstpos++] = IKVM.Runtime.DoubleConverter.ToDouble(v, ref converter); } } + } } \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/java/io/ObjectStreamClass.cs b/src/IKVM.Runtime/Java/Externs/java/io/ObjectStreamClass.cs index c76c099c22..bbfe169003 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/ObjectStreamClass.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/ObjectStreamClass.cs @@ -27,423 +27,425 @@ Jeroen Frijters using System.Reflection.Emit; #endif using IKVM.Internal; +using IKVM.Runtime; namespace IKVM.Java.Externs.java.io { - static class ObjectStreamClass - { - - static class IOHelpers - { - - public static void WriteByte(byte[] buf, int offset, byte value) - { - buf[offset] = value; - } - - public static void WriteBoolean(byte[] buf, int offset, bool value) - { - buf[offset] = value ? (byte)1 : (byte)0; - } - - public static void WriteChar(byte[] buf, int offset, char value) - { - buf[offset + 0] = (byte)(value >> 8); - buf[offset + 1] = (byte)(value >> 0); - } - - public static void WriteShort(byte[] buf, int offset, short value) - { - buf[offset + 0] = (byte)(value >> 8); - buf[offset + 1] = (byte)(value >> 0); - } - - public static void WriteInt(byte[] buf, int offset, int value) - { - buf[offset + 0] = (byte)(value >> 24); - buf[offset + 1] = (byte)(value >> 16); - buf[offset + 2] = (byte)(value >> 8); - buf[offset + 3] = (byte)(value >> 0); - } - - public static void WriteFloat(byte[] buf, int offset, float value) - { -#if !FIRST_PASS && !STATIC_COMPILER - global::java.io.Bits.putFloat(buf, offset, value); + static class ObjectStreamClass + { + + static class IOHelpers + { + + public static void WriteByte(byte[] buf, int offset, byte value) + { + buf[offset] = value; + } + + public static void WriteBoolean(byte[] buf, int offset, bool value) + { + buf[offset] = value ? (byte)1 : (byte)0; + } + + public static void WriteChar(byte[] buf, int offset, char value) + { + buf[offset + 0] = (byte)(value >> 8); + buf[offset + 1] = (byte)(value >> 0); + } + + public static void WriteShort(byte[] buf, int offset, short value) + { + buf[offset + 0] = (byte)(value >> 8); + buf[offset + 1] = (byte)(value >> 0); + } + + public static void WriteInt(byte[] buf, int offset, int value) + { + buf[offset + 0] = (byte)(value >> 24); + buf[offset + 1] = (byte)(value >> 16); + buf[offset + 2] = (byte)(value >> 8); + buf[offset + 3] = (byte)(value >> 0); + } + + public static void WriteFloat(byte[] buf, int offset, float value) + { +#if !FIRST_PASS && !IMPORTER + global::java.io.Bits.putFloat(buf, offset, value); #endif - } - - public static void WriteLong(byte[] buf, int offset, long value) - { - WriteInt(buf, offset, (int)(value >> 32)); - WriteInt(buf, offset + 4, (int)value); - } - - public static void WriteDouble(byte[] buf, int offset, double value) - { -#if !FIRST_PASS && !STATIC_COMPILER - global::java.io.Bits.putDouble(buf, offset, value); + } + + public static void WriteLong(byte[] buf, int offset, long value) + { + WriteInt(buf, offset, (int)(value >> 32)); + WriteInt(buf, offset + 4, (int)value); + } + + public static void WriteDouble(byte[] buf, int offset, double value) + { +#if !FIRST_PASS && !IMPORTER + global::java.io.Bits.putDouble(buf, offset, value); #endif - } - - public static byte ReadByte(byte[] buf, int offset) - { - return buf[offset]; - } - - public static bool ReadBoolean(byte[] buf, int offset) - { - return buf[offset] != 0; - } - - public static char ReadChar(byte[] buf, int offset) - { - return (char)((buf[offset] << 8) + buf[offset + 1]); - } - - public static short ReadShort(byte[] buf, int offset) - { - return (short)((buf[offset] << 8) + buf[offset + 1]); - } - - public static int ReadInt(byte[] buf, int offset) - { - return (buf[offset + 0] << 24) - + (buf[offset + 1] << 16) - + (buf[offset + 2] << 8) - + (buf[offset + 3] << 0); - } - - public static float ReadFloat(byte[] buf, int offset) - { -#if FIRST_PASS || STATIC_COMPILER + } + + public static byte ReadByte(byte[] buf, int offset) + { + return buf[offset]; + } + + public static bool ReadBoolean(byte[] buf, int offset) + { + return buf[offset] != 0; + } + + public static char ReadChar(byte[] buf, int offset) + { + return (char)((buf[offset] << 8) + buf[offset + 1]); + } + + public static short ReadShort(byte[] buf, int offset) + { + return (short)((buf[offset] << 8) + buf[offset + 1]); + } + + public static int ReadInt(byte[] buf, int offset) + { + return (buf[offset + 0] << 24) + + (buf[offset + 1] << 16) + + (buf[offset + 2] << 8) + + (buf[offset + 3] << 0); + } + + public static float ReadFloat(byte[] buf, int offset) + { +#if FIRST_PASS || IMPORTER return 0; #else - return global::java.lang.Float.intBitsToFloat(ReadInt(buf, offset)); + return global::java.lang.Float.intBitsToFloat(ReadInt(buf, offset)); #endif - } - - public static long ReadLong(byte[] buf, int offset) - { - long hi = (uint)ReadInt(buf, offset); - long lo = (uint)ReadInt(buf, offset + 4); - return lo + (hi << 32); - } - - public static double ReadDouble(byte[] buf, int offset) - { -#if FIRST_PASS || STATIC_COMPILER + } + + public static long ReadLong(byte[] buf, int offset) + { + long hi = (uint)ReadInt(buf, offset); + long lo = (uint)ReadInt(buf, offset + 4); + return lo + (hi << 32); + } + + public static double ReadDouble(byte[] buf, int offset) + { +#if FIRST_PASS || IMPORTER return 0; #else - return global::java.lang.Double.longBitsToDouble(ReadLong(buf, offset)); + return global::java.lang.Double.longBitsToDouble(ReadLong(buf, offset)); #endif - } - } - - public static void initNative() - { - } - - public static bool isDynamicTypeWrapper(global::java.lang.Class cl) - { - TypeWrapper wrapper = TypeWrapper.FromClass(cl); - return !wrapper.IsFastClassLiteralSafe; - } - - public static bool hasStaticInitializer(global::java.lang.Class cl) - { - TypeWrapper wrapper = TypeWrapper.FromClass(cl); - try - { - wrapper.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - Type type = wrapper.TypeAsTBD; - if (!type.IsArray && type.TypeInitializer != null) - { - wrapper.RunClassInit(); - return !AttributeHelper.IsHideFromJava(type.TypeInitializer); - } - return false; - } + } + } + + public static void initNative() + { + } + + public static bool isDynamicTypeWrapper(global::java.lang.Class cl) + { + TypeWrapper wrapper = TypeWrapper.FromClass(cl); + return !wrapper.IsFastClassLiteralSafe; + } + + public static bool hasStaticInitializer(global::java.lang.Class cl) + { + TypeWrapper wrapper = TypeWrapper.FromClass(cl); + try + { + wrapper.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + Type type = wrapper.TypeAsTBD; + if (!type.IsArray && type.TypeInitializer != null) + { + wrapper.RunClassInit(); + return !AttributeHelper.IsHideFromJava(type.TypeInitializer); + } + return false; + } #if !FIRST_PASS && !NO_REF_EMIT - private sealed class FastFieldReflector : global::ikvm.@internal.FieldReflectorBase - { - private static readonly MethodInfo ReadByteMethod = typeof(IOHelpers).GetMethod("ReadByte"); - private static readonly MethodInfo ReadBooleanMethod = typeof(IOHelpers).GetMethod("ReadBoolean"); - private static readonly MethodInfo ReadCharMethod = typeof(IOHelpers).GetMethod("ReadChar"); - private static readonly MethodInfo ReadShortMethod = typeof(IOHelpers).GetMethod("ReadShort"); - private static readonly MethodInfo ReadIntMethod = typeof(IOHelpers).GetMethod("ReadInt"); - private static readonly MethodInfo ReadFloatMethod = typeof(IOHelpers).GetMethod("ReadFloat"); - private static readonly MethodInfo ReadLongMethod = typeof(IOHelpers).GetMethod("ReadLong"); - private static readonly MethodInfo ReadDoubleMethod = typeof(IOHelpers).GetMethod("ReadDouble"); - private static readonly MethodInfo WriteByteMethod = typeof(IOHelpers).GetMethod("WriteByte"); - private static readonly MethodInfo WriteBooleanMethod = typeof(IOHelpers).GetMethod("WriteBoolean"); - private static readonly MethodInfo WriteCharMethod = typeof(IOHelpers).GetMethod("WriteChar"); - private static readonly MethodInfo WriteShortMethod = typeof(IOHelpers).GetMethod("WriteShort"); - private static readonly MethodInfo WriteIntMethod = typeof(IOHelpers).GetMethod("WriteInt"); - private static readonly MethodInfo WriteFloatMethod = typeof(IOHelpers).GetMethod("WriteFloat"); - private static readonly MethodInfo WriteLongMethod = typeof(IOHelpers).GetMethod("WriteLong"); - private static readonly MethodInfo WriteDoubleMethod = typeof(IOHelpers).GetMethod("WriteDouble"); - private delegate void ObjFieldGetterSetter(object obj, object[] objarr); - private delegate void PrimFieldGetterSetter(object obj, byte[] objarr); - private static readonly ObjFieldGetterSetter objDummy = new ObjFieldGetterSetter(Dummy); - private static readonly PrimFieldGetterSetter primDummy = new PrimFieldGetterSetter(Dummy); - private global::java.io.ObjectStreamField[] fields; - private ObjFieldGetterSetter objFieldGetter; - private PrimFieldGetterSetter primFieldGetter; - private ObjFieldGetterSetter objFieldSetter; - private PrimFieldGetterSetter primFieldSetter; - - private static void Dummy(object obj, object[] objarr) - { - } - - private static void Dummy(object obj, byte[] barr) - { - } - - internal FastFieldReflector(global::java.io.ObjectStreamField[] fields) - { - this.fields = fields; - TypeWrapper tw = null; - foreach (global::java.io.ObjectStreamField field in fields) - { - FieldWrapper fw = GetFieldWrapper(field); - if (fw != null) - { - if (tw == null) - { - tw = fw.DeclaringType; - } - else if (tw != fw.DeclaringType) - { - // pre-condition is that all fields are from the same Type! - throw new global::java.lang.InternalError(); - } - } - } - if (tw == null) - { - objFieldGetter = objFieldSetter = objDummy; - primFieldGetter = primFieldSetter = primDummy; - } - else - { - try - { - tw.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - DynamicMethod dmObjGetter = DynamicMethodUtils.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) }); - DynamicMethod dmPrimGetter = DynamicMethodUtils.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) }); - DynamicMethod dmObjSetter = DynamicMethodUtils.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) }); - DynamicMethod dmPrimSetter = DynamicMethodUtils.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) }); - CodeEmitter ilgenObjGetter = CodeEmitter.Create(dmObjGetter); - CodeEmitter ilgenPrimGetter = CodeEmitter.Create(dmPrimGetter); - CodeEmitter ilgenObjSetter = CodeEmitter.Create(dmObjSetter); - CodeEmitter ilgenPrimSetter = CodeEmitter.Create(dmPrimSetter); - - // we want the getters to be verifiable (because writeObject can be used from partial trust), - // so we create a local to hold the properly typed object reference - CodeEmitterLocal objGetterThis = ilgenObjGetter.DeclareLocal(tw.TypeAsBaseType); - CodeEmitterLocal primGetterThis = ilgenPrimGetter.DeclareLocal(tw.TypeAsBaseType); - ilgenObjGetter.Emit(OpCodes.Ldarg_0); - ilgenObjGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); - ilgenObjGetter.Emit(OpCodes.Stloc, objGetterThis); - ilgenPrimGetter.Emit(OpCodes.Ldarg_0); - ilgenPrimGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); - ilgenPrimGetter.Emit(OpCodes.Stloc, primGetterThis); - - foreach (global::java.io.ObjectStreamField field in fields) - { - FieldWrapper fw = GetFieldWrapper(field); - if (fw == null) - { - continue; - } - fw.ResolveField(); - TypeWrapper fieldType = fw.FieldTypeWrapper; - try - { - fieldType = fieldType.EnsureLoadable(tw.GetClassLoader()); - fieldType.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - if (fieldType.IsPrimitive) - { - // Getter - ilgenPrimGetter.Emit(OpCodes.Ldarg_1); - ilgenPrimGetter.EmitLdc_I4(field.getOffset()); - ilgenPrimGetter.Emit(OpCodes.Ldloc, primGetterThis); - fw.EmitGet(ilgenPrimGetter); - if (fieldType == PrimitiveTypeWrapper.BYTE) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteByteMethod); - } - else if (fieldType == PrimitiveTypeWrapper.BOOLEAN) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteBooleanMethod); - } - else if (fieldType == PrimitiveTypeWrapper.CHAR) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteCharMethod); - } - else if (fieldType == PrimitiveTypeWrapper.SHORT) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteShortMethod); - } - else if (fieldType == PrimitiveTypeWrapper.INT) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteIntMethod); - } - else if (fieldType == PrimitiveTypeWrapper.FLOAT) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteFloatMethod); - } - else if (fieldType == PrimitiveTypeWrapper.LONG) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteLongMethod); - } - else if (fieldType == PrimitiveTypeWrapper.DOUBLE) - { - ilgenPrimGetter.Emit(OpCodes.Call, WriteDoubleMethod); - } - else - { - throw new global::java.lang.InternalError(); - } - - // Setter - ilgenPrimSetter.Emit(OpCodes.Ldarg_0); - ilgenPrimSetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); - ilgenPrimSetter.Emit(OpCodes.Ldarg_1); - ilgenPrimSetter.EmitLdc_I4(field.getOffset()); - if (fieldType == PrimitiveTypeWrapper.BYTE) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadByteMethod); - } - else if (fieldType == PrimitiveTypeWrapper.BOOLEAN) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadBooleanMethod); - } - else if (fieldType == PrimitiveTypeWrapper.CHAR) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadCharMethod); - } - else if (fieldType == PrimitiveTypeWrapper.SHORT) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadShortMethod); - } - else if (fieldType == PrimitiveTypeWrapper.INT) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadIntMethod); - } - else if (fieldType == PrimitiveTypeWrapper.FLOAT) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadFloatMethod); - } - else if (fieldType == PrimitiveTypeWrapper.LONG) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadLongMethod); - } - else if (fieldType == PrimitiveTypeWrapper.DOUBLE) - { - ilgenPrimSetter.Emit(OpCodes.Call, ReadDoubleMethod); - } - else - { - throw new global::java.lang.InternalError(); - } - fw.EmitSet(ilgenPrimSetter); - } - else - { - // Getter - ilgenObjGetter.Emit(OpCodes.Ldarg_1); - ilgenObjGetter.EmitLdc_I4(field.getOffset()); - ilgenObjGetter.Emit(OpCodes.Ldloc, objGetterThis); - fw.EmitGet(ilgenObjGetter); - fieldType.EmitConvSignatureTypeToStackType(ilgenObjGetter); - ilgenObjGetter.Emit(OpCodes.Stelem_Ref); - - // Setter - ilgenObjSetter.Emit(OpCodes.Ldarg_0); - ilgenObjSetter.Emit(OpCodes.Ldarg_1); - ilgenObjSetter.EmitLdc_I4(field.getOffset()); - ilgenObjSetter.Emit(OpCodes.Ldelem_Ref); - fieldType.EmitCheckcast(ilgenObjSetter); - fieldType.EmitConvStackTypeToSignatureType(ilgenObjSetter, null); - fw.EmitSet(ilgenObjSetter); - } - } - ilgenObjGetter.Emit(OpCodes.Ret); - ilgenPrimGetter.Emit(OpCodes.Ret); - ilgenObjSetter.Emit(OpCodes.Ret); - ilgenPrimSetter.Emit(OpCodes.Ret); - ilgenObjGetter.DoEmit(); - ilgenPrimGetter.DoEmit(); - ilgenObjSetter.DoEmit(); - ilgenPrimSetter.DoEmit(); - objFieldGetter = (ObjFieldGetterSetter)dmObjGetter.CreateDelegate(typeof(ObjFieldGetterSetter)); - primFieldGetter = (PrimFieldGetterSetter)dmPrimGetter.CreateDelegate(typeof(PrimFieldGetterSetter)); - objFieldSetter = (ObjFieldGetterSetter)dmObjSetter.CreateDelegate(typeof(ObjFieldGetterSetter)); - primFieldSetter = (PrimFieldGetterSetter)dmPrimSetter.CreateDelegate(typeof(PrimFieldGetterSetter)); - } - } - - private static FieldWrapper GetFieldWrapper(global::java.io.ObjectStreamField field) - { - global::java.lang.reflect.Field f = field.getField(); - return f == null ? null : FieldWrapper.FromField(f); - } - - public override global::java.io.ObjectStreamField[] getFields() - { - return fields; - } - - public override void getObjFieldValues(object obj, object[] objarr) - { - objFieldGetter(obj, objarr); - } - - public override void setObjFieldValues(object obj, object[] objarr) - { - objFieldSetter(obj, objarr); - } - - public override void getPrimFieldValues(object obj, byte[] barr) - { - primFieldGetter(obj, barr); - } - - public override void setPrimFieldValues(object obj, byte[] barr) - { - primFieldSetter(obj, barr); - } - } + private sealed class FastFieldReflector : global::ikvm.@internal.FieldReflectorBase + { + private static readonly MethodInfo ReadByteMethod = typeof(IOHelpers).GetMethod("ReadByte"); + private static readonly MethodInfo ReadBooleanMethod = typeof(IOHelpers).GetMethod("ReadBoolean"); + private static readonly MethodInfo ReadCharMethod = typeof(IOHelpers).GetMethod("ReadChar"); + private static readonly MethodInfo ReadShortMethod = typeof(IOHelpers).GetMethod("ReadShort"); + private static readonly MethodInfo ReadIntMethod = typeof(IOHelpers).GetMethod("ReadInt"); + private static readonly MethodInfo ReadFloatMethod = typeof(IOHelpers).GetMethod("ReadFloat"); + private static readonly MethodInfo ReadLongMethod = typeof(IOHelpers).GetMethod("ReadLong"); + private static readonly MethodInfo ReadDoubleMethod = typeof(IOHelpers).GetMethod("ReadDouble"); + private static readonly MethodInfo WriteByteMethod = typeof(IOHelpers).GetMethod("WriteByte"); + private static readonly MethodInfo WriteBooleanMethod = typeof(IOHelpers).GetMethod("WriteBoolean"); + private static readonly MethodInfo WriteCharMethod = typeof(IOHelpers).GetMethod("WriteChar"); + private static readonly MethodInfo WriteShortMethod = typeof(IOHelpers).GetMethod("WriteShort"); + private static readonly MethodInfo WriteIntMethod = typeof(IOHelpers).GetMethod("WriteInt"); + private static readonly MethodInfo WriteFloatMethod = typeof(IOHelpers).GetMethod("WriteFloat"); + private static readonly MethodInfo WriteLongMethod = typeof(IOHelpers).GetMethod("WriteLong"); + private static readonly MethodInfo WriteDoubleMethod = typeof(IOHelpers).GetMethod("WriteDouble"); + private delegate void ObjFieldGetterSetter(object obj, object[] objarr); + private delegate void PrimFieldGetterSetter(object obj, byte[] objarr); + private static readonly ObjFieldGetterSetter objDummy = new ObjFieldGetterSetter(Dummy); + private static readonly PrimFieldGetterSetter primDummy = new PrimFieldGetterSetter(Dummy); + private global::java.io.ObjectStreamField[] fields; + private ObjFieldGetterSetter objFieldGetter; + private PrimFieldGetterSetter primFieldGetter; + private ObjFieldGetterSetter objFieldSetter; + private PrimFieldGetterSetter primFieldSetter; + + private static void Dummy(object obj, object[] objarr) + { + } + + private static void Dummy(object obj, byte[] barr) + { + } + + internal FastFieldReflector(global::java.io.ObjectStreamField[] fields) + { + this.fields = fields; + TypeWrapper tw = null; + foreach (global::java.io.ObjectStreamField field in fields) + { + FieldWrapper fw = GetFieldWrapper(field); + if (fw != null) + { + if (tw == null) + { + tw = fw.DeclaringType; + } + else if (tw != fw.DeclaringType) + { + // pre-condition is that all fields are from the same Type! + throw new global::java.lang.InternalError(); + } + } + } + if (tw == null) + { + objFieldGetter = objFieldSetter = objDummy; + primFieldGetter = primFieldSetter = primDummy; + } + else + { + try + { + tw.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + + var dmObjGetter = DynamicMethodUtil.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) }); + var dmPrimGetter = DynamicMethodUtil.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) }); + var dmObjSetter = DynamicMethodUtil.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) }); + var dmPrimSetter = DynamicMethodUtil.Create("__", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) }); + var ilgenObjGetter = CodeEmitter.Create(dmObjGetter); + var ilgenPrimGetter = CodeEmitter.Create(dmPrimGetter); + var ilgenObjSetter = CodeEmitter.Create(dmObjSetter); + var ilgenPrimSetter = CodeEmitter.Create(dmPrimSetter); + + // we want the getters to be verifiable (because writeObject can be used from partial trust), + // so we create a local to hold the properly typed object reference + var objGetterThis = ilgenObjGetter.DeclareLocal(tw.TypeAsBaseType); + var primGetterThis = ilgenPrimGetter.DeclareLocal(tw.TypeAsBaseType); + ilgenObjGetter.Emit(OpCodes.Ldarg_0); + ilgenObjGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); + ilgenObjGetter.Emit(OpCodes.Stloc, objGetterThis); + ilgenPrimGetter.Emit(OpCodes.Ldarg_0); + ilgenPrimGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); + ilgenPrimGetter.Emit(OpCodes.Stloc, primGetterThis); + + foreach (global::java.io.ObjectStreamField field in fields) + { + FieldWrapper fw = GetFieldWrapper(field); + if (fw == null) + { + continue; + } + fw.ResolveField(); + TypeWrapper fieldType = fw.FieldTypeWrapper; + try + { + fieldType = fieldType.EnsureLoadable(tw.GetClassLoader()); + fieldType.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + if (fieldType.IsPrimitive) + { + // Getter + ilgenPrimGetter.Emit(OpCodes.Ldarg_1); + ilgenPrimGetter.EmitLdc_I4(field.getOffset()); + ilgenPrimGetter.Emit(OpCodes.Ldloc, primGetterThis); + fw.EmitGet(ilgenPrimGetter); + if (fieldType == PrimitiveTypeWrapper.BYTE) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteByteMethod); + } + else if (fieldType == PrimitiveTypeWrapper.BOOLEAN) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteBooleanMethod); + } + else if (fieldType == PrimitiveTypeWrapper.CHAR) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteCharMethod); + } + else if (fieldType == PrimitiveTypeWrapper.SHORT) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteShortMethod); + } + else if (fieldType == PrimitiveTypeWrapper.INT) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteIntMethod); + } + else if (fieldType == PrimitiveTypeWrapper.FLOAT) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteFloatMethod); + } + else if (fieldType == PrimitiveTypeWrapper.LONG) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteLongMethod); + } + else if (fieldType == PrimitiveTypeWrapper.DOUBLE) + { + ilgenPrimGetter.Emit(OpCodes.Call, WriteDoubleMethod); + } + else + { + throw new global::java.lang.InternalError(); + } + + // Setter + ilgenPrimSetter.Emit(OpCodes.Ldarg_0); + ilgenPrimSetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType); + ilgenPrimSetter.Emit(OpCodes.Ldarg_1); + ilgenPrimSetter.EmitLdc_I4(field.getOffset()); + if (fieldType == PrimitiveTypeWrapper.BYTE) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadByteMethod); + } + else if (fieldType == PrimitiveTypeWrapper.BOOLEAN) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadBooleanMethod); + } + else if (fieldType == PrimitiveTypeWrapper.CHAR) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadCharMethod); + } + else if (fieldType == PrimitiveTypeWrapper.SHORT) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadShortMethod); + } + else if (fieldType == PrimitiveTypeWrapper.INT) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadIntMethod); + } + else if (fieldType == PrimitiveTypeWrapper.FLOAT) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadFloatMethod); + } + else if (fieldType == PrimitiveTypeWrapper.LONG) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadLongMethod); + } + else if (fieldType == PrimitiveTypeWrapper.DOUBLE) + { + ilgenPrimSetter.Emit(OpCodes.Call, ReadDoubleMethod); + } + else + { + throw new global::java.lang.InternalError(); + } + fw.EmitSet(ilgenPrimSetter); + } + else + { + // Getter + ilgenObjGetter.Emit(OpCodes.Ldarg_1); + ilgenObjGetter.EmitLdc_I4(field.getOffset()); + ilgenObjGetter.Emit(OpCodes.Ldloc, objGetterThis); + fw.EmitGet(ilgenObjGetter); + fieldType.EmitConvSignatureTypeToStackType(ilgenObjGetter); + ilgenObjGetter.Emit(OpCodes.Stelem_Ref); + + // Setter + ilgenObjSetter.Emit(OpCodes.Ldarg_0); + ilgenObjSetter.Emit(OpCodes.Ldarg_1); + ilgenObjSetter.EmitLdc_I4(field.getOffset()); + ilgenObjSetter.Emit(OpCodes.Ldelem_Ref); + fieldType.EmitCheckcast(ilgenObjSetter); + fieldType.EmitConvStackTypeToSignatureType(ilgenObjSetter, null); + fw.EmitSet(ilgenObjSetter); + } + } + ilgenObjGetter.Emit(OpCodes.Ret); + ilgenPrimGetter.Emit(OpCodes.Ret); + ilgenObjSetter.Emit(OpCodes.Ret); + ilgenPrimSetter.Emit(OpCodes.Ret); + ilgenObjGetter.DoEmit(); + ilgenPrimGetter.DoEmit(); + ilgenObjSetter.DoEmit(); + ilgenPrimSetter.DoEmit(); + objFieldGetter = (ObjFieldGetterSetter)dmObjGetter.CreateDelegate(typeof(ObjFieldGetterSetter)); + primFieldGetter = (PrimFieldGetterSetter)dmPrimGetter.CreateDelegate(typeof(PrimFieldGetterSetter)); + objFieldSetter = (ObjFieldGetterSetter)dmObjSetter.CreateDelegate(typeof(ObjFieldGetterSetter)); + primFieldSetter = (PrimFieldGetterSetter)dmPrimSetter.CreateDelegate(typeof(PrimFieldGetterSetter)); + } + } + + private static FieldWrapper GetFieldWrapper(global::java.io.ObjectStreamField field) + { + global::java.lang.reflect.Field f = field.getField(); + return f == null ? null : FieldWrapper.FromField(f); + } + + public override global::java.io.ObjectStreamField[] getFields() + { + return fields; + } + + public override void getObjFieldValues(object obj, object[] objarr) + { + objFieldGetter(obj, objarr); + } + + public override void setObjFieldValues(object obj, object[] objarr) + { + objFieldSetter(obj, objarr); + } + + public override void getPrimFieldValues(object obj, byte[] barr) + { + primFieldGetter(obj, barr); + } + + public override void setPrimFieldValues(object obj, byte[] barr) + { + primFieldSetter(obj, barr); + } + } #endif // !FIRST_PASS && !NO_REF_EMIT - public static object getFastFieldReflector(global::java.io.ObjectStreamField[] fieldsObj) - { + public static object getFastFieldReflector(global::java.io.ObjectStreamField[] fieldsObj) + { #if FIRST_PASS || NO_REF_EMIT return null; #else - return new FastFieldReflector(fieldsObj); + return new FastFieldReflector(fieldsObj); #endif - } - } + } + } } \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/java/io/RandomAccessFile.cs b/src/IKVM.Runtime/Java/Externs/java/io/RandomAccessFile.cs index 7adfbc4a02..510d4527d5 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/RandomAccessFile.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/RandomAccessFile.cs @@ -1,122 +1,478 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System.Runtime.InteropServices; +using System; +using System.IO; +using System.Security; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Vfs; namespace IKVM.Java.Externs.java.io { + /// + /// Implements the native methods for 'RandomAccessFile'. + /// static class RandomAccessFile { - public static void open0(object _this, string name, int mode, [In] global::java.io.FileDescriptor fd, [In] int O_RDWR) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + + static RandomAccessFileAccessor randomAccessFileAccessor; + static RandomAccessFileAccessor RandomAccessFileAccessor => JVM.BaseAccessors.Get(ref randomAccessFileAccessor); + +#endif + + const int O_RDONLY = 1; + const int O_RDWR = 2; + const int O_SYNC = 4; + const int O_DSYNC = 8; + + /// + /// Implements the native method 'open0'. + /// + /// + /// + /// + public static void open0(object self, string name, int mode) { -#if !FIRST_PASS - if ((mode & O_RDWR) == O_RDWR) +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + var fileMode = (FileMode)0; + if ((mode & O_RDONLY) == O_RDONLY) + fileMode |= FileMode.Open; + if ((mode & O_RDWR) == O_RDWR) + fileMode |= FileMode.OpenOrCreate; + + var fileAccess = (FileAccess)0; + if ((mode & O_RDONLY) == O_RDONLY) + fileAccess |= FileAccess.Read; + if ((mode & O_RDWR) == O_RDWR) + fileAccess |= FileAccess.ReadWrite; + + + if (VfsTable.Default.IsPath(name)) + { + FileDescriptorAccessor.SetStream(fd, VfsTable.Default.Open(name, fileMode, fileAccess)); + return; + } + + FileDescriptorAccessor.SetStream(fd, new FileStream(name, fileMode, fileAccess, FileShare.ReadWrite, 1, false)); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (ArgumentException e) + { + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (SecurityException e) + { + throw new global::java.lang.SecurityException(e.Message); + } + catch (UnauthorizedAccessException) { - fd.openReadWrite(name); + throw new global::java.io.FileNotFoundException(name + " (Access is denied)"); } - else + catch (IOException e) { - fd.openReadOnly(name); + throw new global::java.io.FileNotFoundException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.FileNotFoundException(e.Message); } #endif } - public static int read0(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'read0'. + /// + /// + /// + public static int read0(object self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.read(); + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanRead == false) + throw new global::java.io.IOException("Read failed."); + + try + { + return stream.ReadByte(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static int readBytes(object _this, byte[] b, int off, int len, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'readBytes'. + /// + /// + /// + /// + /// + /// + public static int readBytes(object self, byte[] b, int off, int len) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.readBytes(b, off, len); + if (b == null) + throw new global::java.lang.NullPointerException(); + + if ((off < 0) || (off > b.Length) || (len < 0) || (len > (b.Length - off))) + throw new global::java.lang.IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanRead == false) + throw new global::java.io.IOException("Read failed."); + + try + { + var n = stream.Read(b, off, len); + if (n == 0) + n = -1; + + return n; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void write0(object _this, int b, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'write'. + /// + /// + /// + public static void write0(object self, int b) { -#if !FIRST_PASS - fd.write(b); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanWrite == false) + throw new global::java.io.IOException("Write failed."); + + try + { + stream.WriteByte((byte)b); + stream.Flush(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void writeBytes(object _this, byte[] b, int off, int len, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'writeBytes'. + /// + /// + /// + /// + /// + public static void writeBytes(object self, byte[] b, int off, int len) { -#if !FIRST_PASS - fd.writeBytes(b, off, len); +#if FIRST_PASS + throw new NotImplementedException(); +#else + if ((off < 0) || (off > b.Length) || (len < 0) || (len > (b.Length - off))) + throw new global::java.lang.IndexOutOfBoundsException(); + + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + if (stream.CanWrite == false) + throw new global::java.io.IOException("Write failed."); + + try + { + stream.Write(b, off, len); + stream.Flush(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static long getFilePointer(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'getFilePointer'. + /// + /// + /// + /// + public static long getFilePointer(object self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.getFilePointer(); + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + return stream.Position; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void seek0(object _this, long pos, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'seek0'. + /// + /// + /// + public static void seek0(object self, long pos) { -#if !FIRST_PASS - fd.seek(pos); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + stream.Position = pos; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (ArgumentOutOfRangeException) + { + throw new global::java.io.IOException("Negative seek offset."); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static long length(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'length'. + /// + /// + /// + public static long length(object self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - return fd.length(); + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + return stream.Length; + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void setLength(object _this, long newLength, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'setLength'. + /// + /// + /// + public static void setLength(object self, long newLength) { -#if !FIRST_PASS - fd.setLength(newLength); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + stream.SetLength(newLength); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } - public static void close0(object _this, [In] global::java.io.FileDescriptor fd) + /// + /// Implements the native method 'close0'. + /// + /// + public static void close0(object self) { -#if !FIRST_PASS - fd.close(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fd = RandomAccessFileAccessor.GetFd(self); + if (fd == null) + throw new global::java.io.IOException("Stream closed."); + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + return; + + try + { + FileDescriptorAccessor.SetStream(fd, null); + stream.Close(); + } + catch (ObjectDisposedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } #endif } + /// + /// Implements the native method 'initIDs'. + /// public static void initIDs() { + } + } -} \ No newline at end of file +} diff --git a/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs b/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs index 7984ae483d..cea01b3998 100644 --- a/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs +++ b/src/IKVM.Runtime/Java/Externs/java/io/WinNTFileSystem.cs @@ -226,7 +226,7 @@ public static bool checkAccess(object _this, global::java.io.File f, int access) // check the VFS for the file if (VfsTable.Default.IsPath(path)) { - return VfsTable.Default.GetPath(path) switch + return VfsTable.Default.GetEntry(path) switch { VfsFile file => access == ACCESS_READ && file.CanOpen(FileMode.Open, FileAccess.Read), VfsDirectory => true, @@ -358,7 +358,7 @@ public static long getLength(object _this, global::java.io.File f) { var path = GetPathFromFile(f); if (VfsTable.Default.IsPath(path)) - return VfsTable.Default.GetPath(path) is VfsFile file ? file.Size : 0; + return VfsTable.Default.GetEntry(path) is VfsFile file ? file.Size : 0; return new FileInfo(path).Length; } @@ -515,7 +515,7 @@ public static string[] list(object _this, global::java.io.File f) var path = GetPathFromFile(f); if (VfsTable.Default.IsPath(path)) { - if (VfsTable.Default.GetPath(path) is VfsDirectory vfs) + if (VfsTable.Default.GetEntry(path) is VfsDirectory vfs) return vfs.List(); throw new DirectoryNotFoundException(); diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/Class.cs b/src/IKVM.Runtime/Java/Externs/java/lang/Class.cs index 49a9b45188..0ab3acb4c4 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/Class.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/Class.cs @@ -25,32 +25,56 @@ Jeroen Frijters using System.Collections.Generic; using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; namespace IKVM.Java.Externs.java.lang { + /// + /// Implements the native methods for 'java.lang.Class'. + /// static class Class { +#if FIRST_PASS == false + + static ClassLoaderAccessor classLoaderAccessor; + + static ClassLoaderAccessor ClassLoaderAccessor => JVM.BaseAccessors.Get(ref classLoaderAccessor); + +#endif + + /// + /// Implements the native method 'forName0'. + /// + /// + /// + /// + /// + /// + /// + /// public static global::java.lang.Class forName0(string name, bool initialize, global::java.lang.ClassLoader loader, global::java.lang.Class caller) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - //Console.WriteLine("forName: " + name + ", loader = " + loader); + if (name == null) + throw new global::java.lang.NullPointerException(); + TypeWrapper tw = null; if (name.IndexOf(',') > 0) { // we essentially require full trust before allowing arbitrary types to be loaded, // hence we do the "createClassLoader" permission check - global::java.lang.SecurityManager sm = global::java.lang.System.getSecurityManager(); - if (sm != null) - sm.checkPermission(new global::java.lang.RuntimePermission("createClassLoader")); - Type type = Type.GetType(name); + var sm = global::java.lang.System.getSecurityManager(); + sm?.checkPermission(new global::java.lang.RuntimePermission("createClassLoader")); + + var type = Type.GetType(name); if (type != null) - { tw = ClassLoaderWrapper.GetWrapperFromType(type); - } + if (tw == null) { global::java.lang.Throwable.suppressFillInStackTrace = true; @@ -61,8 +85,7 @@ static class Class { try { - ClassLoaderWrapper classLoaderWrapper = ClassLoaderWrapper.GetClassLoaderWrapper(loader); - tw = classLoaderWrapper.LoadClassByDottedName(name); + tw = ClassLoaderWrapper.GetClassLoaderWrapper(loader).LoadClassByDottedName(name); } catch (ClassNotFoundException x) { @@ -78,12 +101,11 @@ static class Class throw x.ToJava(); } } - global::java.security.ProtectionDomain pd; - if (loader != null && caller != null && (pd = getProtectionDomain0(caller)) != null) - { - loader.checkPackageAccess(tw.ClassObject, pd); - } - if (initialize && !tw.IsArray) + + if (loader != null && caller != null && getProtectionDomain0(caller) is global::java.security.ProtectionDomain pd) + ClassLoaderAccessor.InvokeCheckPackageAccess(loader, tw.ClassObject, pd); + + if (initialize && tw.IsArray == false) { try { @@ -93,21 +115,30 @@ static class Class { throw x.ToJava(); } + tw.RunClassInit(); } + return tw.ClassObject; #endif } + /// + /// Implements the native method 'getRawTypeAnnotations'. + /// + /// + /// public static byte[] getRawTypeAnnotations(global::java.lang.Class thisClass) { return TypeWrapper.FromClass(thisClass).GetRawTypeAnnotations(); } #if !FIRST_PASS - private sealed class ConstantPoolImpl : global::sun.reflect.ConstantPool + + sealed class ConstantPoolImpl : global::sun.reflect.ConstantPool { - private readonly object[] constantPool; + + readonly object[] constantPool; internal ConstantPoolImpl(object[] constantPool) { @@ -139,36 +170,60 @@ public override double getDoubleAt(int index) return (double)constantPool[index]; } } + #endif - public static object getConstantPool(global::java.lang.Class thisClass) + /// + /// Implements the native method 'getConstantPool'. + /// + /// + /// + public static object getConstantPool(global::java.lang.Class self) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - return new ConstantPoolImpl(TypeWrapper.FromClass(thisClass).GetConstantPool()); + return new ConstantPoolImpl(TypeWrapper.FromClass(self).GetConstantPool()); #endif } - public static bool isInstance(global::java.lang.Class thisClass, object obj) + /// + /// Implements the native method 'isInstance'. + /// + /// + /// + /// + public static bool isInstance(global::java.lang.Class self, object obj) { - return TypeWrapper.FromClass(thisClass).IsInstance(obj); + return TypeWrapper.FromClass(self).IsInstance(obj); } - public static bool isAssignableFrom(global::java.lang.Class thisClass, global::java.lang.Class otherClass) + /// + /// Implements the native method 'isAssignableFrom'. + /// + /// + /// + /// + /// + public static bool isAssignableFrom(global::java.lang.Class self, global::java.lang.Class otherClass) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else if (otherClass == null) - { throw new global::java.lang.NullPointerException(); - } #endif - return TypeWrapper.FromClass(otherClass).IsAssignableTo(TypeWrapper.FromClass(thisClass)); + return TypeWrapper.FromClass(otherClass).IsAssignableTo(TypeWrapper.FromClass(self)); } - public static bool isInterface(global::java.lang.Class thisClass) + /// + /// Implements the native method 'isInterface'. + /// + /// + /// + public static bool isInterface(global::java.lang.Class self) { - return TypeWrapper.FromClass(thisClass).IsInterface; + return TypeWrapper.FromClass(self).IsInterface; } public static bool isArray(global::java.lang.Class thisClass) @@ -183,46 +238,29 @@ public static bool isPrimitive(global::java.lang.Class thisClass) public static string getName0(global::java.lang.Class thisClass) { - TypeWrapper tw = TypeWrapper.FromClass(thisClass); + var tw = TypeWrapper.FromClass(thisClass); if (tw.IsPrimitive) { if (tw == PrimitiveTypeWrapper.BYTE) - { return "byte"; - } else if (tw == PrimitiveTypeWrapper.CHAR) - { return "char"; - } else if (tw == PrimitiveTypeWrapper.DOUBLE) - { return "double"; - } else if (tw == PrimitiveTypeWrapper.FLOAT) - { return "float"; - } else if (tw == PrimitiveTypeWrapper.INT) - { return "int"; - } else if (tw == PrimitiveTypeWrapper.LONG) - { return "long"; - } else if (tw == PrimitiveTypeWrapper.SHORT) - { return "short"; - } else if (tw == PrimitiveTypeWrapper.BOOLEAN) - { return "boolean"; - } else if (tw == PrimitiveTypeWrapper.VOID) - { return "void"; - } } + if (tw.IsUnsafeAnonymous) { #if !FIRST_PASS @@ -246,28 +284,27 @@ public static string getSigName(global::java.lang.Class thisClass) public static global::java.lang.Class getSuperclass(global::java.lang.Class thisClass) { - TypeWrapper super = TypeWrapper.FromClass(thisClass).BaseTypeWrapper; + var super = TypeWrapper.FromClass(thisClass).BaseTypeWrapper; return super != null ? super.ClassObject : null; } public static global::java.lang.Class[] getInterfaces0(global::java.lang.Class thisClass) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - TypeWrapper[] ifaces = TypeWrapper.FromClass(thisClass).Interfaces; - global::java.lang.Class[] interfaces = new global::java.lang.Class[ifaces.Length]; + var ifaces = TypeWrapper.FromClass(thisClass).Interfaces; + var interfaces = new global::java.lang.Class[ifaces.Length]; for (int i = 0; i < ifaces.Length; i++) - { interfaces[i] = ifaces[i].ClassObject; - } + return interfaces; #endif } public static global::java.lang.Class getComponentType(global::java.lang.Class thisClass) { - TypeWrapper tw = TypeWrapper.FromClass(thisClass); + var tw = TypeWrapper.FromClass(thisClass); return tw.IsArray ? tw.ElementTypeWrapper.ClassObject : null; } @@ -282,7 +319,7 @@ public static int getModifiers(global::java.lang.Class thisClass) public static object[] getSigners(global::java.lang.Class thisClass) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else return thisClass.signers; #endif @@ -299,13 +336,12 @@ public static object[] getEnclosingMethod0(global::java.lang.Class thisClass) { try { - TypeWrapper tw = TypeWrapper.FromClass(thisClass); + var tw = TypeWrapper.FromClass(thisClass); tw.Finish(); - string[] enc = tw.GetEnclosingMethod(); + var enc = tw.GetEnclosingMethod(); if (enc == null) - { return null; - } + return new object[] { tw.GetClassLoader().LoadClassByDottedName(enc[0]).ClassObject, enc[1], enc[2] == null ? null : enc[2].Replace('.', '/') }; } catch (RetargetableJavaException x) @@ -318,18 +354,16 @@ public static object[] getEnclosingMethod0(global::java.lang.Class thisClass) { try { - TypeWrapper wrapper = TypeWrapper.FromClass(thisClass); + var wrapper = TypeWrapper.FromClass(thisClass); wrapper.Finish(); - TypeWrapper decl = wrapper.DeclaringTypeWrapper; + var decl = wrapper.DeclaringTypeWrapper; if (decl == null) - { return null; - } + decl = decl.EnsureLoadable(wrapper.GetClassLoader()); if (!decl.IsAccessibleFrom(wrapper)) - { throw new IllegalAccessError(string.Format("tried to access class {0} from class {1}", decl.Name, wrapper.Name)); - } + decl.Finish(); TypeWrapper[] declInner = decl.InnerClasses; for (int i = 0; i < declInner.Length; i++) @@ -350,23 +384,20 @@ public static object[] getEnclosingMethod0(global::java.lang.Class thisClass) public static global::java.security.ProtectionDomain getProtectionDomain0(global::java.lang.Class thisClass) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - TypeWrapper wrapper = TypeWrapper.FromClass(thisClass); + var wrapper = TypeWrapper.FromClass(thisClass); if (wrapper.IsArray) - { return null; - } - global::java.security.ProtectionDomain pd = wrapper.ClassObject.pd; + + var pd = wrapper.ClassObject.pd; if (pd == null) { // The protection domain for statically compiled code is created lazily (not at global::java.lang.Class creation time), // to work around boot strap issues. - AssemblyClassLoader acl = wrapper.GetClassLoader() as AssemblyClassLoader; + var acl = wrapper.GetClassLoader() as AssemblyClassLoader; if (acl != null) - { pd = acl.GetProtectionDomain(); - } else if (wrapper is AnonymousTypeWrapper) { // dynamically compiled intrinsified lamdba anonymous types end up here and should get their @@ -378,34 +409,29 @@ public static object[] getEnclosingMethod0(global::java.lang.Class thisClass) #endif } - public static global::java.lang.Class getPrimitiveClass(string name) + /// + /// Implements the native method for 'getPrimitiveClass'. + /// + /// + /// This method isn't used anymore (because it is an intrinsic (during core class library compilation)) + /// it still remains for compat because it might be invoked through reflection by evil code + /// + /// + /// + /// + public static global::java.lang.Class getPrimitiveClass(string name) => name switch { - // note that this method isn't used anymore (because it is an intrinsic (during core class library compilation)) - // it still remains for compat because it might be invoked through reflection by evil code - switch (name) - { - case "byte": - return PrimitiveTypeWrapper.BYTE.ClassObject; - case "char": - return PrimitiveTypeWrapper.CHAR.ClassObject; - case "double": - return PrimitiveTypeWrapper.DOUBLE.ClassObject; - case "float": - return PrimitiveTypeWrapper.FLOAT.ClassObject; - case "int": - return PrimitiveTypeWrapper.INT.ClassObject; - case "long": - return PrimitiveTypeWrapper.LONG.ClassObject; - case "short": - return PrimitiveTypeWrapper.SHORT.ClassObject; - case "boolean": - return PrimitiveTypeWrapper.BOOLEAN.ClassObject; - case "void": - return PrimitiveTypeWrapper.VOID.ClassObject; - default: - throw new ArgumentException(name); - } - } + "byte" => PrimitiveTypeWrapper.BYTE.ClassObject, + "char" => PrimitiveTypeWrapper.CHAR.ClassObject, + "double" => PrimitiveTypeWrapper.DOUBLE.ClassObject, + "float" => PrimitiveTypeWrapper.FLOAT.ClassObject, + "int" => PrimitiveTypeWrapper.INT.ClassObject, + "long" => PrimitiveTypeWrapper.LONG.ClassObject, + "short" => PrimitiveTypeWrapper.SHORT.ClassObject, + "boolean" => PrimitiveTypeWrapper.BOOLEAN.ClassObject, + "void" => PrimitiveTypeWrapper.VOID.ClassObject, + _ => throw new ArgumentException(name), + }; public static string getGenericSignature0(global::java.lang.Class thisClass) { @@ -450,7 +476,7 @@ internal static object AnnotationsToMap(ClassLoaderWrapper loader, object[] objA if (attr != null) { #if DONT_WRAP_ANNOTATION_ATTRIBUTES - attr.freeze(); + attr.freeze(); #else // freeze to make sure the defaults are set attr.freeze(); @@ -523,7 +549,7 @@ public static object getDeclaredFields0(global::java.lang.Class thisClass, bool public static object getDeclaredMethods0(global::java.lang.Class thisClass, bool publicOnly) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else Profiler.Enter("Class.getDeclaredMethods0"); try diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.NativeLibrary.cs b/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.NativeLibrary.cs index f256bc0354..98ed2c0274 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.NativeLibrary.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.NativeLibrary.cs @@ -36,6 +36,68 @@ namespace IKVM.Java.Externs.java.lang static partial class ClassLoader { + /// + /// Finds the builtin native library, or returns null if the given name isn't built in. + /// + /// + /// + public static string findBuiltinLib(string name) + { + var l = GetUnmappedLibraryName(name); + return IsBuiltinLib(l) ? l : null; + } + + /// + /// Returns true if the given short library name is a builtin library. + /// + /// + /// + static bool IsBuiltinLib(string name) + { + switch (name) + { + case "net": + case "nio": + case "unpack": + case "jaas_nt": + case "awt": + case "splashscreen": + case "jit": + case "sunec": + case "w2k_lsa_auth": + case "osxkrb5": + case "osx": + case "management": + case "zip": + return true; + default: + return false; + } + } + + /// + /// Takes a mapped library name and returns the unmapped verison. + /// + /// + /// + static string GetUnmappedLibraryName(string name) + { + if (name == null) + return null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Path.GetFileNameWithoutExtension(name); + } + else + { + if (name.StartsWith("lib")) + return Path.GetFileNameWithoutExtension(name).Substring(3); + else + return Path.GetFileNameWithoutExtension(name); + } + } + internal static class NativeLibrary { @@ -90,66 +152,6 @@ public static void unload(object thisNativeLibrary, string name, bool isBuiltin) #endif } - /// - /// Finds the builtin native library, or returns null if the given name isn't built in. - /// - /// - /// - public static string findBuiltinLib(string name) - { - var l = GetUnmappedLibraryName(name); - return IsBuiltinLib(l) ? l : null; - } - - /// - /// Returns true if the given short library name is a builtin library. - /// - /// - /// - static bool IsBuiltinLib(string name) - { - switch (name) - { - case "net": - case "unpack": - case "jaas_nt": - case "awt": - case "splashscreen": - case "jit": - case "sunec": - case "w2k_lsa_auth": - case "osxkrb5": - case "osx": - case "management": - return true; - default: - return false; - } - } - - /// - /// Takes a mapped library name and returns the unmapped verison. - /// - /// - /// - static string GetUnmappedLibraryName(string name) - { - if (name == null) - return null; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Path.GetFileNameWithoutExtension(name); - } - else - { - if (name.StartsWith("lib")) - return Path.GetFileNameWithoutExtension(name).Substring(3); - else - return Path.GetFileNameWithoutExtension(name); - } - } - } } diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.cs b/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.cs index b7a9841ef5..6e015d5948 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/ClassLoader.cs @@ -21,6 +21,10 @@ Jeroen Frijters jeroen@frijters.net */ +using System; +using System.Linq; + +using IKVM.ByteCode.Reading; using IKVM.Internal; namespace IKVM.Java.Externs.java.lang @@ -29,103 +33,157 @@ namespace IKVM.Java.Externs.java.lang static partial class ClassLoader { - public static global::java.net.URL getBootstrapResource(string name) + /// + /// Implements the native method 'registerNatives'. + /// + public static void registerNatives() { - foreach (global::java.net.URL url in ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name)) - return url; - return null; } - public static global::java.util.Enumeration getBootstrapResources(string name) + /// + /// Implements the native method 'defineClass0'. + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.lang.Class defineClass0(global::java.lang.ClassLoader self, string name, byte[] b, int off, int len, global::java.security.ProtectionDomain pd) { -#if FIRST_PASS - return null; -#else - return new global::ikvm.runtime.EnumerationWrapper(ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name)); -#endif + return defineClass1(self, name, b, off, len, pd, null); } - public static global::java.lang.Class defineClass0(global::java.lang.ClassLoader thisClassLoader, string name, byte[] b, int off, int len, global::java.security.ProtectionDomain pd) + /// + /// Implements the native method 'defineClass1'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.lang.Class defineClass1(global::java.lang.ClassLoader self, string name, byte[] b, int off, int len, global::java.security.ProtectionDomain pd, string source) { - return defineClass1(thisClassLoader, name, b, off, len, pd, null); - } +#if FIRST_PASS + throw new NotImplementedException(); +#else - public static global::java.lang.Class defineClass1(global::java.lang.ClassLoader thisClassLoader, string name, byte[] b, int off, int len, global::java.security.ProtectionDomain pd, string source) - { - // it appears the source argument is only used for trace messages in HotSpot. We'll just ignore it for now. - Profiler.Enter("ClassLoader.defineClass"); try { - try - { - ClassLoaderWrapper classLoaderWrapper = ClassLoaderWrapper.GetClassLoaderWrapper(thisClassLoader); - ClassFile classFile = new ClassFile(b, off, len, name, classLoaderWrapper.ClassFileParseOptions, null); - if (name != null && classFile.Name != name) - { -#if !FIRST_PASS - throw new global::java.lang.NoClassDefFoundError(name + " (wrong name: " + classFile.Name + ")"); -#endif - } - TypeWrapper type = classLoaderWrapper.DefineClass(classFile, pd); - return type.ClassObject; - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } + var classLoaderWrapper = ClassLoaderWrapper.GetClassLoaderWrapper(self); + var classFile = new ClassFile(ClassReader.Read(new ReadOnlyMemory(b, off, len)), name, classLoaderWrapper.ClassFileParseOptions, null); + if (name != null && classFile.Name != name) + throw new global::java.lang.NoClassDefFoundError(name + " (wrong name: " + classFile.Name + ")"); + + var type = classLoaderWrapper.DefineClass(classFile, pd); + return type.ClassObject; } - finally + catch (RetargetableJavaException x) { - Profiler.Leave("ClassLoader.defineClass"); + throw x.ToJava(); } +#endif } - public static global::java.lang.Class defineClass2(global::java.lang.ClassLoader thisClassLoader, string name, global::java.nio.ByteBuffer bb, int off, int len, global::java.security.ProtectionDomain pd, string source) + /// + /// Implements the native method 'defineClass2'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.lang.Class defineClass2(global::java.lang.ClassLoader self, string name, global::java.nio.ByteBuffer bb, int off, int len, global::java.security.ProtectionDomain pd, string source) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - byte[] buf = new byte[bb.remaining()]; + var buf = new byte[bb.remaining()]; bb.get(buf); - return defineClass1(thisClassLoader, name, buf, 0, buf.Length, pd, source); + return defineClass1(self, name, buf, 0, buf.Length, pd, source); #endif } - public static void resolveClass0(global::java.lang.ClassLoader thisClassLoader, global::java.lang.Class clazz) + /// + /// Implements the native method 'resolveClass0'. + /// + /// + /// + public static void resolveClass0(global::java.lang.ClassLoader self, global::java.lang.Class clazz) { - // no-op + } - public static global::java.lang.Class findBootstrapClass(global::java.lang.ClassLoader thisClassLoader, string name) + /// + /// Implements the native method 'findBootstrapClass'. + /// + /// + /// + /// + public static global::java.lang.Class findBootstrapClass(global::java.lang.ClassLoader self, string name) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - TypeWrapper tw; try { - tw = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedNameFast(name); + return ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedNameFast(name)?.ClassObject; } catch (RetargetableJavaException x) { throw x.ToJava(); } - return tw != null ? tw.ClassObject : null; #endif } - public static global::java.lang.Class findLoadedClass0(global::java.lang.ClassLoader thisClassLoader, string name) + /// + /// Implements the native method 'findLoadedClass0'. + /// + /// + /// + /// + public static global::java.lang.Class findLoadedClass0(global::java.lang.ClassLoader self, string name) { - if (name == null) - { - return null; - } - ClassLoaderWrapper loader = ClassLoaderWrapper.GetClassLoaderWrapper(thisClassLoader); - TypeWrapper tw = loader.FindLoadedClass(name); - return tw != null ? tw.ClassObject : null; + return name != null ? (ClassLoaderWrapper.GetClassLoaderWrapper(self).FindLoadedClass(name)?.ClassObject) : null; + } + + /// + /// Implements the native method 'getBootstrapResource'. + /// + /// + /// + public static global::java.net.URL getBootstrapResource(string name) + { + return ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name).FirstOrDefault(); + } + + /// + /// Implements the native method 'getBootstrapResources'. + /// + /// + /// + public static global::java.util.Enumeration getBootstrapResources(string name) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return new global::ikvm.runtime.EnumerationWrapper(ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name)); +#endif } + /// + /// Implements the native method 'retrieveDirectives'. + /// + /// public static object retrieveDirectives() { return IKVM.Runtime.Assertions.RetrieveDirectives(); diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/Package.cs b/src/IKVM.Runtime/Java/Externs/java/lang/Package.cs index 4c885b7505..f6c6927df5 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/Package.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/Package.cs @@ -26,6 +26,7 @@ Jeroen Frijters using System.Threading; using IKVM.Internal; +using IKVM.Runtime; using IKVM.Runtime.Vfs; namespace IKVM.Java.Externs.java.lang @@ -44,7 +45,7 @@ static void LazyInitSystemPackages() if (systemPackages == null) { var dict = new Dictionary(); - var path = Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(JVM.CoreAssembly), "resources.jar"); + var path = Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(JVM.BaseAssembly), "resources.jar"); foreach (var pkgs in ClassLoaderWrapper.GetBootstrapClassLoader().GetPackageInfo()) foreach (var pkg in pkgs.Value) dict[pkg.Replace('.', '/') + "/"] = path; diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/ProcessImpl.cs b/src/IKVM.Runtime/Java/Externs/java/lang/ProcessImpl.cs index e40b1dc9cf..5700210d06 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/ProcessImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/ProcessImpl.cs @@ -1,111 +1,588 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; +using System.Security.AccessControl; +using System.Text.RegularExpressions; +using System.Threading; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Ikvm.Internal; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Accessors.Java.Util; +using IKVM.Runtime.Util.Java.Security; namespace IKVM.Java.Externs.java.lang { + /// + /// Implements the native methods for 'ProcessImpl'. + /// static class ProcessImpl { - public static int parseCommandString(string cmdstr) +#if FIRST_PASS == false + + static CallerIDAccessor callerIDAccessor; + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + static ThreadAccessor threadAccessor; + static ProcessBuilderRedirectAccessor processBuilderRedirectAccessor; + static ProcessBuilderNullInputStreamAccessor processBuilderNullInputStreamAccessor; + static ProcessBuilderNullOutputStreamAccessor processBuilderNullOutputStreamAccessor; + static FileAccessor fileAccessor; + static FileDescriptorAccessor fileDescriptorAccessor; + static InputStreamAccessor inputStreamAccessor; + static OutputStreamAccessor outputStreamAccessor; + static FileInputStreamAccessor fileInputStreamAccessor; + static FileOutputStreamAccessor fileOutputStreamAccessor; + static BufferedInputStreamAccessor bufferedInputStreamAccessor; + static BufferedOutputStreamAccessor bufferedOutputStreamAccessor; + static AccessControllerAccessor accessControllerAccessor; + static MapAccessor mapAccessor; + static MapEntryAccessor mapEntryAccessor; + static SetAccessor setAccessor; + static IteratorAccessor iteratorAccessor; + static ProcessImplAccessor processImplAccessor; + + static CallerIDAccessor CallerIDAccessor => JVM.BaseAccessors.Get(ref callerIDAccessor); + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + + static ThreadAccessor ThreadAccessor => JVM.BaseAccessors.Get(ref threadAccessor); + + static ProcessBuilderRedirectAccessor ProcessBuilderRedirectAccessor => JVM.BaseAccessors.Get(ref processBuilderRedirectAccessor); + + static ProcessBuilderNullInputStreamAccessor ProcessBuilderNullInputStreamAccessor => JVM.BaseAccessors.Get(ref processBuilderNullInputStreamAccessor); + + static ProcessBuilderNullOutputStreamAccessor ProcessBuilderNullOutputStreamAccessor => JVM.BaseAccessors.Get(ref processBuilderNullOutputStreamAccessor); + + static FileAccessor FileAccessor => JVM.BaseAccessors.Get(ref fileAccessor); + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static InputStreamAccessor InputStreamAccessor => JVM.BaseAccessors.Get(ref inputStreamAccessor); + + static OutputStreamAccessor OutputStreamAccessor => JVM.BaseAccessors.Get(ref outputStreamAccessor); + + static FileInputStreamAccessor FileInputStreamAccessor => JVM.BaseAccessors.Get(ref fileInputStreamAccessor); + + static FileOutputStreamAccessor FileOutputStreamAccessor => JVM.BaseAccessors.Get(ref fileOutputStreamAccessor); + + static BufferedInputStreamAccessor BufferedInputStreamAccessor => JVM.BaseAccessors.Get(ref bufferedInputStreamAccessor); + + static BufferedOutputStreamAccessor BufferedOutputStreamAccessor => JVM.BaseAccessors.Get(ref bufferedOutputStreamAccessor); + + static AccessControllerAccessor AccessControllerAccessor => JVM.BaseAccessors.Get(ref accessControllerAccessor); + + static MapAccessor MapAccessor => JVM.BaseAccessors.Get(ref mapAccessor); + + static MapEntryAccessor MapEntryAccessor => JVM.BaseAccessors.Get(ref mapEntryAccessor); + + static SetAccessor SetAccessor => JVM.BaseAccessors.Get(ref setAccessor); + + static IteratorAccessor IteratorAccessor => JVM.BaseAccessors.Get(ref iteratorAccessor); + + static ProcessImplAccessor ProcessImplAccessor => JVM.BaseAccessors.Get(ref processImplAccessor); + +#endif + + enum WindowsVerificationMode + { + + CmdOrBat = 0, + Win32 = 1, + Legacy = 2, + + } + + static readonly char[][] WindowsInvalidChars = + { + // We guarantee the only command file execution for implicit [cmd.exe] run. + // http://technet.microsoft.com/en-us/library/bb490954.aspx + new [] {' ', '\t', '<', '>', '&', '|', '^'}, + new [] {' ', '\t', '<', '>'}, + new [] {' ', '\t'} + }; + + /// + /// Implements the native method 'start'. + /// + /// + /// + /// + /// + /// + public static object start(string[] cmdarray, object environment, string dir, object[] redirects, bool redirectErrorStream) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + // close these specifically upon exception + FileStream f0 = null; + FileStream f1 = null; + FileStream f2 = null; + + try + { + Stream h0 = null; + Stream h1 = null; + Stream h2 = null; + + // transform redirect specifications into Streams + if (redirects != null) + { + var PIPE = ProcessBuilderRedirectAccessor.GetPipe(); + var INHERIT = ProcessBuilderRedirectAccessor.GetInherit(); + + if (redirects[0] == PIPE) + h0 = null; + else if (redirects[0] == INHERIT) + h0 = FileDescriptorAccessor.GetStream(FileDescriptorAccessor.GetIn()); + else + { + f0 = File.OpenRead(FileAccessor.InvokeGetPath(ProcessBuilderRedirectAccessor.InvokeFile(redirects[0]))); + h0 = f0; + } + + if (redirects[1] == PIPE) + h1 = null; + else if (redirects[1] == INHERIT) + h1 = FileDescriptorAccessor.GetStream(FileDescriptorAccessor.GetOut()); + else + { + f1 = NewFileOutputStream(ProcessBuilderRedirectAccessor.InvokeFile(redirects[1]), ProcessBuilderRedirectAccessor.InvokeAppend(redirects[1])); + h1 = f1; + } + + if (redirects[2] == PIPE) + h2 = null; + else if (redirects[2] == INHERIT) + h2 = FileDescriptorAccessor.GetStream(FileDescriptorAccessor.GetErr()); + else + { + f2 = NewFileOutputStream(ProcessBuilderRedirectAccessor.InvokeFile(redirects[2]), ProcessBuilderRedirectAccessor.InvokeAppend(redirects[2])); + h2 = f2; + } + } + + return Create(cmdarray, MapToDictionary(environment), dir, h0, h1, h2, redirectErrorStream); + } + catch (Exception e) + { + f0?.Dispose(); + f1?.Dispose(); + f2?.Dispose(); + + if (e is global::java.lang.Throwable) + throw; + else + throw new global::java.io.IOException(e.Message, e); + } +#endif + } + + /// + /// Converts a map of string key and string values to a dictionary of the same. + /// + /// + /// + static Dictionary MapToDictionary(object map) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (map == null) + return null; + + var d = new Dictionary(); + var i = SetAccessor.InvokeIterator(MapAccessor.InvokeEntrySet(map)); + while (IteratorAccessor.InvokeHasNext(i)) + { + var e = IteratorAccessor.InvokeNext(i); + var k = (string)MapEntryAccessor.InvokeGetKey(e); + var v = (string)MapEntryAccessor.InvokeGetValue(e); + d[k] = v; + } + + return d; +#endif + } + + /// + /// Creates a new 'ProcessImpl' given the specified input. + /// + /// + /// + /// + /// + /// + /// + /// + /// + static object Create(string[] cmd, Dictionary env, string dir, Stream s0, Stream s1, Stream s2, bool redirectErrorStream) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsWindows) + return CreateWindows(cmd, env, dir, s0, s1, s2, redirectErrorStream); + else + return CreateUnix(cmd, env, dir, s0, s1, s2, redirectErrorStream); +#endif + } + + /// + /// Returns whether the argument has already been quoted. + /// + /// + /// + /// + /// + /// + static bool IsQuoted(bool noQuotesInside, string arg, string errorMessage) + { + int lastPos = arg.Length - 1; + if (lastPos >= 1 && arg[0] == '"' && arg[lastPos] == '"') + { + // the argument has already been quoted + if (noQuotesInside) + { + if (arg.IndexOf('"', 1) != lastPos) + { + // there is ["] inside + throw new ArgumentException(errorMessage); + } + } + return true; + } + + if (noQuotesInside) + { + if (arg.IndexOf('"') >= 0) + { + // there is ["] inside + throw new ArgumentException(errorMessage); + } + } + + return false; + } + + /// + /// Returns whether the argument needs escaping, based on the given verification type. + /// + /// + /// + /// + static bool NeedsEscapingOnWindows(WindowsVerificationMode verificationType, string arg) + { + // Switch off MS heuristic for internal ["]. + // Please, use the explicit [cmd.exe] call + // if you need the internal ["]. + // Example: "cmd.exe", "/C", "Extended_MS_Syntax" + + // For [.exe] or [.com] file the unpaired/internal ["] + // in the argument is not a problem. + var argIsQuoted = IsQuoted(verificationType == WindowsVerificationMode.CmdOrBat, arg, "Argument has embedded quote, use the explicit CMD.EXE call."); + if (argIsQuoted == false) + { + var testEscape = WindowsInvalidChars[(int)verificationType]; + for (int i = 0; i < testEscape.Length; ++i) + { + if (arg.IndexOf(testEscape[i]) >= 0) + { + return true; + } + } + } + + return false; + } + + /// + /// Returns whether the specified file ends with a shell executable extension. + /// + /// + /// + static bool IsWindowsShellFile(string executablePath) + { + return executablePath.EndsWith(".CMD", StringComparison.OrdinalIgnoreCase) || executablePath.EndsWith(".BAT", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Surrounds the specified string with quotes. + /// + /// + /// + static string QuoteString(string arg) + { + return '"' + arg + '"'; + } + + /// + /// Merges the arguments into a single command line suitable for Windows. + /// + /// + /// + /// + static string CreateWindowsCommandLine(WindowsVerificationMode verificationType, String executablePath, string[] cmd) + { + var cmdbuf = new global::System.Text.StringBuilder(80); + cmdbuf.Append(executablePath); + + for (int i = 1; i < cmd.Length; ++i) + { + cmdbuf.Append(' '); + var s = cmd[i]; + + if (NeedsEscapingOnWindows(verificationType, s)) + { + cmdbuf.Append('"').Append(s); + + // The code protects the [java.exe] and console command line + // parser, that interprets the [\"] combination as an escape + // sequence for the ["] char. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + // + // If the argument is an FS path, doubling of the tail [\] + // char is not a problem for non-console applications. + // + // The [\"] sequence is not an escape sequence for the [cmd.exe] + // command line parser. The case of the [""] tail escape + // sequence could not be realized due to the argument validation + // procedure. + if ((verificationType != WindowsVerificationMode.CmdOrBat) && s.EndsWith("\\")) + cmdbuf.Append('\\'); + + cmdbuf.Append('"'); + } + else + { + cmdbuf.Append(s); + } + } + + return cmdbuf.ToString(); + } + + /// + /// Returns the proper path to the executable. + /// + /// + /// + static string GetWindowsExecutablePath(string path) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var pathIsQuoted = IsQuoted(true, path, "Executable name has embedded quote, split the arguments"); + + // Win32 CreateProcess requires path to be normalized + var fileToRun = FileAccessor.Init(pathIsQuoted ? path.Substring(1, path.Length - 2) : path); + + // From the [CreateProcess] function documentation: + // + // "If the file name does not contain an extension, .exe is appended. + // Therefore, if the file name extension is .com, this parameter + // must include the .com extension. If the file name ends in + // a period (.) with no extension, or if the file name contains a path, + // .exe is not appended." + // + // "If the file name !does not contain a directory path!, + // the system searches for the executable file in the following + // sequence:..." + // + // In practice ANY non-existent path is extended by [.exe] extension + // in the [CreateProcess] funcion with the only exception: + // the path ends by (.) + + return FileAccessor.InvokeGetPath(fileToRun); +#endif + } + + /// + /// Extracts individual tokens from a command. + /// + /// + /// + static string[] GetTokensFromWindowsCommand(string command) + { + var matchList = new List(8); + foreach (Match m in Regex.Matches(command, "[^\\s\"]+|\"[^\"]*\"")) + matchList.Add(m.Value); + + return matchList.ToArray(); + } + + /// + /// Initializes the ProcessImpl object for a Windows platform. + /// + /// + /// + /// + /// + /// + /// + /// + /// + static object CreateWindows(string[] cmd, Dictionary env, string dir, Stream h0, Stream h1, Stream h2, bool redirectErrorStream) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + string cmdstr; + + var sm = SystemAccessor.InvokeGetSecurityManager(); + var allowAmbiguousCommands = false; + if (sm == null) + { + allowAmbiguousCommands = true; + var value = SystemAccessor.InvokeGetProperty("jdk.lang.Process.allowAmbiguousCommands"); + if (value != null) + allowAmbiguousCommands = string.Equals(value, "false", StringComparison.OrdinalIgnoreCase) == false; + } + + if (allowAmbiguousCommands) + { + // Legacy mode. + + // Normalize path if possible. + var executablePath = FileAccessor.InvokeGetPath(FileAccessor.Init(cmd[0])); + + // No worry about internal, unpaired ["], and redirection/piping. + if (NeedsEscapingOnWindows(WindowsVerificationMode.Legacy, executablePath)) + executablePath = QuoteString(executablePath); + + // legacy mode doesn't worry about extended verification + cmdstr = CreateWindowsCommandLine(WindowsVerificationMode.Legacy, executablePath, cmd); + } + else + { + string executablePath; + + try + { + // might be able to obtain it from the first argument + executablePath = GetWindowsExecutablePath(cmd[0]); + } + catch (ArgumentException) + { + // Workaround for the calls like + // Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar") + + // No chance to avoid CMD/BAT injection, except to do the work + // right from the beginning. Otherwise we have too many corner + // cases from + // Runtime.getRuntime().exec(String[] cmd [, ...]) + // calls with internal ["] and escape sequences. + + // gets the executable path + cmd = GetTokensFromWindowsCommand(string.Join(" ", cmd)); + executablePath = GetWindowsExecutablePath(cmd[0]); + + // Check new executable name once more + if (sm != null) + SecurityManagerAccessor.InvokeCheckExec(sm, executablePath); + } + + // Quotation protects from interpretation of the [path] argument as + // start of longer path with spaces. Quotation has no influence to + // [.exe] extension heuristic. + cmdstr = CreateWindowsCommandLine(IsWindowsShellFile(executablePath) ? WindowsVerificationMode.CmdOrBat : WindowsVerificationMode.Win32, QuoteString(executablePath), cmd); + } + + // even though we just combined the string, escaped, etc, we need to split it again for S.D.Process + int exeEnd = IndexOfArguments(cmdstr); + int argBeg = exeEnd; + if (cmdstr.Length > argBeg && cmdstr[argBeg] == ' ') + argBeg++; + + var psi = new ProcessStartInfo(); + psi.FileName = cmdstr.Substring(0, exeEnd).Trim('\"'); + psi.Arguments = cmdstr.Substring(argBeg); + return Create(psi, env, dir, h0, h1, h2, redirectErrorStream); +#endif + } + + /// + /// Returns the character position of the arguments without the command path. + /// + /// + /// + static int IndexOfArguments(string cmdstr) { int pos = cmdstr.IndexOf(' '); if (pos == -1) - { return cmdstr.Length; - } + if (cmdstr[0] == '"') { - int close = cmdstr.IndexOf('"', 1); + var close = cmdstr.IndexOf('"', 1); return close == -1 ? cmdstr.Length : close + 1; } - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - { + + if (RuntimeUtil.IsWindows == false) return pos; - } - IList path = null; - for (; ; ) + + IList searchPaths = null; + while (true) { - string str = cmdstr.Substring(0, pos); + var str = cmdstr.Substring(0, pos); if (IsPathRooted(str)) { - if (Exists(str)) - { + if (WindowsExecutableExists(str)) return pos; - } } else { - if (path == null) - { - path = GetSearchPath(); - } - foreach (string p in path) - { - if (Exists(Path.Combine(p, str))) - { + foreach (var p in searchPaths ??= GetWindowsSearchPath()) + if (WindowsExecutableExists(Path.Combine(p, str))) return pos; - } - } } + if (pos == cmdstr.Length) - { return cmdstr.IndexOf(' '); - } + pos = cmdstr.IndexOf(' ', pos + 1); if (pos == -1) - { pos = cmdstr.Length; - } } } - private static List GetSearchPath() + /// + /// Returns the set of paths to search for Windows executables. + /// + /// + static List GetWindowsSearchPath() { - List list = new List(); + var list = new List(); list.Add(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); list.Add(Environment.CurrentDirectory); list.Add(Environment.SystemDirectory); - string windir = Path.GetDirectoryName(Environment.SystemDirectory); + + var windir = Path.GetDirectoryName(Environment.SystemDirectory); list.Add(Path.Combine(windir, "System")); list.Add(windir); - string path = Environment.GetEnvironmentVariable("PATH"); + + var path = Environment.GetEnvironmentVariable("PATH"); if (path != null) - { - foreach (string p in path.Split(Path.PathSeparator)) - { + foreach (var p in path.Split(Path.PathSeparator)) list.Add(p); - } - } + return list; } - private static bool IsPathRooted(string path) + /// + /// Returns true if the path is rooted. + /// + /// + /// + static bool IsPathRooted(string path) { try { @@ -117,26 +594,23 @@ private static bool IsPathRooted(string path) } } - private static bool Exists(string file) + /// + /// Returns true if the specified file can be considered a valid executable. + /// + /// + /// + static bool WindowsExecutableExists(string file) { try { if (File.Exists(file)) - { return true; - } else if (Directory.Exists(file)) - { return false; - } else if (file.IndexOf('.') == -1 && File.Exists(file + ".exe")) - { return true; - } else - { return false; - } } catch { @@ -144,6 +618,392 @@ private static bool Exists(string file) } } + /// + /// Creates a new 'ProcessImpl' for a Unix machine. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + static object CreateUnix(string[] cmd, Dictionary env, string dir, Stream h0, Stream h1, Stream h2, bool redirectErrorStream) + { +#if NETFRAMEWORK + throw new NotImplementedException(); +#else + var psi = new ProcessStartInfo(); + psi.FileName = cmd[0]; + + foreach (var arg in cmd.Skip(1)) + psi.ArgumentList.Add(arg); + + return Create(psi, env, dir, h0, h1, h2, redirectErrorStream); +#endif + + } + + /// + /// Creates a new 'ProcessImpl' based on the filename and arguments configured on the . + /// + /// + /// + /// + /// + /// + /// + /// + /// + static object Create(ProcessStartInfo psi, Dictionary env, string dir, Stream h0, Stream h1, Stream h2, bool redirectErrorStream) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + psi.UseShellExecute = false; + psi.CreateNoWindow = true; + psi.RedirectStandardInput = true; + psi.RedirectStandardOutput = true; + psi.RedirectStandardError = true; + psi.WorkingDirectory = dir; + + if (env != null) + foreach (var kvp in env) + psi.Environment[kvp.Key] = kvp.Value; + + // begin new process + var process = Process.Start(psi); + + // if any of the handles are files, close files when process exits + var f0 = h0 as FileStream; + var f1 = h1 as FileStream; + var f2 = h2 as FileStream; + if (f0 != null || f1 != null || f2 != null) + { + process.EnableRaisingEvents = true; + process.Exited += (s, a) => + { + f0?.Close(); + f1?.Close(); + f2?.Close(); + }; + } + + // base streams from process + var b0 = process.StandardInput.BaseStream; + var b1 = process.StandardOutput.BaseStream; + var b2 = process.StandardError.BaseStream; + + // pipe input data into process standard input + if (h0 != null) + { + ConnectPipe(h0, b0); + h0 = null; + } + else + { + h0 = b0; + } + + Stream stdoutDrain = null; + + if (h1 != null) + { + stdoutDrain = h1; + ConnectPipe(b1, stdoutDrain); + h1 = null; + } + else if (redirectErrorStream) + { + var pipe = new PipeStream(); + ConnectPipe(b1, pipe); + ConnectPipe(b2, pipe); + h1 = pipe; + } + else + { + h1 = b1; + } + + if (redirectErrorStream) + { + if (stdoutDrain != null) + ConnectPipe(b2, stdoutDrain); + h2 = null; + } + else if (h2 != null) + { + ConnectPipe(b2, h2); + h2 = null; + } + else + { + h2 = b2; + } + + // wrap CLR streams in Java streams + object s0 = null; + object s1 = null; + object s2 = null; + AccessControllerAccessor.InvokeDoPrivileged(new ActionPrivilegedAction(() => + { + s0 = h0 == null ? ProcessBuilderNullOutputStreamAccessor.GetInstance() : BufferedOutputStreamAccessor.Init(FileOutputStreamAccessor.Init2(FileDescriptorAccessor.FromStream(h0))); + s1 = h1 == null ? ProcessBuilderNullInputStreamAccessor.GetInstance() : BufferedInputStreamAccessor.Init(FileInputStreamAccessor.Init2(FileDescriptorAccessor.FromStream(h1))); + s2 = h2 == null ? ProcessBuilderNullInputStreamAccessor.GetInstance() : FileInputStreamAccessor.Init2(FileDescriptorAccessor.FromStream(h2)); + }), null, CallerIDAccessor.InvokeCreate(ProcessImplAccessor.Type.TypeHandle)); + + // return new process + return ProcessImplAccessor.Init(process, s0, s1, s2); +#endif + } + + class PipeStream : Stream + { + + readonly byte[] buf = new byte[4096]; + int pos; + int users = 2; + + public override bool CanRead => true; + + public override bool CanWrite => true; + + public override bool CanSeek => false; + + public override long Length => throw new NotImplementedException(); + + public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public override int Read(byte[] buffer, int offset, int count) + { + lock (this) + { + if (count == 0) + return 0; + + while (pos == 0) + { + try + { + Monitor.Wait(this); + } + catch (ThreadInterruptedException) + { + + } + } + + if (pos == -1) + return 0; + + count = Math.Min(count, pos); + Array.Copy(buf, 0, buffer, offset, count); + pos -= count; + + Array.Copy(buf, count, buf, 0, pos); + Monitor.PulseAll(this); + return count; + } + } + + public override void Write(byte[] buffer, int offset, int count) + { + lock (this) + { + while (buf.Length - pos < count) + { + try + { + Monitor.Wait(this); + } + catch (ThreadInterruptedException) + { + + } + } + + Array.Copy(buffer, offset, buf, pos, count); + pos += count; + Monitor.PulseAll(this); + } + } + + public override void Close() + { + lock (this) + { + if (--users == 0) + { + pos = -1; + Monitor.PulseAll(this); + } + } + } + + public override void Flush() + { + + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + } + + /// + /// Begins reading from and writing to . + /// + /// + /// + static void ConnectPipe(Stream src, Stream dst) + { + var buf = new byte[4096]; + + void cb(IAsyncResult ar) + { + try + { + int count = src.EndRead(ar); + if (count > 0) + { + dst.Write(buf, 0, count); + dst.Flush(); + src.BeginRead(buf, 0, buf.Length, cb, null); + } + else + { + dst.Close(); + } + } + catch + { + + } + }; + + try + { + src.BeginRead(buf, 0, buf.Length, cb, null); + } + catch + { + + } + } + + static void WaitForWithInterrupt(Process pid) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var current = ThreadAccessor.InvokeCurrentThread(); + + // spin until thread is interrupted, or process exits + while (!ThreadAccessor.InvokeIsInterrupted(current) && !pid.WaitForExit(100)) + continue; +#endif + } + + public static int waitFor(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var pid = ProcessImplAccessor.GetProcess(self); + if (pid.HasExited) + return pid.ExitCode; + + // wait for the thread to be interrupted, or for the process to exit + WaitForWithInterrupt(pid); + + // check if we were interrupted and throw + if (ThreadAccessor.InvokeInterrupted()) + throw new global::java.lang.InterruptedException(); + + return pid.ExitCode; +#endif + } + + public static int exitValue(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var pid = ProcessImplAccessor.GetProcess(self); + if (pid.HasExited == false) + throw new global::java.lang.IllegalThreadStateException("process has not exited"); + + return pid.ExitCode; +#endif + } + + public static void destroy(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var pid = ProcessImplAccessor.GetProcess(self); + if (pid.HasExited) + return; + + try + { + pid.Kill(); + } + catch + { + // ignore + } +#endif + } + + static FileStream OpenForAtomicAppend(string path) + { +#if NETFRAMEWORK + return new FileStream(path, FileMode.Append, FileSystemRights.AppendData, FileShare.ReadWrite, 1, FileOptions.None); +#else + return new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 1, FileOptions.None); +#endif + } + + /// + /// Open a file for writing. If is true then the file is opened for atomic + /// append directly and a FileOutputStream constructed with the resulting handle. This is because a + /// FileOutputStream created to append to a file does not open the file in a manner that guarantees that writes + /// by the child process will be atomic. + /// + /// + /// + /// + static FileStream NewFileOutputStream(object f, bool append) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var path = FileAccessor.InvokeGetPath(f); + var sm = SystemAccessor.InvokeGetSecurityManager(); + if (sm != null) + SecurityManagerAccessor.InvokeCheckWrite(sm, path); + + if (append) + { + return OpenForAtomicAppend(path); + } + else + { + return File.OpenWrite(path); + } +#endif + } + } } diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/SecurityManager.cs b/src/IKVM.Runtime/Java/Externs/java/lang/SecurityManager.cs index 23792ffbea..bd1ffa5d6a 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/SecurityManager.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/SecurityManager.cs @@ -42,36 +42,35 @@ static class SecurityManager public static global::java.lang.Class[] getClassContext(object thisSecurityManager) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - List stack = new List(); - StackTrace trace = new StackTrace(); + var stack = new List(); + var trace = new StackTrace(); for (int i = 0; i < trace.FrameCount; i++) { - StackFrame frame = trace.GetFrame(i); - MethodBase method = frame.GetMethod(); + var frame = trace.GetFrame(i); + var method = frame.GetMethod(); + if (IKVM.Java.Externs.sun.reflect.Reflection.IsHideFromStackWalk(method)) - { continue; - } - Type type = method.DeclaringType; + + var type = method.DeclaringType; if (type == typeof(global::java.lang.SecurityManager)) - { continue; - } + stack.Add(ClassLoaderWrapper.GetWrapperFromType(type).ClassObject); } + return stack.ToArray(); #endif } public static object currentClassLoader0(object thisSecurityManager) { - global::java.lang.Class currentClass = currentLoadedClass0(thisSecurityManager); + var currentClass = currentLoadedClass0(thisSecurityManager); if (currentClass != null) - { return TypeWrapper.FromClass(currentClass).GetClassLoader().GetJavaClassLoader(); - } + return null; } diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/System.cs b/src/IKVM.Runtime/Java/Externs/java/lang/System.cs index 20b538f889..33ea771b2f 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/System.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/System.cs @@ -1,99 +1,167 @@ -/* - Copyright (C) 2007-2014 Jeroen Frijters +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Accessors.Java.Util; namespace IKVM.Java.Externs.java.lang { + /// + /// Implements the native methods for 'java.lang.System'. + /// static class System { +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static PropertiesAccessor propertiesAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static PropertiesAccessor PropertiesAccessor => JVM.BaseAccessors.Get(ref propertiesAccessor); + +#endif + + const long january_1st_1970 = 62135596800000L; + + /// + /// Implements the native method 'registerNatives'. + /// public static void registerNatives() { - +#if FIRST_PASS + throw new NotImplementedException(); +#else + JVM.EnsureInitialized(); +#endif } + /// + /// Implements the native method 'setIn0'. + /// + /// public static void setIn0(object @in) { -#if !FIRST_PASS - global::java.lang.StdIO.@in = (global::java.io.InputStream)@in; +#if FIRST_PASS + throw new NotImplementedException(); +#else + SystemAccessor.SetIn(@in); #endif } + /// + /// Implements the native method 'setOut0'. + /// public static void setOut0(object @out) { -#if !FIRST_PASS - global::java.lang.StdIO.@out = (global::java.io.PrintStream)@out; +#if FIRST_PASS + throw new NotImplementedException(); +#else + SystemAccessor.SetOut(@out); #endif } + /// + /// Implements the native method 'setErr0'. + /// public static void setErr0(object err) { -#if !FIRST_PASS - global::java.lang.StdIO.err = (global::java.io.PrintStream)err; +#if FIRST_PASS + throw new NotImplementedException(); +#else + SystemAccessor.SetErr(err); #endif } + /// + /// Implements the native method 'currentTimeMillis'. + /// + /// + public static long currentTimeMillis() + { + return DateTime.UtcNow.Ticks / 10000L - january_1st_1970; + } + + /// + /// Implements the native method 'nanoTime'. + /// + /// + public static long nanoTime() + { + const long NANOS_PER_SEC = 1000000000; + + var time = (double)Stopwatch.GetTimestamp(); + var freq = (double)Stopwatch.Frequency; + return (long)(time / freq * NANOS_PER_SEC); + } + + /// + /// Implements the native method 'arraycopy'. + /// + /// + /// + /// + /// + /// + public static void arraycopy(object src, int srcPos, object dest, int destPos, int length) + { + ByteCodeHelper.arraycopy(src, srcPos, dest, destPos, length); + } + + /// + /// Implements the native method 'identityHashCode'. + /// + /// + /// + public static int identityHashCode(object x) + { + return RuntimeHelpers.GetHashCode(x); + } + + /// + /// Implements the native method 'initProperties'. + /// + /// + /// public static object initProperties(object props) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - global::java.lang.VMSystemProperties.initProperties((global::java.util.Properties)props); + foreach (var kvp in JVM.Properties.Init) + if (kvp.Value is string v) + PropertiesAccessor.InvokeSetProperty(props, kvp.Key, v); + return props; #endif } + /// + /// Implements the native method 'mapLibraryName'. + /// + /// + /// public static string mapLibraryName(string libname) { #if FIRST_PASS throw new NotImplementedException(); #else if (libname == null) - { throw new global::java.lang.NullPointerException(); - } - if (global::ikvm.@internal.Util.WINDOWS) - { + + if (RuntimeUtil.IsWindows) return libname + ".dll"; - } - else if (global::ikvm.@internal.Util.MACOSX) - { + else if (RuntimeUtil.IsOSX) return "lib" + libname + ".jnilib"; - } else - { return "lib" + libname + ".so"; - } #endif } - public static void arraycopy(object src, int srcPos, object dest, int destPos, int length) - { - IKVM.Runtime.ByteCodeHelper.arraycopy(src, srcPos, dest, destPos, length); - } } diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/Thread.cs b/src/IKVM.Runtime/Java/Externs/java/lang/Thread.cs index 872a99cea2..4915057859 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/Thread.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/Thread.cs @@ -22,39 +22,56 @@ Jeroen Frijters */ +using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Util.Java.Security; namespace IKVM.Java.Externs.java.lang { + /// + /// Implements the native mthods of 'java.lang.Thread'. + /// static class Thread { - static readonly object mainThreadGroup; +#if FIRST_PASS == false -#if !FIRST_PASS + static AccessControllerAccessor accessControllerAccessor; + static ThreadAccessor threadAccessor; - static Thread() - { - mainThreadGroup = new global::java.lang.ThreadGroup(global::java.lang.ThreadGroup.createRootGroup(), "main"); - } + static AccessControllerAccessor AccessControllerAccessor => JVM.BaseAccessors.Get(ref accessControllerAccessor); + + static ThreadAccessor ThreadAccessor => JVM.BaseAccessors.Get(ref threadAccessor); #endif + /// + /// Implements the native method 'getMainThreadGroup'. + /// + /// public static object getMainThreadGroup() { - return mainThreadGroup; +#if FIRST_PASS + throw new NotImplementedException(); +#else + return JVM.MainThreadGroup; +#endif } // this is called from JniInterface.cs internal static void WaitUntilLastJniThread() { -#if !FIRST_PASS - int count = global::java.lang.Thread.currentThread().isDaemon() ? 0 : 1; +#if FIRST_PASS + throw new NotImplementedException(); +#else + int count = ThreadAccessor.InvokeIsDaemon(ThreadAccessor.InvokeCurrentThread()) ? 0 : 1; while (Interlocked.CompareExchange(ref global::java.lang.Thread.nonDaemonCount[0], 0, 0) > count) global::System.Threading.Thread.Sleep(1); #endif @@ -63,19 +80,23 @@ internal static void WaitUntilLastJniThread() // this is called from JniInterface.cs internal static void AttachThreadFromJni(object threadGroup) { -#if !FIRST_PASS - if (threadGroup == null) - threadGroup = mainThreadGroup; - - if (global::java.lang.Thread.current == null) - new global::java.lang.Thread((global::java.lang.ThreadGroup)threadGroup); +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (ThreadAccessor.GetCurrent() == null) + ThreadAccessor.Init(threadGroup ?? JVM.MainThreadGroup); #endif } + /// + /// Implements the native method 'getStackTrace'. + /// + /// + /// public static global::java.lang.StackTraceElement[] getStackTrace(StackTrace stack) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else var stackTrace = new List(); ExceptionHelper.ExceptionInfoHelper.Append(stackTrace, stack, 0, true); @@ -83,15 +104,19 @@ internal static void AttachThreadFromJni(object threadGroup) #endif } + /// + /// Implements the native method 'getThreads'. + /// + /// public static object getThreads() { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - return global::java.security.AccessController.doPrivileged(global::ikvm.runtime.Delegates.toPrivilegedAction(delegate + return AccessControllerAccessor.InvokeDoPrivileged(new FuncPrivilegedAction(() => { - var root = (global::java.lang.ThreadGroup)mainThreadGroup; - for (; ; ) + var root = (global::java.lang.ThreadGroup)JVM.MainThreadGroup; + while (true) { var threads = new global::java.lang.Thread[root.activeCount()]; if (root.enumerate(threads) == threads.Length) diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/VMSystemProperties.cs b/src/IKVM.Runtime/Java/Externs/java/lang/VMSystemProperties.cs deleted file mode 100644 index d6f46238bb..0000000000 --- a/src/IKVM.Runtime/Java/Externs/java/lang/VMSystemProperties.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; - -using IKVM.Internal; -using IKVM.Runtime.Vfs; - -using Mono.Unix.Native; - -namespace IKVM.Java.Externs.java.lang -{ - - static class VMSystemProperties - { - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct OSVERSIONINFOEXW - { - public int dwOSVersionInfoSize; - public int dwMajorVersion; - public int dwMinorVersion; - public int dwBuildNumber; - public int dwPlatformId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] - public string szCSDVersion; - public ushort wServicePackMajor; - public ushort wServicePackMinor; - public ushort wSuiteMask; - public VER_NT wProductType; - public byte wReserved; - } - - /// - /// VER_NT_* values. - /// - enum VER_NT : byte - { - - DOMAIN_CONTROLLER = 0x0000002, - SERVER = 0x0000003, - WORKSTATION = 0x0000001, - - } - - /// - /// Set of properties to initially import upon startup. - /// - public static IDictionary ImportProperties { get; set; } - - /// - /// Gets the of the runtime. - /// - /// - public static Assembly getRuntimeAssembly() - { - return typeof(VMSystemProperties).Assembly; - } - - /// - /// Gets the RID architecture. - /// - /// - static string GetRuntimeIdentifierArchitecture() => RuntimeInformation.ProcessArchitecture switch - { - Architecture.X86 => "x86", - Architecture.X64 => "x64", - Architecture.Arm => "arm", - Architecture.Arm64 => "arm64", - _ => throw new NotSupportedException(), - }; - - /// - /// Returns the architecture name of the ikvm.home directory to use for this run. - /// - /// - static IEnumerable GetIkvmHomeArchsEnumerator() - { - var arch = GetRuntimeIdentifierArchitecture(); - if (arch == null) - yield break; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var v = Environment.OSVersion.Version; - - // Windows 10 - if (v.Major > 10 || (v.Major == 10 && v.Minor >= 0)) - yield return $"win10-{arch}"; - - // Windows 8.1 - if (v.Major > 6 || (v.Major == 6 && v.Minor >= 3)) - yield return $"win81-{arch}"; - - // Windows 7 - if (v.Major > 6 || (v.Major == 6 && v.Minor >= 1)) - yield return $"win7-{arch}"; - - // fallback - yield return $"win-{arch}"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - yield return $"linux-{arch}"; - } - } - - /// - /// Returns the architecture name of the ikvm.home directory to use for this run. - /// - /// - public static string[] getIkvmHomeArchs() - { - return GetIkvmHomeArchsEnumerator().ToArray(); - } - - /// - /// Gets the path to the root of the VFS. - /// - /// - public static string getVirtualFileSystemRoot() - { - return VfsTable.RootPath; - } - - public static string getBootClassPath() - { - return VfsTable.Default.GetAssemblyClassesPath(JVM.CoreAssembly); - } - - public static string getStdoutEncoding() - { - return IsWindowsConsole(true) ? GetConsoleEncoding() : null; - } - - public static string getStderrEncoding() - { - return IsWindowsConsole(false) ? GetConsoleEncoding() : null; - } - - public static FileVersionInfo getKernel32FileVersionInfo() - { - try - { - foreach (ProcessModule module in Process.GetCurrentProcess().Modules) - if (string.Compare(module.ModuleName, "kernel32.dll", StringComparison.OrdinalIgnoreCase) == 0) - return module.FileVersionInfo; - } - catch - { - - } - - return null; - } - - static bool IsWindowsConsole(bool stdout) - { - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - return false; - else - return stdout ? !Console.IsOutputRedirected : !Console.IsErrorRedirected; - } - - static string GetConsoleEncoding() - { - var codepage = Console.InputEncoding.CodePage; - return codepage is >= 847 and <= 950 ? $"ms{codepage}" : $"cp{codepage}"; - } - - [DllImport("ntdll.dll", SetLastError = true)] - static extern int RtlGetVersion(ref OSVERSIONINFOEXW versionInfo); - - /// - /// Gets the Windows ProductType. - /// - /// - public static byte getWindowsProductType() - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == false) - throw new global::java.lang.UnsupportedOperationException("Cannot retrieve a Windows product type for this operating system."); - - var osvi = default(OSVERSIONINFOEXW); - osvi.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEXW)); - if (RtlGetVersion(ref osvi) != 0) - return 0; - - return (byte)osvi.wProductType; -#endif - } - - /// - /// Gets the 'sysname' on Linux. - /// - /// - public static string[] getLinuxSysnameAndRelease() - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == false) - throw new global::java.lang.UnsupportedOperationException("Cannot retrieve sysname information for this operating system."); - - if (Syscall.uname(out var utsname) != 0) - return null; - - return new[] { utsname.sysname, utsname.release }; -#endif - } - - } - -} diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleImpl.cs b/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleImpl.cs index 7240870f46..9969446e6b 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleImpl.cs @@ -70,7 +70,7 @@ static class MethodHandleImpl { names.Add(new global::java.lang.invoke.LambdaForm.Name(global::java.lang.invoke.LambdaForm.constantZero(global::java.lang.invoke.LambdaForm.BasicType.basicType(srcType.returnType())))); } - global::java.lang.invoke.LambdaForm form = new global::java.lang.invoke.LambdaForm("PairwiseConvert", srcType.parameterCount() + 1, names.ToArray(), srcType.returnType() == global::java.lang.Void.TYPE ? global::java.lang.invoke.LambdaForm.VOID_RESULT : global::java.lang.invoke.LambdaForm.LAST_RESULT, false); + global::java.lang.invoke.LambdaForm form = new global::java.lang.invoke.LambdaForm("PairwiseConvert", srcType.parameterCount() + 1, names.ToArray(), srcType.returnType() == global::java.lang.Void.TYPE ? global::java.lang.invoke.LambdaForm.VOID_RESULT : global::java.lang.invoke.LambdaForm.LAST_RESULT, false, null); return new global::java.lang.invoke.LightWeightMethodHandle(srcType, form); #endif } diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleNatives.cs b/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleNatives.cs index bb281898bc..59597c8049 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleNatives.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/invoke/MethodHandleNatives.cs @@ -35,7 +35,7 @@ static class MethodHandleNatives // called from map.xml as a replacement for Class.isInstance() in JlInvoke.MethodHandleImpl.castReference() public static bool Class_isInstance(global::java.lang.Class clazz, object obj) { - TypeWrapper tw = TypeWrapper.FromClass(clazz); + var tw = TypeWrapper.FromClass(clazz); // handle the type system hole that is caused by arrays being both derived from cli.System.Array and directly from java.lang.Object return tw.IsInstance(obj) || (tw == CoreClasses.cli.System.Object.Wrapper && obj is Array); } @@ -48,7 +48,9 @@ public static void init(global::java.lang.invoke.MemberName self, object refObj) // this overload is called via a map.xml patch to the MemberName(Method, boolean) constructor, because we need wantSpecial public static void init(global::java.lang.invoke.MemberName self, object refObj, bool wantSpecial) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else global::java.lang.reflect.Method method; global::java.lang.reflect.Constructor constructor; global::java.lang.reflect.Field field; @@ -75,9 +77,11 @@ public static void init(global::java.lang.invoke.MemberName self, object refObj, #endif } - private static void InitMethodImpl(global::java.lang.invoke.MemberName self, MethodWrapper mw, bool wantSpecial) + static void InitMethodImpl(global::java.lang.invoke.MemberName self, MethodWrapper mw, bool wantSpecial) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else int flags = (int)mw.Modifiers; flags |= mw.IsConstructor ? global::java.lang.invoke.MethodHandleNatives.Constants.MN_IS_CONSTRUCTOR : global::java.lang.invoke.MethodHandleNatives.Constants.MN_IS_METHOD; if (mw.IsStatic) @@ -135,10 +139,12 @@ private static void InitMethodImpl(global::java.lang.invoke.MemberName self, Met } #if !FIRST_PASS - private static void SetModifiers(global::java.lang.invoke.MemberName self, MemberWrapper mw) + + static void SetModifiers(global::java.lang.invoke.MemberName self, MemberWrapper mw) { self._flags(self._flags() | (int)mw.Modifiers); } + #endif public static void expand(global::java.lang.invoke.MemberName self) @@ -202,7 +208,7 @@ public static void expand(global::java.lang.invoke.MemberName self) } #if !FIRST_PASS - private static void ResolveMethod(global::java.lang.invoke.MemberName self, global::java.lang.Class caller) + static void ResolveMethod(global::java.lang.invoke.MemberName self, global::java.lang.Class caller) { bool invokeSpecial = self.getReferenceKind() == global::java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial; bool newInvokeSpecial = self.getReferenceKind() == global::java.lang.invoke.MethodHandleNatives.Constants.REF_newInvokeSpecial; @@ -330,10 +336,10 @@ private static bool IsReferenceKindStatic(int referenceKind) #endif // TODO consider caching this delegate in MethodWrapper - private static Delegate CreateMemberNameDelegate(MethodWrapper mw, global::java.lang.Class caller, bool doDispatch, global::java.lang.invoke.MethodType type) + static Delegate CreateMemberNameDelegate(MethodWrapper mw, global::java.lang.Class caller, bool doDispatch, global::java.lang.invoke.MethodType type) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else if (mw.IsDynamicOnly) { @@ -382,20 +388,16 @@ private static Delegate CreateMemberNameDelegate(MethodWrapper mw, global::java. public static int getMembers(global::java.lang.Class defc, string matchName, string matchSig, int matchFlags, global::java.lang.Class caller, int skip, global::java.lang.invoke.MemberName[] results) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else if (matchName != null || matchSig != null || matchFlags != global::java.lang.invoke.MethodHandleNatives.Constants.MN_IS_METHOD) - { throw new NotImplementedException(); - } - MethodWrapper[] methods = TypeWrapper.FromClass(defc).GetMethods(); + + var methods = TypeWrapper.FromClass(defc).GetMethods(); for (int i = skip, len = Math.Min(results.Length, methods.Length - skip); i < len; i++) - { if (!methods[i].IsConstructor && !methods[i].IsClassInitializer) - { results[i - skip] = new global::java.lang.invoke.MemberName((global::java.lang.reflect.Method)methods[i].ToMethodOrConstructor(true), false); - } - } + return methods.Length - skip; #endif } @@ -403,17 +405,21 @@ public static int getMembers(global::java.lang.Class defc, string matchName, str public static long objectFieldOffset(global::java.lang.invoke.MemberName self) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - global::java.lang.reflect.Field field = (global::java.lang.reflect.Field)TypeWrapper.FromClass(self.getDeclaringClass()) - .GetFieldWrapper(self.getName(), self.getSignature().Replace('/', '.')).ToField(false); - return global::sun.misc.Unsafe.allocateUnsafeFieldId(field); + var fw = TypeWrapper.FromClass(self.getDeclaringClass()).GetFieldWrapper(self.getName(), self.getSignature().Replace('/', '.')); + return (long)fw.Cookie; #endif } public static long staticFieldOffset(global::java.lang.invoke.MemberName self) { - return objectFieldOffset(self); +#if FIRST_PASS + throw new NotImplementedException(); +#else + var fw = TypeWrapper.FromClass(self.getDeclaringClass()).GetFieldWrapper(self.getName(), self.getSignature().Replace('/', '.')); + return (long)fw.Cookie; +#endif } public static object staticFieldBase(global::java.lang.invoke.MemberName self) @@ -432,11 +438,12 @@ internal static void InitializeCallSite(global::java.lang.invoke.CallSite site) public static void setCallSiteTargetNormal(global::java.lang.invoke.CallSite site, global::java.lang.invoke.MethodHandle target) { -#if !FIRST_PASS +#if FIRST_PASS + throw new NotImplementedException(); +#else if (site.ics == null) - { InitializeCallSite(site); - } + lock (site.ics) { site.target = target; @@ -458,7 +465,7 @@ public static void registerNatives() public static object getMemberVMInfo(global::java.lang.invoke.MemberName self) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else if (self.isField()) { @@ -480,9 +487,9 @@ public static int getConstant(int which) public static int getNamedCon(int which, object[] name) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else - FieldInfo[] fields = typeof(global::java.lang.invoke.MethodHandleNatives.Constants).GetFields(BindingFlags.Static | BindingFlags.NonPublic); + var fields = typeof(global::java.lang.invoke.MethodHandleNatives.Constants).GetFields(BindingFlags.Static | BindingFlags.NonPublic); if (which >= fields.Length) { name[0] = null; diff --git a/src/IKVM.Runtime/Java/Externs/java/lang/reflect/Executable.cs b/src/IKVM.Runtime/Java/Externs/java/lang/reflect/Executable.cs index 8f215d9666..bf45b93b4d 100644 --- a/src/IKVM.Runtime/Java/Externs/java/lang/reflect/Executable.cs +++ b/src/IKVM.Runtime/Java/Externs/java/lang/reflect/Executable.cs @@ -24,6 +24,7 @@ Jeroen Frijters using System.Collections.Generic; using IKVM.Internal; +using IKVM.Runtime; namespace IKVM.Java.Externs.java.lang.reflect { @@ -49,7 +50,7 @@ public static object[] getParameters0(global::java.lang.reflect.Executable _this global::java.lang.reflect.Parameter[] parameters = new global::java.lang.reflect.Parameter[methodParameters.Length]; for (int i = 0; i < parameters.Length; i++) { - parameters[i] = new global::java.lang.reflect.Parameter(methodParameters[i].name ?? "", methodParameters[i].flags, _this, i); + parameters[i] = new global::java.lang.reflect.Parameter(methodParameters[i].name ?? "", (int)(ushort)methodParameters[i].accessFlags, _this, i); } return parameters; #endif diff --git a/src/IKVM.Runtime/Java/Externs/java/net/Inet6AddressImpl.cs b/src/IKVM.Runtime/Java/Externs/java/net/Inet6AddressImpl.cs index e53831f06f..c470fce8d2 100644 --- a/src/IKVM.Runtime/Java/Externs/java/net/Inet6AddressImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/java/net/Inet6AddressImpl.cs @@ -24,8 +24,7 @@ Jeroen Frijters using System; using System.Net; using System.Net.Sockets; - -using IKVM.Runtime.Java.Externs.java.net; +using IKVM.Runtime.Util.Java.Net; namespace IKVM.Java.Externs.java.net { diff --git a/src/IKVM.Runtime/Java/Externs/java/net/PlainDatagramSocketImpl.cs b/src/IKVM.Runtime/Java/Externs/java/net/PlainDatagramSocketImpl.cs index 75c25351ac..a040a044d5 100644 --- a/src/IKVM.Runtime/Java/Externs/java/net/PlainDatagramSocketImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/java/net/PlainDatagramSocketImpl.cs @@ -8,7 +8,10 @@ using System.Reflection; using System.Runtime.InteropServices; -using IKVM.Runtime.Java.Externs.java.net; +using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; using static IKVM.Java.Externs.java.net.SocketImplUtil; @@ -21,7 +24,10 @@ namespace IKVM.Java.Externs.java.net static class PlainDatagramSocketImpl { -#if !FIRST_PASS +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); /// /// Compiles a fast getter for a . @@ -134,7 +140,7 @@ static int GetDefaultScopeId(global::java.net.Inet6Address address) return s2; // for Linux we need to obtain the default scope ID by effectively doing a route lookup - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && FindScopeId(address) is int s3 and not 0) + if (RuntimeUtil.IsLinux && FindScopeId(address) is int s3 and not 0) { Inet6AddressCachedScopeIdSetter(address, s3); return s3; @@ -196,10 +202,10 @@ public static void datagramSocketCreate(object this_) // returns connection reset errors on unconnected UDP sockets (as well // as connected sockets). The solution is to only enable this feature // when the socket is connected. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) socket.IOControl(SIO_UDP_CONNRESET, IOControlFalseBuffer, null); - impl.fd.setSocket(socket); + FileDescriptorAccessor.SetSocket(impl.fd, socket); }); #endif } @@ -217,14 +223,14 @@ public static void datagramSocketClose(object this_) #else InvokeAction(this_, (impl) => { - var socket = impl.fd?.getSocket(); + var socket = FileDescriptorAccessor.GetSocket(impl.fd); if (socket == null) return; InvokeWithSocket(impl, socket => { + FileDescriptorAccessor.SetSocket(impl.fd, null); socket.Close(); - impl.fd.setSocket(null); }); }); #endif @@ -257,7 +263,7 @@ public static void connect0(object this_, object address_, int port) socket.EndConnect(socket.BeginConnect(GetEndPointAddress(address), port, null, null)); // see comment in in socketCreate - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) socket.IOControl(SIO_UDP_CONNRESET, IOControlTrueBuffer, null); }); }); @@ -291,7 +297,7 @@ public static void disconnect0(object this_, int family) socket.EndConnect(socket.BeginConnect(new IPEndPoint(IPAddress.IPv6Any, 0), null, null)); // see comment in in socketCreate - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) socket.IOControl(SIO_UDP_CONNRESET, IOControlFalseBuffer, null); }); })); @@ -566,7 +572,7 @@ public static int peekData(object this_, object packet_) // Windows Poll method reports errors as readable, however, Linux reports it as errored, so // we can use Poll on Windows for both errors, but must use Select on Linux to trap both // read and error states - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) { // wait for data to be available if (socket.Poll(impl.timeout * 1000L > int.MaxValue ? int.MaxValue : impl.timeout * 1000, SelectMode.SelectRead) == false) @@ -605,7 +611,7 @@ public static int peekData(object this_, object packet_) catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) { // Windows may leave multiple ICMP packets on the socket, purge them - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) PurgeOutstandingICMP(socket); throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); @@ -672,7 +678,7 @@ public static void receive0(object this_, object packet_) // Windows Poll method reports errors as readable, however, Linux reports it as errored, so // we can use Poll on Windows for both errors, but must use Select on Linux to trap both // read and error states - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) { // wait for data to be available if (socket.Poll(impl.timeout * 1000L > int.MaxValue ? int.MaxValue : impl.timeout * 1000, SelectMode.SelectRead) == false) @@ -711,7 +717,7 @@ public static void receive0(object this_, object packet_) catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) { // Windows may leave multiple ICMP packets on the socket, purge them - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) PurgeOutstandingICMP(socket); throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); diff --git a/src/IKVM.Runtime/Java/Externs/java/net/PlainSocketImpl.cs b/src/IKVM.Runtime/Java/Externs/java/net/PlainSocketImpl.cs index ed67e01527..400fe4f31e 100644 --- a/src/IKVM.Runtime/Java/Externs/java/net/PlainSocketImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/java/net/PlainSocketImpl.cs @@ -7,7 +7,10 @@ using System.Reflection; using System.Runtime.InteropServices; -using IKVM.Runtime.Java.Externs.java.net; +using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; using static IKVM.Java.Externs.java.net.SocketImplUtil; @@ -20,7 +23,45 @@ namespace IKVM.Java.Externs.java.net static class PlainSocketImpl { -#if !FIRST_PASS +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + [Flags] + enum HANDLE_FLAGS + { + + NONE = 0, + INHERIT = 0x00000001, + PROTECT_FROM_CLOSE = 0x00000002, + + } + +#if NETFRAMEWORK + + /// + /// Invokes the Win32 SetHandleInformation function. + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags); +#else + + /// + /// Invokes the Win32 SetHandleInformation function. + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetHandleInformation(SafeHandle hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags); + +#endif /// /// Compiles a fast getter for a . @@ -83,7 +124,7 @@ static int GetDefaultScopeId(global::java.net.Inet6Address address) return s2; // for Linux we need to obtain the default scope ID by effectively doing a route lookup - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && FindScopeId(address) is int s3 and not 0) + if (RuntimeUtil.IsLinux && FindScopeId(address) is int s3 and not 0) { Inet6AddressCachedScopeIdSetter(address, s3); return s3; @@ -129,12 +170,12 @@ public static void initProto() /// /// Implements the native method for 'socketCreate'. /// - public static void socketCreate(object this_, bool isServer) + public static void socketCreate(object self, bool isServer) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { var socket = new Socket(isServer ? SocketType.Stream : SocketType.Dgram, ProtocolType.Tcp); @@ -142,7 +183,7 @@ public static void socketCreate(object this_, bool isServer) if (AbstractPlainSocketImplServerSocketGetter(impl) != null) socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - impl.fd.setSocket(socket); + FileDescriptorAccessor.SetSocket(impl.fd, socket); }); #endif } @@ -150,12 +191,12 @@ public static void socketCreate(object this_, bool isServer) /// /// Implements the native method for 'socketConnect'. /// - public static void socketConnect(object this_, object address_, int port, int timeout) + public static void socketConnect(object self, object address_, int port, int timeout) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, address_, (impl, address) => + InvokeAction(self, address_, (impl, address) => { InvokeActionWithSocket(impl, socket => { @@ -237,12 +278,12 @@ public static void socketConnect(object this_, object address_, int port, int ti /// /// Implements the native method for 'socketBind'. /// - public static void socketBind(object this_, global::java.net.InetAddress address, int port) + public static void socketBind(object self, global::java.net.InetAddress address, int port) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { InvokeActionWithSocket(impl, socket => { @@ -276,12 +317,12 @@ public static void socketBind(object this_, global::java.net.InetAddress address /// /// Implements the native method for 'socketListen'. /// - public static void socketListen(object this_, int count) + public static void socketListen(object self, int count) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { InvokeActionWithSocket(impl, socket => { @@ -294,12 +335,12 @@ public static void socketListen(object this_, int count) /// /// Implements the native method for 'socketAccept'. /// - public static void socketAccept(object this_, object s_) + public static void socketAccept(object self, object s_) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, s_, (impl, s) => + InvokeAction(self, s_, (impl, s) => { InvokeActionWithSocket(impl, socket => { @@ -324,9 +365,17 @@ public static void socketAccept(object this_, object s_) if (newSocket == null) throw new global::java.net.SocketException("Invalid socket."); +// // allow socket handle to be inherited by child processes on Windows +// if (RuntimeUtil.IsWindows) +//#if NETFRAMEWORK +// SetHandleInformation(newSocket.Handle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.NONE); +//#else +// SetHandleInformation(newSocket.SafeHandle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.NONE); +//#endif + // associate new FileDescriptor with socket var newfd = new global::java.io.FileDescriptor(); - newfd.setSocket(newSocket); + FileDescriptorAccessor.SetSocket(newfd, newSocket); // populate newly accepted socket var remoteIpEndPoint = (IPEndPoint)newSocket.RemoteEndPoint; @@ -380,12 +429,12 @@ public static int socketAvailable(object this_) /// /// Implements the native method for 'socketClose0'. /// - public static void socketClose0(object this_, bool useDeferredClose) + public static void socketClose0(object self, bool useDeferredClose) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { if (impl.fd == null) throw new global::java.net.SocketException("Socket already closed."); @@ -410,7 +459,7 @@ public static void socketClose0(object this_, bool useDeferredClose) } // null socket before close, as close may take a minute to flush - impl.fd.setSocket(null); + FileDescriptorAccessor.SetSocket(impl.fd, null); socket.Close(); }); }); @@ -420,12 +469,12 @@ public static void socketClose0(object this_, bool useDeferredClose) /// /// Implements the native method for 'socketShutdown'. /// - public static void socketShutdown(object this_, int howto) + public static void socketShutdown(object self, int howto) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { if (impl.fd == null) throw new global::java.net.SocketException("Socket already closed."); @@ -441,12 +490,12 @@ public static void socketShutdown(object this_, int howto) /// /// Implements the native method for 'socketSetOption0'. /// - public static void socketSetOption0(object this_, int cmd, bool on, object value) + public static void socketSetOption0(object self, int cmd, bool on, object value) { #if FIRST_PASS throw new NotImplementedException(); #else - InvokeAction(this_, impl => + InvokeAction(self, impl => { InvokeActionWithSocket(impl, socket => { @@ -511,12 +560,12 @@ public static void socketSetOption0(object this_, int cmd, bool on, object value /// /// Implements the native method for 'socketGetOption'. /// - public static int socketGetOption(object this_, int opt, object iaContainerObj_) + public static int socketGetOption(object self, int opt, object iaContainerObj_) { #if FIRST_PASS throw new NotImplementedException(); #else - return InvokeFunc(this_, iaContainerObj_, (impl, iaContainerObj) => + return InvokeFunc(self, iaContainerObj_, (impl, iaContainerObj) => { return InvokeFuncWithSocket(impl, socket => { diff --git a/src/IKVM.Runtime/Java/Externs/java/net/SocketInvokeUtil.cs b/src/IKVM.Runtime/Java/Externs/java/net/SocketInvokeUtil.cs index fd6f388b68..3fe6e7af01 100644 --- a/src/IKVM.Runtime/Java/Externs/java/net/SocketInvokeUtil.cs +++ b/src/IKVM.Runtime/Java/Externs/java/net/SocketInvokeUtil.cs @@ -1,6 +1,8 @@ using System; using System.Net.Sockets; +using IKVM.Runtime.Util.Java.Net; + namespace IKVM.Java.Externs.java.net { diff --git a/src/IKVM.Runtime/Java/Externs/java/nio/Bits.cs b/src/IKVM.Runtime/Java/Externs/java/nio/Bits.cs index 927656ed54..008e0d569e 100644 --- a/src/IKVM.Runtime/Java/Externs/java/nio/Bits.cs +++ b/src/IKVM.Runtime/Java/Externs/java/nio/Bits.cs @@ -29,7 +29,6 @@ Jeroen Frijters namespace IKVM.Java.Externs.java.nio { - [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] [SecurityCritical] static class Bits { diff --git a/src/IKVM.Runtime/Java/Externs/java/nio/MappedByteBuffer.cs b/src/IKVM.Runtime/Java/Externs/java/nio/MappedByteBuffer.cs index 5d0f654256..6486cf9c57 100644 --- a/src/IKVM.Runtime/Java/Externs/java/nio/MappedByteBuffer.cs +++ b/src/IKVM.Runtime/Java/Externs/java/nio/MappedByteBuffer.cs @@ -25,7 +25,9 @@ Jeroen Frijters using System.Runtime.InteropServices; using System.Security; -using IKVM.Internal; +using IKVM.Runtime; + +using Mono.Unix.Native; namespace IKVM.Java.Externs.java.nio { @@ -33,16 +35,16 @@ namespace IKVM.Java.Externs.java.nio static class MappedByteBuffer { - private static volatile int bogusField; + static volatile int bogusField; - public static bool isLoaded0(object thisMappedByteBuffer, long address, long length, int pageCount) + public static bool isLoaded0(object self, long address, long length, int pageCount) { // on Windows, JDK simply returns false, so we can get away with that too. return false; } [SecuritySafeCritical] - public static void load0(object thisMappedByteBuffer, long address, long length) + public static void load0(object self, long address, long length) { int bogus = bogusField; while (length > 0) @@ -52,18 +54,22 @@ public static void load0(object thisMappedByteBuffer, long address, long length) length -= 4096; address += 4096; } + // do a volatile store of the sum of the bytes to make sure the reads don't get optimized out bogusField = bogus; - GC.KeepAlive(thisMappedByteBuffer); + GC.KeepAlive(self); } [SecuritySafeCritical] - public static void force0(object thisMappedByteBuffer, object fd, long address, long length) + public static void force0(object self, object fd, long address, long length) { - if (JVM.IsUnix) +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsLinux || RuntimeUtil.IsOSX) { - ikvm_msync((IntPtr)address, (int)length); - GC.KeepAlive(thisMappedByteBuffer); + Syscall.msync((IntPtr)address, (ulong)length, MsyncFlags.MS_SYNC); + GC.KeepAlive(self); } else { @@ -73,29 +79,22 @@ public static void force0(object thisMappedByteBuffer, object fd, long address, { if (FlushViewOfFile((IntPtr)address, (IntPtr)length) != 0) { - GC.KeepAlive(thisMappedByteBuffer); + GC.KeepAlive(self); return; } + const int ERROR_LOCK_VIOLATION = 33; if (Marshal.GetLastWin32Error() != ERROR_LOCK_VIOLATION) - { break; - } } -#if !FIRST_PASS throw new global::java.io.IOException("Flush failed"); -#endif } +#endif } [DllImport("kernel32", SetLastError = true)] - private static extern int FlushViewOfFile(IntPtr lpBaseAddress, IntPtr dwNumberOfBytesToFlush); - - private static int ikvm_msync(IntPtr address, int size) => msync(address, size, 0x4); - - [DllImport("libc", EntryPoint = "msync")] - private static extern int msync(IntPtr address, int size, int flags); + static extern int FlushViewOfFile(IntPtr lpBaseAddress, IntPtr dwNumberOfBytesToFlush); } diff --git a/src/IKVM.Runtime/Java/Externs/java/security/AccessController.cs b/src/IKVM.Runtime/Java/Externs/java/security/AccessController.cs index 9c43685636..6607cd01a0 100644 --- a/src/IKVM.Runtime/Java/Externs/java/security/AccessController.cs +++ b/src/IKVM.Runtime/Java/Externs/java/security/AccessController.cs @@ -24,7 +24,6 @@ Jeroen Frijters using System; using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; using IKVM.Internal; @@ -34,26 +33,30 @@ namespace IKVM.Java.Externs.java.security static class AccessController { + /// + /// Implements the native method 'getStackAccessControlContext'. + /// + /// + /// + /// public static object getStackAccessControlContext(global::java.security.AccessControlContext context, global::ikvm.@internal.CallerID callerID) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - List array = new List(); - bool is_privileged = GetProtectionDomains(array, callerID, new StackTrace(1)); + var array = new List(); + var is_privileged = GetProtectionDomains(array, callerID, new StackTrace(1)); if (array.Count == 0) - { if (is_privileged && context == null) - { return null; - } - } + return CreateAccessControlContext(array, is_privileged, context); #endif } #if !FIRST_PASS - private static bool GetProtectionDomains(List array, global::ikvm.@internal.CallerID callerID, StackTrace stack) + + static bool GetProtectionDomains(List array, global::ikvm.@internal.CallerID callerID, StackTrace stack) { // first we have to skip all AccessController related frames, because we can be called from a doPrivileged implementation (not the privileged action) // in which case we should ignore the doPrivileged frame @@ -71,9 +74,8 @@ private static bool GetProtectionDomains(List context, bool is_privileged, global::java.security.AccessControlContext privileged_context) + static object CreateAccessControlContext(List context, bool is_privileged, global::java.security.AccessControlContext privileged_context) { - global::java.security.AccessControlContext acc = new global::java.security.AccessControlContext(context == null || context.Count == 0 ? null : context.ToArray(), is_privileged); + var acc = new global::java.security.AccessControlContext(context == null || context.Count == 0 ? null : context.ToArray(), is_privileged); acc._privilegedContext(privileged_context); return acc; } - private static global::java.security.ProtectionDomain GetProtectionDomainFromType(Type type) + static global::java.security.ProtectionDomain GetProtectionDomainFromType(Type type) { - if (type == null - || type.Assembly == typeof(object).Assembly - || type.Assembly == typeof(AccessController).Assembly - || type.Assembly == IKVM.Java.Externs.java.lang.SecurityManager.jniAssembly - || type.Assembly == typeof(global::java.lang.Thread).Assembly) - { + if (type == null || type.Assembly == typeof(object).Assembly || type.Assembly == typeof(AccessController).Assembly || type.Assembly == IKVM.Java.Externs.java.lang.SecurityManager.jniAssembly || type.Assembly == typeof(global::java.lang.Thread).Assembly) return null; - } - TypeWrapper tw = ClassLoaderWrapper.GetWrapperFromType(type); + + var tw = ClassLoaderWrapper.GetWrapperFromType(type); if (tw != null) - { return IKVM.Java.Externs.java.lang.Class.getProtectionDomain0(tw.ClassObject); - } + return null; } + #endif + /// + /// Implements the native method 'getInheritedAccessControlContext'. + /// + /// public static object getInheritedAccessControlContext() { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - object inheritedAccessControlContext = global::java.lang.Thread.currentThread().inheritedAccessControlContext; - global::java.security.AccessControlContext acc = inheritedAccessControlContext as global::java.security.AccessControlContext; + var inheritedAccessControlContext = global::java.lang.Thread.currentThread().inheritedAccessControlContext; + var acc = inheritedAccessControlContext as global::java.security.AccessControlContext; if (acc != null) - { return acc; - } - global::java.security.AccessController.LazyContext lc = inheritedAccessControlContext as global::java.security.AccessController.LazyContext; + var lc = inheritedAccessControlContext as global::java.security.AccessController.LazyContext; if (lc == null) - { return null; - } - List list = new List(); + var list = new List(); while (lc != null) { if (GetProtectionDomains(list, lc.callerID, lc.stackTrace)) - { return CreateAccessControlContext(list, true, lc.context); - } + lc = lc.parent; } diff --git a/src/IKVM.Runtime/Java/Externs/java/util/TimeZone.cs b/src/IKVM.Runtime/Java/Externs/java/util/TimeZone.cs index 7b9a95934a..d28cd20302 100644 --- a/src/IKVM.Runtime/Java/Externs/java/util/TimeZone.cs +++ b/src/IKVM.Runtime/Java/Externs/java/util/TimeZone.cs @@ -1,312 +1,96 @@ -/* - Copyright (C) 2007-2013 Jeroen Frijters +using System; +using System.IO; - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. +using IKVM.Runtime; - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: +namespace IKVM.Java.Externs.java.util +{ - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. + /// + /// Implements the native methods for 'java.util.TimeZone'. + /// + static class TimeZone + { - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; + /// + /// Gets the current local .NET time zone ID. + /// + /// + static string GetCurrentTimeZoneID() + { + return TimeZoneInfo.Local.Id; + } -namespace IKVM.Java.Externs.java.util -{ + /// + /// Searches the tzmappings file for the specified time zone name. + /// + /// + /// + /// + static string MatchJavaTZ(string javaHome, string tzName) + { + try + { + var mapFileName = Path.Combine(javaHome, "lib", "tzmappings"); + if (File.Exists(mapFileName) == false) + return null; + + using var mapFile = File.OpenText(mapFileName); + string mapLine = null; + while ((mapLine = mapFile.ReadLine()) != null) + { + mapLine = mapLine.Trim(); + if (mapLine.Length < 4) + continue; + if (mapLine[0] == '#') + continue; + + var l = mapLine.Split(':'); + if (l.Length < 4) + continue; - static class TimeZone - { + if (l[0] == tzName) + return l[3]; + } - static string GetCurrentTimeZoneID() - { - return TimeZoneInfo.Local.Id; - } + return null; + } + catch (IOException) + { + return null; + } + } - public static string getSystemTimeZoneID(string javaHome) - { - // (the switch was generated from the contents of $JAVA_HOME/lib/tzmappings) - switch (GetCurrentTimeZoneID()) - { - case "Romance": - case "Romance Standard Time": - return "Europe/Paris"; - case "Warsaw": - return "Europe/Warsaw"; - case "Central Europe": - case "Central Europe Standard Time": - case "Prague Bratislava": - return "Europe/Prague"; - case "W. Central Africa Standard Time": - return "Africa/Luanda"; - case "FLE": - case "FLE Standard Time": - return "Europe/Helsinki"; - case "GFT": - case "GFT Standard Time": - case "GTB": - case "GTB Standard Time": - return "Europe/Athens"; - case "Israel": - case "Israel Standard Time": - return "Asia/Jerusalem"; - case "Arab": - case "Arab Standard Time": - return "Asia/Riyadh"; - case "Arabic Standard Time": - return "Asia/Baghdad"; - case "E. Africa": - case "E. Africa Standard Time": - return "Africa/Nairobi"; - case "Saudi Arabia": - case "Saudi Arabia Standard Time": - return "Asia/Riyadh"; - case "Iran": - case "Iran Standard Time": - return "Asia/Tehran"; - case "Afghanistan": - case "Afghanistan Standard Time": - return "Asia/Kabul"; - case "India": - case "India Standard Time": - return "Asia/Calcutta"; - case "Myanmar Standard Time": - return "Asia/Rangoon"; - case "Nepal Standard Time": - return "Asia/Katmandu"; - case "Sri Lanka": - case "Sri Lanka Standard Time": - return "Asia/Colombo"; - case "Beijing": - case "China": - case "China Standard Time": - return "Asia/Shanghai"; - case "AUS Central": - case "AUS Central Standard Time": - return "Australia/Darwin"; - case "Cen. Australia": - case "Cen. Australia Standard Time": - return "Australia/Adelaide"; - case "Vladivostok": - case "Vladivostok Standard Time": - return "Asia/Vladivostok"; - case "West Pacific": - case "West Pacific Standard Time": - return "Pacific/Guam"; - case "E. South America": - case "E. South America Standard Time": - return "America/Sao_Paulo"; - case "Greenland Standard Time": - return "America/Godthab"; - case "Newfoundland": - case "Newfoundland Standard Time": - return "America/St_Johns"; - case "Pacific SA": - case "Pacific SA Standard Time": - return "America/Santiago"; - case "SA Western": - case "SA Western Standard Time": - return "America/Caracas"; - case "SA Pacific": - case "SA Pacific Standard Time": - return "America/Bogota"; - case "US Eastern": - case "US Eastern Standard Time": - return "America/Indianapolis"; - case "Central America Standard Time": - return "America/Regina"; - case "Mexico": - case "Mexico Standard Time": - return "America/Mexico_City"; - case "Canada Central": - case "Canada Central Standard Time": - return "America/Regina"; - case "US Mountain": - case "US Mountain Standard Time": - return "America/Phoenix"; - case "GMT": - case "GMT Standard Time": - return "Europe/London"; - case "Ekaterinburg": - case "Ekaterinburg Standard Time": - return "Asia/Yekaterinburg"; - case "West Asia": - case "West Asia Standard Time": - return "Asia/Karachi"; - case "Central Asia": - case "Central Asia Standard Time": - return "Asia/Dhaka"; - case "N. Central Asia Standard Time": - return "Asia/Novosibirsk"; - case "Bangkok": - case "Bangkok Standard Time": - return "Asia/Bangkok"; - case "North Asia Standard Time": - return "Asia/Krasnoyarsk"; - case "SE Asia": - case "SE Asia Standard Time": - return "Asia/Bangkok"; - case "North Asia East Standard Time": - return "Asia/Ulaanbaatar"; - case "Singapore": - case "Singapore Standard Time": - return "Asia/Singapore"; - case "Taipei": - case "Taipei Standard Time": - return "Asia/Taipei"; - case "W. Australia": - case "W. Australia Standard Time": - return "Australia/Perth"; - case "Korea": - case "Korea Standard Time": - return "Asia/Seoul"; - case "Tokyo": - case "Tokyo Standard Time": - return "Asia/Tokyo"; - case "Yakutsk": - case "Yakutsk Standard Time": - return "Asia/Yakutsk"; - case "Central European": - case "Central European Standard Time": - return "Europe/Belgrade"; - case "W. Europe": - case "W. Europe Standard Time": - return "Europe/Berlin"; - case "Tasmania": - case "Tasmania Standard Time": - return "Australia/Hobart"; - case "AUS Eastern": - case "AUS Eastern Standard Time": - return "Australia/Sydney"; - case "E. Australia": - case "E. Australia Standard Time": - return "Australia/Brisbane"; - case "Sydney Standard Time": - return "Australia/Sydney"; - case "Central Pacific": - case "Central Pacific Standard Time": - return "Pacific/Guadalcanal"; - case "Dateline": - case "Dateline Standard Time": - return "GMT-1200"; - case "Fiji": - case "Fiji Standard Time": - return "Pacific/Fiji"; - case "Samoa": - case "Samoa Standard Time": - return "Pacific/Apia"; - case "Hawaiian": - case "Hawaiian Standard Time": - return "Pacific/Honolulu"; - case "Alaskan": - case "Alaskan Standard Time": - return "America/Anchorage"; - case "Pacific": - case "Pacific Standard Time": - return "America/Los_Angeles"; - case "Mexico Standard Time 2": - return "America/Chihuahua"; - case "Mountain": - case "Mountain Standard Time": - return "America/Denver"; - case "Central": - case "Central Standard Time": - return "America/Chicago"; - case "Eastern": - case "Eastern Standard Time": - return "America/New_York"; - case "E. Europe": - case "E. Europe Standard Time": - return "Europe/Minsk"; - case "Egypt": - case "Egypt Standard Time": - return "Africa/Cairo"; - case "South Africa": - case "South Africa Standard Time": - return "Africa/Harare"; - case "Atlantic": - case "Atlantic Standard Time": - return "America/Halifax"; - case "SA Eastern": - case "SA Eastern Standard Time": - return "America/Buenos_Aires"; - case "Mid-Atlantic": - case "Mid-Atlantic Standard Time": - return "Atlantic/South_Georgia"; - case "Azores": - case "Azores Standard Time": - return "Atlantic/Azores"; - case "Cape Verde Standard Time": - return "Atlantic/Cape_Verde"; - case "Russian": - case "Russian Standard Time": - return "Europe/Moscow"; - case "New Zealand": - case "New Zealand Standard Time": - return "Pacific/Auckland"; - case "Tonga Standard Time": - return "Pacific/Tongatapu"; - case "Arabian": - case "Arabian Standard Time": - return "Asia/Muscat"; - case "Caucasus": - case "Caucasus Standard Time": - return "Asia/Yerevan"; - case "Greenwich": - case "Greenwich Standard Time": - return "GMT"; - case "Central Brazilian Standard Time": - return "America/Manaus"; - case "Central Standard Time (Mexico)": - return "America/Mexico_City"; - case "Georgian Standard Time": - return "Asia/Tbilisi"; - case "Mountain Standard Time (Mexico)": - return "America/Chihuahua"; - case "Namibia Standard Time": - return "Africa/Windhoek"; - case "Pacific Standard Time (Mexico)": - return "America/Tijuana"; - case "Western Brazilian Standard Time": - return "America/Rio_Branco"; - case "Azerbaijan Standard Time": - return "Asia/Baku"; - case "Jordan Standard Time": - return "Asia/Amman"; - case "Middle East Standard Time": - return "Asia/Beirut"; - default: - // this means fall back to GMT offset - return getSystemGMTOffsetID(); - } - } + /// + /// Implements the native method 'getSystemTimeZoneID'. + /// + /// + /// + public static string getSystemTimeZoneID(string javaHome) + { + if (RuntimeUtil.IsWindows) + return MatchJavaTZ(javaHome, GetCurrentTimeZoneID()) ?? getSystemGMTOffsetID(); + else + return GetCurrentTimeZoneID() ?? getSystemGMTOffsetID(); + } - public static string getSystemGMTOffsetID() - { - TimeSpan sp = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); - int hours = sp.Hours; - int mins = sp.Minutes; - if (hours >= 0 && mins >= 0) - { - return String.Format("GMT+{0:D2}:{1:D2}", hours, mins); - } - else - { - return String.Format("GMT-{0:D2}:{1:D2}", -hours, -mins); - } - } + /// + /// Implements the native method 'getSystemGMTOffsetID'. + /// + /// + public static string getSystemGMTOffsetID() + { + var sp = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); + var h = sp.Hours; + var m = sp.Minutes; + if (h == 0 && m == 0) + return "GMT"; + else if (h >= 0 && m >= 0) + return $"GMT+{h:D2}:{m:D2}"; + else + return $"GMT-{-h:D2}:{-m:D2}"; + } - } + } } diff --git a/src/IKVM.Runtime/Java/Externs/java/util/concurrent/atomic/AtomicLong.cs b/src/IKVM.Runtime/Java/Externs/java/util/concurrent/atomic/AtomicLong.cs new file mode 100644 index 0000000000..b09ca4b7a8 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/java/util/concurrent/atomic/AtomicLong.cs @@ -0,0 +1,22 @@ +namespace IKVM.Java.Externs.java.util.concurrent.atomic +{ + + /// + /// Implements the native backend for 'AtomicLong'. + /// + static class AtomicLong + { + + /// + /// Implements the native method for 'VMSupportsCS8'. We return true since the implementation uses + /// Unsafe methods which we have implemented ontop of Interlocked for long and double values. + /// + /// + public static bool VMSupportsCS8() + { + return true; + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/java/util/zip/ClassStubZipEntry.cs b/src/IKVM.Runtime/Java/Externs/java/util/zip/ClassStubZipEntry.cs index 1f1dec4e48..7290ebee29 100644 --- a/src/IKVM.Runtime/Java/Externs/java/util/zip/ClassStubZipEntry.cs +++ b/src/IKVM.Runtime/Java/Externs/java/util/zip/ClassStubZipEntry.cs @@ -25,7 +25,7 @@ Jeroen Frijters using System; using System.IO; -using IKVM.Internal; +using IKVM.Runtime; using IKVM.Runtime.Vfs; namespace IKVM.Java.Externs.java.util.zip @@ -83,19 +83,19 @@ public override int Read(byte[] buffer, int offset, int count) { // For compatibility with real file i/o, we try to read the requested number // of bytes, instead of returning earlier if the underlying InputStream does so. - int totalRead = 0; + var totalRead = 0; while (count > 0) { - int read = inp.read(buffer, offset, count); + var read = inp.read(buffer, offset, count); if (read <= 0) - { break; - } + offset += read; count -= read; totalRead += read; position += read; } + return totalRead; } @@ -110,23 +110,22 @@ public override long Position if (value < position) { if (value < 0) - { throw new System.IO.IOException("Negative seek offset"); - } + position = 0; inp.close(); inp = zipFile.getInputStream(entry); } - long skip = value - position; + + var skip = value - position; while (skip > 0) { - long skipped = inp.skip(skip); + var skipped = inp.skip(skip); if (skipped == 0) { if (position != entry.getSize()) - { throw new System.IO.IOException("skip failed"); - } + // we're actually at EOF in the InputStream, but we set the virtual position beyond EOF position += skip; break; @@ -156,6 +155,7 @@ public override long Seek(long offset, SeekOrigin origin) Position = entry.getSize() + offset; break; } + return position; } @@ -179,26 +179,35 @@ public override void Close() #endif + /// + /// Implements the native method for 'expandIkvmClasses'. + /// + /// + /// public static void expandIkvmClasses(object _zipFile, object _entries) { -#if !FIRST_PASS - global::java.util.zip.ZipFile zipFile = (global::java.util.zip.ZipFile)_zipFile; - global::java.util.LinkedHashMap entries = (global::java.util.LinkedHashMap)_entries; +#if FIRST_PASS + throw new NotImplementedException(); +#else + var zipFile = (global::java.util.zip.ZipFile)_zipFile; + var entries = (global::java.util.LinkedHashMap)_entries; try { - string path = zipFile.getName(); - global::java.util.zip.ZipEntry entry = (global::java.util.zip.ZipEntry)entries.get(JVM.JarClassList); - if (entry != null && VfsTable.Default.IsPath(path)) + var path = zipFile.getName(); + if (VfsTable.Default.IsPath(path)) { - using (var stream = new ZipEntryStream(zipFile, entry)) + var entry = (global::java.util.zip.ZipEntry)entries.get(JVM.JarClassList); + if (entry != null) { + using var stream = new ZipEntryStream(zipFile, entry); entries.remove(entry.name); - BinaryReader br = new BinaryReader(stream); - int count = br.ReadInt32(); + + var br = new BinaryReader(stream); + var count = br.ReadInt32(); for (int i = 0; i < count; i++) { - global::java.util.zip.ClassStubZipEntry classEntry = new global::java.util.zip.ClassStubZipEntry(path, br.ReadString()); + var classEntry = new global::java.util.zip.ClassStubZipEntry(path, br.ReadString()); classEntry.setMethod(global::java.util.zip.ClassStubZipEntry.STORED); classEntry.setTime(entry.getTime()); entries.put(classEntry.name, classEntry); diff --git a/src/IKVM.Runtime/Java/Externs/sun/management/OperatingSystemImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/management/OperatingSystemImpl.cs index b457beb70b..090ac16358 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/management/OperatingSystemImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/management/OperatingSystemImpl.cs @@ -1,30 +1,11 @@ -/* - Copyright (C) 2011-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; +using System; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +using Mono.Unix.Native; namespace IKVM.Java.Externs.sun.management { @@ -85,8 +66,6 @@ struct PERFORMANCE_INFORMATION } - static OSPlatform platform; - [DllImport("psapi", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetProcessMemoryInfo(IntPtr Process, out PROCESS_MEMORY_COUNTERS ppsmemCounters, uint cb); @@ -99,28 +78,187 @@ struct PERFORMANCE_INFORMATION [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetPerformanceInfo(ref PERFORMANCE_INFORMATION pPerformanceInformation, uint size); + /// + /// Describes the Linux 'sysinfo' structure. + /// + [StructLayout(LayoutKind.Sequential)] + unsafe struct sysinfo_t_x64 + { + + /// + /// Seconds since boot. + /// + public long uptime; + + /// + /// 1, 5, and 15 minute load averages. + /// + public fixed ulong loads[3]; + + /// + /// Total usable main memory size. + /// + public ulong totalram; + + /// + /// Available memory size. + /// + public ulong freeram; + + /// + /// Amount of shared memory. + /// + public ulong sharedram; + + /// + /// Memory used by buffers. + /// + public ulong bufferram; + + /// + /// Total swap space size. + /// + public ulong totalswap; + + /// + /// swap space still available. + /// + public ulong freeswap; + + /// + /// Number of current processes. + /// + public ushort procs; + + /// + /// Number of current processes. + /// + public ushort pad; + + /// + /// Total high memory size. + /// + public ulong totalhigh; + + /// + /// Available high memory size. + /// + public ulong freehigh; + + /// po;llokp + /// Memory unit size in bytes. + /// + public uint mem_unit; + + } + + /// + /// Invokes the native linux method 'sysinfo' for a x64 platform. + /// + /// + /// + [DllImport("libc", EntryPoint = "sysinfo")] + static extern int sysinfo_x64(ref sysinfo_t_x64 info); + + /// + /// Describes the rlimit structure for a x64 Linux platform. + /// + [StructLayout(LayoutKind.Sequential)] + struct rlimit_t_x64 + { + + public ulong rlim_cur; + public ulong rlim_max; + + } + + enum RLIMIT + { + FSIZE = 0, + NOFILE = 1, + CORE = 2, + CPU = 3, + DATA = 4, + STACK = 5, + AS = 6, + } + + /// + /// Invokes the native linux method 'getrlimit' for a x64 platform. + /// + /// + /// + [DllImport("libc", EntryPoint = "getrlimit")] + static extern int getrlimit_x64(RLIMIT resource, ref rlimit_t_x64 info); + + /// + /// Regular expression for the Linux /proc/pid/stat file. + /// + static readonly Regex LinuxProcStatRegex = new Regex(@"^-?\d+ \(.+\) [A-Za-z] -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ (-?\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant); + /// /// Initializes the static information. /// public static void initialize() { - // determine OS platform up front - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - platform = OSPlatform.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - platform = OSPlatform.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - platform = OSPlatform.OSX; + } - public static long getCommittedVirtualMemorySize0(object _this) + /// + /// Implements the native method 'getCommittedVirtualMemorySize0'. + /// + /// + /// + public static long getCommittedVirtualMemorySize0(object self) { - return Process.GetCurrentProcess().PagedMemorySize64; +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Process.GetCurrentProcess().PagedMemorySize64; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + try + { + using var s = File.OpenRead("/proc/self/stat"); + using var r = new StreamReader(s); + + var l = r.ReadLine(); + if (l != null && LinuxProcStatRegex.Match(l) is Match m && m.Groups.Count >= 2) + return (long)ulong.Parse(m.Groups[1].Value); + + throw new global::java.lang.InternalError("Unable to get virtual memory usage"); + } + catch (IOException e) + { + throw new global::java.lang.InternalError("Unable to open or read /proc/self/stat", e); + } + catch (Exception e) + { + throw new global::java.lang.InternalError("Unable to get virtual memory usage", e); + } + } + else + { + return -1; + } +#endif } - public static long getTotalSwapSpaceSize(object _this) + /// + /// Implements the native method 'getTotalSwapSpaceSize'. + /// + /// + /// + /// + public static long getTotalSwapSpaceSize(object self) { - if (platform == OSPlatform.Windows) +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var r = -1L; var p = new MEMORYSTATUSEX(); @@ -130,15 +268,33 @@ public static long getTotalSwapSpaceSize(object _this) return r; } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && IntPtr.Size == 8) + { + var si = new sysinfo_t_x64(); + if (sysinfo_x64(ref si) != 0) + throw new global::java.lang.InternalError("sysinfo failed to get swap size"); + + return (long)si.totalswap * si.mem_unit; + } else { return -1; } +#endif } - public static long getFreeSwapSpaceSize(object _this) + /// + /// Implements the native method 'getFreeSwapSpaceSize'. + /// + /// + /// + /// + public static long getFreeSwapSpaceSize(object self) { - if (platform == OSPlatform.Windows) +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var r = -1L; var p = new MEMORYSTATUSEX(); @@ -148,15 +304,29 @@ public static long getFreeSwapSpaceSize(object _this) return r; } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && IntPtr.Size == 8) + { + var si = new sysinfo_t_x64(); + if (sysinfo_x64(ref si) != 0) + throw new global::java.lang.InternalError("sysinfo failed to get swap size"); + + return (long)si.freeswap * si.mem_unit; + } else { return -1; } +#endif } - public static long getFreePhysicalMemorySize(object _this) + /// + /// Implements the native method 'getFreePhysicalMemorySize'. + /// + /// + /// + public static long getFreePhysicalMemorySize(object self) { - if (platform == OSPlatform.Windows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var m = -1L; var p = new PERFORMANCE_INFORMATION(); @@ -165,15 +335,24 @@ public static long getFreePhysicalMemorySize(object _this) return m; } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return Syscall.sysconf(SysconfName._SC_AVPHYS_PAGES) * Syscall.sysconf(SysconfName._SC_PAGESIZE); + } else { return -1; } } - public static long getTotalPhysicalMemorySize(object _this) + /// + /// Implements the native method 'getTotalPhysicalMemorySize'. + /// + /// + /// + public static long getTotalPhysicalMemorySize(object self) { - if (platform == OSPlatform.Windows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var m = -1L; var p = new PERFORMANCE_INFORMATION(); @@ -182,27 +361,99 @@ public static long getTotalPhysicalMemorySize(object _this) return m; } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return Syscall.sysconf(SysconfName._SC_PHYS_PAGES); + } else { return -1; } } - public static long getProcessCpuTime(object _this) + /// + /// Implements the native method 'getProcessCpuTime'. + /// + /// + /// + public static long getProcessCpuTime(object self) { return Process.GetCurrentProcess().TotalProcessorTime.Ticks * 100; } - public static double getSystemCpuLoad(object _this) + /// + /// Implements the native method 'getSystemCpuLoad'. + /// + /// + /// + public static double getSystemCpuLoad(object self) { return -1; } - public static double getProcessCpuLoad(object _this) + /// + /// Implements the native method 'getProcessCpuLoad'. + /// + /// + /// + public static double getProcessCpuLoad(object self) { return -1; } + /// + /// Implements the native method 'getOpenFileDescriptorCount'. + /// + /// + /// + public static long getOpenFileDescriptorCount(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + try + { + return Directory.GetFiles("/proc/self/fd").Length; + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } + } + else + { + return -1; + } +#endif + } + + /// + /// Implements the native method 'getMaxFileDescriptorCount'. + /// + /// + /// + public static long getMaxFileDescriptorCount(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && IntPtr.Size == 8) + { + var rlp = new rlimit_t_x64(); + if (getrlimit_x64(RLIMIT.NOFILE, ref rlp) == -1) + throw new global::java.lang.InternalError("getrlimit failed"); + + return (long)rlp.rlim_cur; + } + else + { + return -1; + } +#endif + } + } } \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/misc/Signal.cs b/src/IKVM.Runtime/Java/Externs/sun/misc/Signal.cs index 3025bd0747..0322f22751 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/misc/Signal.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/misc/Signal.cs @@ -29,12 +29,12 @@ Jeroen Frijters using System.Runtime.InteropServices; using System.Security; -using IKVM.Internal; +using IKVM.Runtime; namespace IKVM.Java.Externs.sun.misc { - static class Signal + static class Signal { /* derived from version 6.0 VC98/include/signal.h */ @@ -103,11 +103,12 @@ private static bool ConsoleCtrlCheck(CtrlTypes ctrlType) } #if !FIRST_PASS + private static void DumpAllJavaThreads() { Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); global::java.util.Map traces = global::java.lang.Thread.getAllStackTraces(); - Console.WriteLine("Full thread dump IKVM.NET {0} ({1} bit):", JVM.SafeGetAssemblyVersion(Assembly.GetExecutingAssembly()), IntPtr.Size * 8); + Console.WriteLine("Full thread dump IKVM.NET {0} ({1} bit):", Assembly.GetExecutingAssembly().GetName().Version, IntPtr.Size * 8); global::java.util.Iterator entries = traces.entrySet().iterator(); while (entries.hasNext()) { @@ -123,6 +124,7 @@ private static void DumpAllJavaThreads() } Console.WriteLine(); } + #endif public static int findSignal(string sigName) diff --git a/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs b/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs index 27f0cf4d0e..417efe93f0 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/misc/Unsafe.cs @@ -1,39 +1,16 @@ -/* - Copyright (C) 2007-2015 Jeroen Frijters - Copyright (C) 2009 Volker Berlin (i-net software) - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; +using System; +using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Security; using System.Threading; +using IKVM.ByteCode.Reading; using IKVM.Internal; +using IKVM.Runtime; namespace IKVM.Java.Externs.sun.misc { @@ -41,445 +18,2446 @@ namespace IKVM.Java.Externs.sun.misc static class Unsafe { - public static global::java.lang.reflect.Field createFieldAndMakeAccessible(global::java.lang.Class c, string fieldName) + /// + /// Holds a reference to the various delegates that facilitate unsafe operations for arrays. References are kept + /// alive and associated with the element type of the array in a conditional weak table. + /// + class ArrayDelegateRef + { + + readonly Lazy volatileGetter; + readonly Lazy volatilePutter; + readonly Lazy compareExchange; + + /// + /// Initializes a new instance. + /// + /// + public ArrayDelegateRef(TypeWrapper type) + { + volatileGetter = new Lazy(() => CreateGetArrayVolatileDelegate(type), true); + volatilePutter = new Lazy(() => CreatePutArrayVolatileDelegate(type), true); + compareExchange = new Lazy(() => CreateCompareExchangeArrayDelegate(type), true); + } + + /// + /// Gets a delegate capable of implementing the volatile get logic. This value is a + /// + public Delegate VolatileGetter => volatileGetter.Value; + + /// + /// Gets a delegate capable of implemetning the volatile put logic. This value is an + /// + public Delegate VolatilePutter => volatilePutter.Value; + + /// + /// Gets a delegate capable of implemetning the compare and exchange logic. This value is an + /// + public Delegate CompareExchange => compareExchange.Value; + + } + + /// + /// Cache of delegates for array operations. + /// + static readonly ConditionalWeakTable arrayRefCache = new ConditionalWeakTable(); + + /// + /// Generic CompareExchange method. + /// + static readonly MethodInfo compareAndSwapArrayMethodInfo = typeof(Unsafe).GetMethods(BindingFlags.NonPublic | BindingFlags.Static) + .Where(i => i.Name == nameof(CompareAndSwapArray) && i.IsGenericMethodDefinition && i.GetGenericArguments().Length == 1) + .FirstOrDefault(); + + /// + /// Emits the appropriate ldind opcode for the given type. + /// + /// + /// + /// + static void EmitLdind(ILGenerator il, Type t) + { + if (t == typeof(bool)) + il.Emit(OpCodes.Ldind_U1); + else if (t == typeof(byte)) + il.Emit(OpCodes.Ldind_U1); + else if (t == typeof(char)) + il.Emit(OpCodes.Ldind_U2); + else if (t == typeof(short)) + il.Emit(OpCodes.Ldind_I2); + else if (t == typeof(int)) + il.Emit(OpCodes.Ldind_I4); + else if (t == typeof(long)) + il.Emit(OpCodes.Ldind_I8); + else if (t == typeof(float)) + il.Emit(OpCodes.Ldind_R4); + else if (t == typeof(double)) + il.Emit(OpCodes.Ldind_R8); + else + il.Emit(OpCodes.Ldind_Ref); + } + + /// + /// Emits the appropriate ldind opcode for the given type. + /// + /// + /// + /// + static void EmitStind(ILGenerator il, Type t) + { + if (t == typeof(bool)) + il.Emit(OpCodes.Stind_I1); + else if (t == typeof(byte)) + il.Emit(OpCodes.Stind_I1); + else if (t == typeof(char)) + il.Emit(OpCodes.Stind_I2); + else if (t == typeof(short)) + il.Emit(OpCodes.Stind_I2); + else if (t == typeof(int)) + il.Emit(OpCodes.Stind_I4); + else if (t == typeof(long)) + il.Emit(OpCodes.Stind_I8); + else if (t == typeof(float)) + il.Emit(OpCodes.Stind_R4); + else if (t == typeof(double)) + il.Emit(OpCodes.Stind_R8); + else + il.Emit(OpCodes.Stind_Ref); + } + + /// + /// Implementation of native method 'registerNatives'. + /// + public static void registerNatives() + { + + } + + /// + /// Implements the logic to get a field by offset. + /// + /// + /// + /// + /// + /// + static T GetField(object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + return FieldWrapper.FromCookie((IntPtr)offset).UnsafeGetValue(o); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Implements the logic to set a field by offset. + /// + /// + /// + /// + /// + /// + static void PutField(object o, long offset, T value) + + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + FieldWrapper.FromCookie((IntPtr)offset).UnsafeSetValue(o, value); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Implementation of native method 'getObject'. + /// + /// + /// + /// + /// + public static object getObject(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + object[] array => array[offset], + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putObject'. + /// + /// + /// + /// + /// + public static void putObject(object self, object o, long offset, object x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case object[] array: + array[offset] = x; + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getBoolean'. + /// + /// + /// + /// + /// + public static bool getBoolean(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => Buffer.GetByte(array, (int)offset) != 0, + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putBoolean'. + /// + /// + /// + /// + /// + public static void putBoolean(object self, object o, long offset, bool x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + Buffer.SetByte(array, (int)offset, x ? (byte)1 : (byte)0); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getByte'. + /// + /// + /// + /// + /// + public static byte getByte(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => Buffer.GetByte(array, (int)offset), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putByte'. + /// + /// + /// + /// + /// + public static void putByte(object self, object o, long offset, byte x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + Buffer.SetByte(array, (int)offset, x); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getShort'. + /// + /// + /// + /// + /// + public static short getShort(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => ReadInt16(array, offset), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putShort'. + /// + /// + /// + /// + /// + public static void putShort(object self, object o, long offset, short x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt16(array, offset, x); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getChar'. + /// + /// + /// + /// + /// + public static char getChar(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => (char)ReadInt16(array, offset), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putChar'. + /// + /// + /// + /// + /// + public static void putChar(object self, object o, long offset, char x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt16(array, offset, (short)x); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getInt'. + /// + /// + /// + /// + /// + public static int getInt(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => ReadInt32(array, offset), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putInt'. + /// + /// + /// + /// + /// + public static void putInt(object self, object o, long offset, int x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt32(array, offset, x); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getLong'. + /// + /// + /// + /// + /// + public static long getLong(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => ReadInt64(array, offset), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putLong'. + /// + /// + /// + /// + /// + public static void putLong(object self, object o, long offset, long x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt64(array, offset, x); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getFloat'. + /// + /// + /// + /// + /// + public static float getFloat(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => global::java.lang.Float.intBitsToFloat(ReadInt32(array, offset)), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putFloat'. + /// + /// + /// + /// + /// + public static void putFloat(object self, object o, long offset, float x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt32(array, offset, global::java.lang.Float.floatToRawIntBits(x)); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getDouble'. + /// + /// + /// + /// + /// + public static double getDouble(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => global::java.lang.Double.longBitsToDouble(ReadInt64(array, offset)), + _ => GetField(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putDouble'. + /// + /// + /// + /// + /// + public static void putDouble(object self, object o, long offset, double x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt64(array, offset, global::java.lang.Double.doubleToRawLongBits(x)); + break; + default: + PutField(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getByte'. + /// + /// + /// + /// + public static byte getByte(object self, long address) + { + return Marshal.ReadByte((IntPtr)address); + } + + /// + /// Implementation of native method 'putByte'. + /// + /// + /// + /// + public static void putByte(object self, long address, byte x) + { + Marshal.WriteByte((IntPtr)address, x); + } + + /// + /// Implementation of native method 'getShort'. + /// + /// + /// + /// + public static short getShort(object self, long address) + { + return Marshal.ReadInt16((IntPtr)address); + } + + /// + /// Implementation of native method 'putShort'. + /// + /// + /// + /// + public static void putShort(object self, long address, short x) + { + Marshal.WriteInt16((IntPtr)address, x); + } + + /// + /// Implementation of native method 'getChar'. + /// + /// + /// + /// + public static char getChar(object self, long address) + { + return (char)Marshal.ReadInt16((IntPtr)address); + } + + /// + /// Implementation of native method 'putChar'. + /// + /// + /// + /// + public static void putChar(object self, long address, char x) + { + Marshal.WriteInt16((IntPtr)address, (short)x); + } + + /// + /// Implementation of native method 'getInt'. + /// + /// + /// + /// + public static int getInt(object self, long address) + { + return Marshal.ReadInt32((IntPtr)address); + } + + /// + /// Implementation of native method 'putInt'. + /// + /// + /// + /// + public static void putInt(object self, long address, int x) + { + Marshal.WriteInt32((IntPtr)address, x); + } + + /// + /// Implementation of native method 'getLong'. + /// + /// + /// + /// + public static long getLong(object self, long address) + { + return Marshal.ReadInt64((IntPtr)address); + } + + /// + /// Implementation of native method 'putLong'. + /// + /// + /// + /// + public static void putLong(object self, long address, long x) + { + Marshal.WriteInt64((IntPtr)address, x); + } + + /// + /// Implementation of native method 'getFloat'. + /// + /// + /// + /// + public static float getFloat(object self, long address) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return global::java.lang.Float.intBitsToFloat(getInt(self, address)); +#endif + } + + /// + /// Implementation of native method 'putFloat'. + /// + /// + /// + /// + public static void putFloat(object self, long address, float x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + putInt(self, address, global::java.lang.Float.floatToIntBits(x)); +#endif + } + + /// + /// Implementation of native method 'getDouble'. + /// + /// + /// + /// + public static double getDouble(object self, long address) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return global::java.lang.Double.longBitsToDouble(getLong(self, address)); +#endif + } + + /// + /// Implementation of native method 'putDouble'. + /// + /// + /// + /// + public static void putDouble(object self, long address, double x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + putLong(self, address, global::java.lang.Double.doubleToLongBits(x)); +#endif + } + + /// + /// Implementation of native method 'getAddress'. + /// + /// + /// + /// + public static long getAddress(object self, long address) + { + return Marshal.ReadIntPtr((IntPtr)address).ToInt64(); + } + + /// + /// Implementation of native method 'putAddress'. + /// + /// + /// + /// + public static void putAddress(object self, long address, long x) + { + Marshal.WriteIntPtr((IntPtr)address, (IntPtr)x); + } + + /// + /// Implementation of native method 'allocateMemory'. + /// + /// + /// + /// + /// + public static long allocateMemory(object self, long bytes) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (bytes == 0) + return 0; + + try + { + return Marshal.AllocHGlobal((IntPtr)bytes).ToInt64(); + } + catch (OutOfMemoryException e) + { + throw new global::java.lang.OutOfMemoryError(e.Message).initCause(e); + } +#endif + } + + /// + /// Implementation of native method 'reallocateMemory'. + /// + /// + /// + /// + /// + /// + public static long reallocateMemory(object self, long address, long bytes) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (bytes == 0) + { + freeMemory(self, address); + return 0; + } + + try + { + return Marshal.ReAllocHGlobal((IntPtr)address, (IntPtr)bytes).ToInt64(); + } + catch (OutOfMemoryException e) + { + throw new global::java.lang.OutOfMemoryError(e.Message).initCause(e); + } +#endif + } + + /// + /// Implementation of native method 'setMemory'. + /// + /// + /// + /// + /// + /// + /// + public static unsafe void setMemory(object self, object o, long offset, long bytes, byte value) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (o == null) + new Span((void*)(IntPtr)offset, (int)bytes).Fill(value); + else if (o is byte[] array) + ((Span)array).Slice((int)offset, (int)bytes).Fill(value); + else if (o is Array array2) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(array2, GCHandleType.Pinned); + new Span((byte*)h.AddrOfPinnedObject(), Buffer.ByteLength(array2)).Slice((int)offset, (int)bytes).Fill(value); + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + else + throw new global::java.lang.IllegalArgumentException(); +#endif + } + + /// + /// Implementation of native method 'copyMemory'. + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe void copyMemory(object self, object srcBase, long srcOffset, object destBase, long destOffset, long bytes) + { + if (srcBase == null) + { + if (destBase is byte[] byteArray) + { + Marshal.Copy((IntPtr)srcOffset, byteArray, (int)destOffset, (int)bytes); + } + else if (destBase is bool[]) + { + var tmp = new byte[(int)bytes]; + copyMemory(self, srcBase, srcOffset, tmp, 0, bytes); + copyMemory(self, tmp, 0, destBase, destOffset, bytes); + } + else if (destBase is short[] shortArray) + { + Marshal.Copy((IntPtr)srcOffset, shortArray, (int)(destOffset >> 1), (int)(bytes >> 1)); + } + else if (destBase is char[] charArray) + { + Marshal.Copy((IntPtr)srcOffset, charArray, (int)(destOffset >> 1), (int)(bytes >> 1)); + } + else if (destBase is int[] intArray) + { + Marshal.Copy((IntPtr)srcOffset, intArray, (int)(destOffset >> 2), (int)(bytes >> 2)); + } + else if (destBase is float[] floatArray) + { + Marshal.Copy((IntPtr)srcOffset, floatArray, (int)(destOffset >> 2), (int)(bytes >> 2)); + } + else if (destBase is long[] longArray) + { + Marshal.Copy((IntPtr)srcOffset, longArray, (int)(destOffset >> 3), (int)(bytes >> 3)); + } + else if (destBase is double[] doubleArray) + { + Marshal.Copy((IntPtr)srcOffset, doubleArray, (int)(destOffset >> 3), (int)(bytes >> 3)); + } + else if (destBase == null) + { + Buffer.MemoryCopy((void*)(IntPtr)srcOffset, (void*)(IntPtr)destOffset, long.MaxValue, bytes); + } + else + { + throw new global::java.lang.IllegalArgumentException(); + } + } + else if (srcBase is Array && destBase is Array) + { + Buffer.BlockCopy((Array)srcBase, (int)srcOffset, (Array)destBase, (int)destOffset, (int)bytes); + } + else + { + if (srcBase is byte[] byteArray) + { + Marshal.Copy(byteArray, (int)srcOffset, (IntPtr)destOffset, (int)bytes); + } + else if (srcBase is bool[]) + { + var tmp = new byte[(int)bytes]; + copyMemory(self, srcBase, srcOffset, tmp, 0, bytes); + copyMemory(self, tmp, 0, destBase, destOffset, bytes); + } + else if (srcBase is short[] shortArray) + { + Marshal.Copy(shortArray, (int)(srcOffset >> 1), (IntPtr)(destOffset), (int)(bytes >> 1)); + } + else if (srcBase is char[] charArray) + { + Marshal.Copy(charArray, (int)(srcOffset >> 1), (IntPtr)(destOffset), (int)(bytes >> 1)); + } + else if (srcBase is int[] intArray) + { + Marshal.Copy(intArray, (int)(srcOffset >> 2), (IntPtr)(destOffset), (int)(bytes >> 2)); + } + else if (srcBase is float[] floatArray) + { + Marshal.Copy(floatArray, (int)(srcOffset >> 2), (IntPtr)(destOffset), (int)(bytes >> 2)); + } + else if (srcBase is long[] longArray) + { + Marshal.Copy(longArray, (int)(srcOffset >> 3), (IntPtr)(destOffset), (int)(bytes >> 3)); + } + else if (srcBase is double[] doubleArray) + { + Marshal.Copy(doubleArray, (int)(srcOffset >> 3), (IntPtr)(destOffset), (int)(bytes >> 3)); + } + else + { + throw new global::java.lang.IllegalArgumentException(); + } + } + } + + /// + /// Implementation of native method 'freeMemory'. + /// + /// + /// + public static void freeMemory(object self, long address) + { + Marshal.FreeHGlobal((IntPtr)address); + } + + /// + /// Implementation of native method 'staticFieldOffset'. + /// + /// + /// + /// + public static long staticFieldOffset(object self, global::java.lang.reflect.Field f) + { + return (long)FieldWrapper.FromField(f).Cookie; + } + + /// + /// Implementation of native method 'objectFieldOffset'. + /// + /// + /// + /// + public static long objectFieldOffset(object self, global::java.lang.reflect.Field f) + { + return (long)FieldWrapper.FromField(f).Cookie; + } + + /// + /// Implementation of native method 'staticFieldBase'. + /// + /// + /// + /// + public static object staticFieldBase(object self, global::java.lang.reflect.Field f) + { + return null; + } + + /// + /// Implementation of native method 'shouldBeInitialized'. + /// + /// + /// + /// + public static bool shouldBeInitialized(object self, global::java.lang.Class c) + { + return TypeWrapper.FromClass(c).HasStaticInitializer; + } + + /// + /// Implementation of native method 'ensureClassInitialized'. + /// + /// + /// + public static void ensureClassInitialized(object self, global::java.lang.Class c) + { + var tw = TypeWrapper.FromClass(c); + if (tw.IsArray == false) + { + try + { + tw.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + + tw.RunClassInit(); + } + } + + /// + /// Implementation of native method 'arrayBaseOffset'. + /// + /// + /// + /// + public static int arrayBaseOffset(object self, global::java.lang.Class arrayClass) + { + return 0; + } + + /// + /// Implementation of native method 'arrayIndexScale'. + /// + /// + /// + /// + public static int arrayIndexScale(object self, global::java.lang.Class arrayClass) + { + var tw = TypeWrapper.FromClass(arrayClass); + var ac = tw.TypeAsTBD; + + if (ac == typeof(byte[]) || ac == typeof(bool[])) + return 1; + + if (ac == typeof(char[]) || ac == typeof(short[])) + return 2; + + if (ac == typeof(int[]) || ac == typeof(float[]) || ac == typeof(object[])) + return 4; + + if (ac == typeof(long[]) || ac == typeof(double[])) + return 8; + + // don't change this, the Unsafe intrinsics depend on this value + return 1; + } + + /// + /// Implementation of native method 'addressSize'. + /// + /// + /// + public static int addressSize(object self) + { + return IntPtr.Size; + } + + /// + /// Implementation of native method 'pageSize'. + /// + /// + /// + public static int pageSize(object self) + { + return Environment.SystemPageSize; + } + + /// + /// Implementation of native method 'defineClass'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.lang.Class defineClass(object self, string name, byte[] b, int off, int len, global::java.lang.ClassLoader loader, global::java.security.ProtectionDomain protectionDomain) + { + return IKVM.Java.Externs.java.lang.ClassLoader.defineClass1(loader, name.Replace('/', '.'), b, off, len, protectionDomain, null); + } + + /// + /// Implementation of native method 'defineAnonymousClass'. + /// + /// + /// + /// + /// + /// + /// + public static global::java.lang.Class defineAnonymousClass(object self, global::java.lang.Class hostClass, byte[] data, object[] cpPatches) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + var tw = TypeWrapper.FromClass(hostClass); + var cl = tw.GetClassLoader(); + var cf = new ClassFile(ClassReader.Read(data), "", cl.ClassFileParseOptions, cpPatches); + if (cf.IKVMAssemblyAttribute != null) + { + // if this happens, the OpenJDK is probably trying to load an OpenJDK class file as a resource, + // make sure the build process includes the original class file as a resource in that case + throw new global::java.lang.ClassFormatError("Trying to define anonymous class based on stub class: " + cf.Name); + } + + return cl.GetTypeWrapperFactory().DefineClassImpl(null, tw, cf, cl, hostClass.pd).ClassObject; + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } +#endif + } + + /// + /// Implementation of native method 'allocateInstance'. + /// + /// + /// + /// + public static object allocateInstance(object self, global::java.lang.Class cls) + { + var wrapper = TypeWrapper.FromClass(cls); + try + { + wrapper.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + + return FormatterServices.GetUninitializedObject(wrapper.TypeAsBaseType); + } + + /// + /// Implementation of native method 'monitorEnter'. + /// + /// + /// + public static void monitorEnter(object self, object o) + { + Monitor.Enter(o); + } + + /// + /// Implementation of native method 'monitorExit'. + /// + /// + /// + public static void monitorExit(object self, object o) + { + Monitor.Exit(o); + } + + /// + /// Implementation of native method 'tryMonitorEnter'. + /// + /// + /// + /// + public static bool tryMonitorEnter(object self, object o) + { + return Monitor.TryEnter(o); + } + + /// + /// Implementation of native method 'throwException'. + /// + /// + /// + public static void throwException(object self, Exception ee) + { + throw ee; + } + + /// + /// Implements the logic to get a field by offset using volatile. + /// + /// + /// + /// + /// + /// + static T GetFieldVolatile(object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + return FieldWrapper.FromCookie((IntPtr)offset).UnsafeVolatileGet(o); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Implements the logic to set a field by offset using volatile. + /// + /// + /// + /// + /// + /// + static void PutFieldVolatile(object o, long offset, T value) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + FieldWrapper.FromCookie((IntPtr)offset).UnsafeVolatileSet(o, value); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Creates a delegate capable of accessing an index of a specific type. + /// + /// + /// + static Delegate CreateGetArrayVolatileDelegate(TypeWrapper tw) + { + var et = tw.IsPrimitive ? tw.TypeAsTBD : typeof(object); + var dm = DynamicMethodUtil.Create($"____{tw.Name.Replace(".", "_")}", tw.TypeAsTBD, true, et, new[] { typeof(object[]), typeof(long) }); + var il = dm.GetILGenerator(); + + // load reference to element + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Conv_Ovf_I); + il.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType); + + if (tw.IsWidePrimitive == false) + { + il.Emit(OpCodes.Volatile); + EmitLdind(il, tw.TypeAsLocalOrStackType); + } + else + { + // Java volatile semantics require atomicity, CLR volatile semantics do not + var mi = typeof(Unsafe).GetMethod(nameof(InterlockedRead), BindingFlags.NonPublic | BindingFlags.Static, null, new[] { tw.TypeAsTBD.MakeByRefType() }, null); + il.Emit(OpCodes.Call, mi); + } + + il.Emit(OpCodes.Ret); + return dm.CreateDelegate(typeof(Func<,,>).MakeGenericType(typeof(object[]), typeof(long), et)); + } + + /// + /// Implements the logic to get an object array by offset using volatile. + /// + /// + /// + /// + static object GetArrayObjectVolatile(object[] array, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + return ((Func)arrayRefCache.GetValue(ClassLoaderWrapper.GetWrapperFromType(array.GetType().GetElementType()), _ => new ArrayDelegateRef(_)).VolatileGetter)(array, offset); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Creates a delegate capable of accessing an index of a specific type. + /// + /// + /// + static Delegate CreatePutArrayVolatileDelegate(TypeWrapper tw) + { + var et = tw.IsPrimitive ? tw.TypeAsTBD : typeof(object); + var dm = DynamicMethodUtil.Create($"____{tw.Name.Replace(".", "_")}", tw.TypeAsTBD, true, typeof(void), new[] { typeof(object[]), typeof(long), et }); + var il = dm.GetILGenerator(); + + // load reference to element + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Conv_Ovf_I); + il.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Ldarg_2); + + if (tw.IsWidePrimitive == false) + { + il.Emit(OpCodes.Volatile); + EmitStind(il, tw.TypeAsLocalOrStackType); + } + else + { + // Java volatile semantics require atomicity, CLR volatile semantics do not + var mi = typeof(Interlocked).GetMethod(nameof(Interlocked.Exchange), new[] { tw.TypeAsTBD.MakeByRefType() }); + il.Emit(OpCodes.Call, mi); + } + + il.Emit(OpCodes.Ret); + return dm.CreateDelegate(typeof(Action<,,>).MakeGenericType(typeof(object[]), typeof(long), et)); + } + + /// + /// Implements the logic to set an object array by offset using volatile. + /// + /// + /// + /// + /// + static void PutArrayObjectVolatile(object[] array, long offset, object value) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + ((Action)arrayRefCache.GetValue(ClassLoaderWrapper.GetWrapperFromType(array.GetType().GetElementType()), _ => new ArrayDelegateRef(_)).VolatilePutter)(array, offset, value); + } + catch (Exception e) + { + throw new global::java.lang.InternalError(e); + } +#endif + } + + /// + /// Implementation of native method 'getObjectVolatile'. + /// + /// + /// + /// + /// + public static object getObjectVolatile(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case object[] array when array.GetType() == typeof(object[]): + return Volatile.Read(ref array[offset]); + case object[] array: + return GetArrayObjectVolatile(array, offset); + default: + return GetFieldVolatile(o, offset); + } +#endif + } + + /// + /// Implementation of native method 'putObjectVolatile'. + /// + /// + /// + /// + /// + public static void putObjectVolatile(object self, object o, long offset, object x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case object[] array when array.GetType() == typeof(object[]): + Volatile.Write(ref array[offset], x); + break; + case object[] array: + PutArrayObjectVolatile(array, offset, x); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getIntVolatile'. + /// + /// + /// + /// + /// + public static int getIntVolatile(object self, object o, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => ReadInt32Volatile(array, offset), + _ => GetFieldVolatile(o, offset) + }; +#endif + } + + /// + /// Implementation of native method 'putIntVolatile'. + /// + /// + /// + /// + /// + public static void putIntVolatile(object self, object o, long offset, int x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt32Volatile(array, offset, x); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getBooleanVolatile'. + /// + /// + /// + /// + /// + public static bool getBooleanVolatile(object self, object o, long offset) + { + return getBoolean(self, o, offset); + } + + /// + /// Implementation of native method 'putBooleanVolatile'. + /// + /// + /// + /// + /// + public static void putBooleanVolatile(object self, object o, long offset, bool x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteByteVolatile(array, offset, x ? (byte)1 : (byte)0); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif + } + + /// + /// Implementation of native method 'getByteVolatile'. + /// + /// + /// + /// + /// + public static byte getByteVolatile(object self, object o, long offset) + { + return getByte(self, o, offset); + } + + /// + /// Implementation of native method 'putByteVolatile'. + /// + /// + /// + /// + /// + public static void putByteVolatile(object self, object o, long offset, byte x) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - // we pass in ReflectAccess.class as the field type (which isn't used) - // to make Field.toString() return something "meaningful" instead of crash - global::java.lang.reflect.Field field = new global::java.lang.reflect.Field(c, fieldName, global::ikvm.@internal.ClassLiteral.Value, 0, -1, null, null); - field.@override = true; - return field; + switch (o) + { + case Array array: + WriteByteVolatile(array, offset, x); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } #endif } - public static global::java.lang.reflect.Field copyFieldAndMakeAccessible(global::java.lang.reflect.Field field) + /// + /// Implementation of native method 'getShortVolatile'. + /// + /// + /// + /// + /// + public static short getShortVolatile(object self, object o, long offset) + { + return o switch + { + Array array => ReadInt16Volatile(array, offset), + _ => GetFieldVolatile(o, offset) + }; + } + + /// + /// Implementation of native method 'putShortVolatile'. + /// + /// + /// + /// + /// + public static void putShortVolatile(object self, object o, long offset, short x) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else - field = new global::java.lang.reflect.Field(field.getDeclaringClass(), field.getName(), field.getType(), field.getModifiers() & ~global::java.lang.reflect.Modifier.FINAL, field._slot(), null, null); - field.@override = true; - return field; + switch (o) + { + case Array array: + WriteInt16Volatile(array, offset, x); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } #endif } - private static void CheckArrayBounds(object obj, long offset, int accessLength) + /// + /// Implementation of native method 'getCharVolatile'. + /// + /// + /// + /// + /// + public static char getCharVolatile(object self, object o, long offset) + { + return o switch + { + Array array => (char)ReadInt16Volatile(array, offset), + _ => GetFieldVolatile(o, offset) + }; + } + + /// + /// Implementation of native method 'putCharVolatile'. + /// + /// + /// + /// + /// + public static void putCharVolatile(object self, object o, long offset, char x) { - // NOTE we rely on the fact that Buffer.ByteLength() requires a primitive array - int arrayLength = Buffer.ByteLength((Array)obj); - if (offset < 0 || offset > arrayLength - accessLength || accessLength > arrayLength) +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) { - throw new IndexOutOfRangeException(); + case Array array: + WriteInt16Volatile(array, offset, (short)x); + break; + default: + PutFieldVolatile(o, offset, x); + break; } +#endif } - [SecuritySafeCritical] - public static short ReadInt16(object obj, long offset) + /// + /// Implementation of native method 'getLongVolatile'. + /// + /// + /// + /// + /// + public static long getLongVolatile(object self, object o, long offset) { - Stats.Log("ReadInt16"); - CheckArrayBounds(obj, offset, 2); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - short value = Marshal.ReadInt16((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset)); - handle.Free(); - return value; +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => ReadInt64Volatile(array, offset), + _ => GetFieldVolatile(o, offset) + }; +#endif } - [SecuritySafeCritical] - public static int ReadInt32(object obj, long offset) + /// + /// Implementation of native method 'putLongVolatile'. + /// + /// + /// + /// + /// + public static void putLongVolatile(object self, object o, long offset, long x) { - Stats.Log("ReadInt32"); - CheckArrayBounds(obj, offset, 4); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - int value = Marshal.ReadInt32((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset)); - handle.Free(); - return value; +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt64Volatile(array, offset, x); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif } - [SecuritySafeCritical] - public static long ReadInt64(object obj, long offset) + /// + /// Implementation of native method 'getFloatVolatile'. + /// + /// + /// + /// + /// + /// + public static float getFloatVolatile(object self, object o, long offset) { - Stats.Log("ReadInt64"); - CheckArrayBounds(obj, offset, 8); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - long value = Marshal.ReadInt64((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset)); - handle.Free(); - return value; +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => global::java.lang.Float.intBitsToFloat(ReadInt32Volatile(array, offset)), + _ => GetFieldVolatile(o, offset) + }; +#endif } - [SecuritySafeCritical] - public static void WriteInt16(object obj, long offset, short value) + /// + /// Implementation of native method 'putFloatVolatile'. + /// + /// + /// + /// + /// + public static void putFloatVolatile(object self, object o, long offset, float x) { - Stats.Log("WriteInt16"); - CheckArrayBounds(obj, offset, 2); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - Marshal.WriteInt16((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset), value); - handle.Free(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt32Volatile(array, offset, global::java.lang.Float.floatToRawIntBits(x)); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif } - [SecuritySafeCritical] - public static void WriteInt32(object obj, long offset, int value) + /// + /// Implementation of native method 'getDoubleVolatile'. + /// + /// + /// + /// + /// + /// + public static double getDoubleVolatile(object self, object o, long offset) { - Stats.Log("WriteInt32"); - CheckArrayBounds(obj, offset, 4); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - Marshal.WriteInt32((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset), value); - handle.Free(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + Array array => global::java.lang.Double.longBitsToDouble(ReadInt64Volatile(array, offset)), + _ => GetFieldVolatile(o, offset) + }; +#endif } - [SecuritySafeCritical] - public static void WriteInt64(object obj, long offset, long value) + /// + /// Implementation of native method 'putDoubleVolatile'. + /// + /// + /// + /// + /// + public static void putDoubleVolatile(object self, object o, long offset, double x) { - Stats.Log("WriteInt64"); - CheckArrayBounds(obj, offset, 8); - GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned); - Marshal.WriteInt64((IntPtr)(handle.AddrOfPinnedObject().ToInt64() + offset), value); - handle.Free(); +#if FIRST_PASS + throw new NotImplementedException(); +#else + switch (o) + { + case Array array: + WriteInt64Volatile(array, offset, global::java.lang.Double.doubleToRawLongBits(x)); + break; + default: + PutFieldVolatile(o, offset, x); + break; + } +#endif } - public static void throwException(object thisUnsafe, Exception x) + public static void putOrderedObject(object self, object o, long offset, object x) { - throw x; + putObjectVolatile(self, o, offset, x); } - public static bool shouldBeInitialized(object thisUnsafe, global::java.lang.Class clazz) + public static void putOrderedInt(object self, object o, long offset, int x) { - return TypeWrapper.FromClass(clazz).HasStaticInitializer; + putIntVolatile(self, o, offset, x); } - public static void ensureClassInitialized(object thisUnsafe, global::java.lang.Class clazz) + public static void putOrderedLong(object self, object o, long offset, long x) { - TypeWrapper tw = TypeWrapper.FromClass(clazz); - if (!tw.IsArray) - { - try - { - tw.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - tw.RunClassInit(); - } + putLongVolatile(self, o, offset, x); } - [SecurityCritical] - public static object allocateInstance(object thisUnsafe, global::java.lang.Class clazz) + /// + /// Implements the logic to compare and swap an object. + /// + /// + /// + /// + /// + /// + /// + /// + static bool CompareAndSwapField(object o, long offset, T expected, T value) { - TypeWrapper wrapper = TypeWrapper.FromClass(clazz); +#if FIRST_PASS + throw new NotImplementedException(); +#else try { - wrapper.Finish(); + return FieldWrapper.FromCookie((IntPtr)offset).UnsafeCompareAndSwap(o, expected, value); } - catch (RetargetableJavaException x) + catch (Exception e) { - throw x.ToJava(); + throw new global::java.lang.InternalError(e); } - return FormatterServices.GetUninitializedObject(wrapper.TypeAsBaseType); +#endif } - public static global::java.lang.Class defineClass(object thisUnsafe, string name, byte[] buf, int offset, int length, global::java.lang.ClassLoader cl, global::java.security.ProtectionDomain pd) + /// + /// Creates a delegate capable of accessing an index of a specific type. + /// + /// + /// + static Delegate CreateCompareExchangeArrayDelegate(TypeWrapper tw) { - return IKVM.Java.Externs.java.lang.ClassLoader.defineClass1(cl, name.Replace('/', '.'), buf, offset, length, pd, null); + var p = Expression.Parameter(typeof(object[])); + var i = Expression.Parameter(typeof(long)); + var v = Expression.Parameter(typeof(object)); + var e = Expression.Parameter(typeof(object)); + return Expression.Lambda>( + Expression.Call( + compareAndSwapArrayMethodInfo.MakeGenericMethod(tw.TypeAsTBD), + Expression.Convert(p, tw.MakeArrayType(1).TypeAsTBD), + i, + Expression.Convert(v, tw.TypeAsTBD), + Expression.Convert(e, tw.TypeAsTBD)), + p, i, v, e) + .Compile(); } - public static global::java.lang.Class defineClass(object thisUnsafe, string name, byte[] buf, int offset, int length, global::ikvm.@internal.CallerID callerID) + /// + /// Implements CompareAndSwap for a typed array. + /// + /// + /// + /// + /// + /// + /// + static object CompareAndSwapArray(T[] o, long offset, T value, T comparand) + where T : class { -#if FIRST_PASS - return null; -#else - return defineClass(thisUnsafe, name, buf, offset, length, callerID.getCallerClassLoader(), callerID.getCallerClass().pd); -#endif + return Interlocked.CompareExchange(ref o[offset], value, comparand); } - public static global::java.lang.Class defineAnonymousClass(object thisUnsafe, global::java.lang.Class host, byte[] data, object[] cpPatches) + /// + /// Implements the logic to compare and swap an object. + /// + /// + /// + /// + /// + /// + /// + /// + static object CompareAndSwapObjectArray(object[] o, long offset, object value, object expected) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else try { - ClassLoaderWrapper loader = TypeWrapper.FromClass(host).GetClassLoader(); - ClassFile classFile = new ClassFile(data, 0, data.Length, "", loader.ClassFileParseOptions, cpPatches); - if (classFile.IKVMAssemblyAttribute != null) - { - // if this happens, the OpenJDK is probably trying to load an OpenJDK class file as a resource, - // make sure the build process includes the original class file as a resource in that case - throw new global::java.lang.ClassFormatError("Trying to define anonymous class based on stub class: " + classFile.Name); - } - return loader.GetTypeWrapperFactory().DefineClassImpl(null, TypeWrapper.FromClass(host), classFile, loader, host.pd).ClassObject; + return ((Func)arrayRefCache.GetValue(ClassLoaderWrapper.GetWrapperFromType(o.GetType().GetElementType()), _ => new ArrayDelegateRef(_)).CompareExchange)(o, offset, value, expected); } - catch (RetargetableJavaException x) + catch (Exception e) { - throw x.ToJava(); + throw new global::java.lang.InternalError(e); } #endif } - public static bool compareAndSwapInt(object thisUnsafe, object obj, long offset, int expect, int update) + /// + /// Implementation of native method 'compareAndSwapObject'. + /// + /// + /// + /// + /// + /// + /// + /// + public static bool compareAndSwapObject(object self, object o, long offset, object expected, object x) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return o switch + { + object[] array when array.GetType() == typeof(object[]) => Interlocked.CompareExchange(ref array[offset], x, expected) == expected, + object[] array => CompareAndSwapObjectArray(array, offset, x, expected) == expected, + _ => CompareAndSwapField(o, offset, expected, x) + }; +#endif + } + + /// + /// Implementation of native method 'compareAndSwapInt'. + /// + /// + /// + /// + /// + /// + /// + /// + public static bool compareAndSwapInt(object self, object o, long offset, int expected, int x) { #if FIRST_PASS - return false; + throw new NotImplementedException(); #else - int[] array = obj as int[]; - if (array != null && (offset & 3) == 0) + if (o is int[] array && (offset % sizeof(int)) == 0) { - Stats.Log("compareAndSwapInt.array"); - return Interlocked.CompareExchange(ref array[offset / 4], update, expect) == expect; + return Interlocked.CompareExchange(ref array[offset / sizeof(int)], x, expected) == expected; } - else if (obj is Array) + else if (o is Array array1 && (offset % sizeof(int)) == 0) { - Stats.Log("compareAndSwapInt.unaligned"); - // unaligned or not the right array type, so we can't be atomic - lock (thisUnsafe) - { - if (ReadInt32(obj, offset) == expect) - { - WriteInt32(obj, offset, update); - return true; - } - return false; - } + return CompareExchangeInt32(array1, offset, x, expected) == expected; + } + else if (o is Array array2) + { + return CompareExchangeInt32Unaligned(array2, offset, x, expected) == expected; } else { - if (offset >= cacheCompareExchangeInt32.Length || cacheCompareExchangeInt32[offset] == null) - { - InterlockedResize(ref cacheCompareExchangeInt32, (int)offset + 1); - cacheCompareExchangeInt32[offset] = (CompareExchangeInt32)CreateCompareExchange(offset); - } - Stats.Log("compareAndSwapInt.", offset); - return cacheCompareExchangeInt32[offset](obj, update, expect) == expect; + return CompareAndSwapField(o, offset, expected, x); } #endif } - public static bool compareAndSwapLong(object thisUnsafe, object obj, long offset, long expect, long update) + /// + /// Implementation of native method 'compareAndSwapLong'. + /// + /// + /// + /// + /// + /// + /// + /// + public static bool compareAndSwapLong(object self, object o, long offset, long expected, long x) { #if FIRST_PASS - return false; + throw new NotImplementedException(); #else - long[] array = obj as long[]; - if (array != null && (offset & 7) == 0) + if (o is long[] array && (offset % sizeof(long)) == 0) { - Stats.Log("compareAndSwapLong.array"); - return Interlocked.CompareExchange(ref array[offset / 8], update, expect) == expect; + return Interlocked.CompareExchange(ref array[offset / sizeof(long)], x, expected) == expected; } - else if (obj is Array) + else if (o is Array array1 && (offset % sizeof(long)) == 0) { - Stats.Log("compareAndSwapLong.unaligned"); - // unaligned or not the right array type, so we can't be atomic - lock (thisUnsafe) - { - if (ReadInt64(obj, offset) == expect) - { - WriteInt64(obj, offset, update); - return true; - } - return false; - } + return CompareExchangeInt64(array1, offset, x, expected) == expected; + } + else if (o is Array array2) + { + return CompareExchangeInt64Unaligned(array2, offset, x, expected) == expected; } else { - if (offset >= cacheCompareExchangeInt64.Length || cacheCompareExchangeInt64[offset] == null) - { - InterlockedResize(ref cacheCompareExchangeInt64, (int)offset + 1); - cacheCompareExchangeInt64[offset] = (CompareExchangeInt64)CreateCompareExchange(offset); - } - Stats.Log("compareAndSwapLong.", offset); - return cacheCompareExchangeInt64[offset](obj, update, expect) == expect; + return CompareAndSwapField(o, offset, expected, x); } #endif } - private delegate int CompareExchangeInt32(object obj, int value, int comparand); - private delegate long CompareExchangeInt64(object obj, long value, long comparand); - private delegate object CompareExchangeObject(object obj, object value, object comparand); - private static CompareExchangeInt32[] cacheCompareExchangeInt32 = new CompareExchangeInt32[0]; - private static CompareExchangeInt64[] cacheCompareExchangeInt64 = new CompareExchangeInt64[0]; - private static CompareExchangeObject[] cacheCompareExchangeObject = new CompareExchangeObject[0]; + public static void unpark(object self, object thread) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + global::java.util.concurrent.locks.LockSupport.unpark((global::java.lang.Thread)thread); +#endif + } - private static void InterlockedResize(ref T[] array, int newSize) + public static void park(object self, bool isAbsolute, long time) { - for (; ; ) +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (isAbsolute) { - T[] oldArray = array; - T[] newArray = oldArray; - if (oldArray.Length >= newSize) - { - return; - } - Array.Resize(ref newArray, newSize); - if (Interlocked.CompareExchange(ref array, newArray, oldArray) == oldArray) - { - return; - } + global::java.util.concurrent.locks.LockSupport.parkUntil(time); + } + else + { + if (time == 0) + time = global::java.lang.Long.MAX_VALUE; + + global::java.util.concurrent.locks.LockSupport.parkNanos(time); + } +#endif + } + + public static int getLoadAverage(object self, double[] loadavg, int nelems) + { + return -1; + } + + public static void loadFence(object self) + { + Thread.MemoryBarrier(); + } + + public static void storeFence(object self) + { + Thread.MemoryBarrier(); + } + + public static void fullFence(object self) + { + Thread.MemoryBarrier(); + } + + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe short ReadInt16(Array obj, long offset) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var v = System.Runtime.CompilerServices.Unsafe.ReadUnaligned((byte*)h.AddrOfPinnedObject() + offset); + return v; + } + finally + { + if (h.IsAllocated) + h.Free(); } } -#if !FIRST_PASS - private static Delegate CreateCompareExchange(long fieldOffset) + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe short ReadInt16Volatile(Array obj, long offset) { - FieldInfo field = GetFieldInfo(fieldOffset); - bool primitive = field.FieldType.IsPrimitive; - Type signatureType = primitive ? field.FieldType : typeof(object); - MethodInfo compareExchange; - Type delegateType; - if (signatureType == typeof(int)) + GCHandle h = new(); + + try { - compareExchange = InterlockedMethods.CompareExchangeInt32; - delegateType = typeof(CompareExchangeInt32); + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + ref var r = ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset); + var v = Volatile.Read(ref r); + return v; } - else if (signatureType == typeof(long)) + finally { - compareExchange = InterlockedMethods.CompareExchangeInt64; - delegateType = typeof(CompareExchangeInt64); + if (h.IsAllocated) + h.Free(); } - else + } + + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe int ReadInt32(Array obj, long offset) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var v = System.Runtime.CompilerServices.Unsafe.ReadUnaligned((byte*)h.AddrOfPinnedObject() + offset); + return v; + } + finally { - compareExchange = InterlockedMethods.CompareExchangeOfT.MakeGenericMethod(field.FieldType); - delegateType = typeof(CompareExchangeObject); + if (h.IsAllocated) + h.Free(); } - DynamicMethod dm = new DynamicMethod("CompareExchange", signatureType, new Type[] { typeof(object), signatureType, signatureType }, field.DeclaringType); - ILGenerator ilgen = dm.GetILGenerator(); - // note that we don't bother will special casing static fields, because it is legal to use ldflda on a static field - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Castclass, field.DeclaringType); - ilgen.Emit(OpCodes.Ldflda, field); - ilgen.Emit(OpCodes.Ldarg_1); - if (!primitive) + } + + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe int ReadInt32Volatile(Array obj, long offset) + { + GCHandle h = new(); + + try { - ilgen.Emit(OpCodes.Castclass, field.FieldType); + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + ref var r = ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset); + var v = Volatile.Read(ref r); + return v; } - ilgen.Emit(OpCodes.Ldarg_2); - if (!primitive) + finally { - ilgen.Emit(OpCodes.Castclass, field.FieldType); + if (h.IsAllocated) + h.Free(); } - ilgen.Emit(OpCodes.Call, compareExchange); - ilgen.Emit(OpCodes.Ret); - return dm.CreateDelegate(delegateType); } - private static FieldInfo GetFieldInfo(long offset) + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe long ReadInt64(Array obj, long offset) { - FieldWrapper fw = FieldWrapper.FromField(global::sun.misc.Unsafe.getField(offset)); - fw.Link(); - fw.ResolveField(); - return fw.GetField(); + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var v = System.Runtime.CompilerServices.Unsafe.ReadUnaligned((byte*)h.AddrOfPinnedObject() + offset); + return v; + } + finally + { + if (h.IsAllocated) + h.Free(); + } } -#endif - public static bool compareAndSwapObject(object thisUnsafe, object obj, long offset, object expect, object update) + /// + /// Reads an from the array at the specific byte offset. + /// + /// + /// + /// + static unsafe long ReadInt64Volatile(Array obj, long offset) { -#if FIRST_PASS - return false; -#else - object[] array = obj as object[]; - if (array != null) + GCHandle h = new(); + + try { - Stats.Log("compareAndSwapObject.array"); - return Atomic.CompareExchange(array, (int)offset, update, expect) == expect; + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + ref var r = ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset); + var v = Interlocked.Read(ref r); // java considers volatile to be atomic + return v; } - else + finally { - if (offset >= cacheCompareExchangeObject.Length || cacheCompareExchangeObject[offset] == null) - { - InterlockedResize(ref cacheCompareExchangeObject, (int)offset + 1); - cacheCompareExchangeObject[offset] = (CompareExchangeObject)CreateCompareExchange(offset); - } - Stats.Log("compareAndSwapObject.", offset); - return cacheCompareExchangeObject[offset](obj, update, expect) == expect; + if (h.IsAllocated) + h.Free(); } -#endif } - abstract class Atomic + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteByte(Array obj, long offset, byte value) { - // NOTE we don't care that we keep the Type alive, because Unsafe should only be used inside the core class libraries - private static Dictionary impls = new Dictionary(); + GCHandle h = new(); - internal static object CompareExchange(object[] array, int index, object value, object comparand) + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((byte*)h.AddrOfPinnedObject() + offset, value); + } + finally { - return GetImpl(array.GetType().GetElementType()).CompareExchangeImpl(array, index, value, comparand); + if (h.IsAllocated) + h.Free(); } + } + + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteByteVolatile(Array obj, long offset, byte value) + { + GCHandle h = new(); - private static Atomic GetImpl(Type type) + try { - Atomic impl; - if (!impls.TryGetValue(type, out impl)) - { - impl = (Atomic)Activator.CreateInstance(typeof(Impl<>).MakeGenericType(type)); - Dictionary curr = impls; - Dictionary copy = new Dictionary(curr); - copy[type] = impl; - Interlocked.CompareExchange(ref impls, copy, curr); - } - return impl; + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + Volatile.Write(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value); + } + finally + { + if (h.IsAllocated) + h.Free(); } + } - protected abstract object CompareExchangeImpl(object[] array, int index, object value, object comparand); + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt16(Array obj, long offset, short value) + { + GCHandle h = new(); - sealed class Impl : Atomic - where T : class + try { - protected override object CompareExchangeImpl(object[] array, int index, object value, object comparand) - { - return Interlocked.CompareExchange(ref ((T[])array)[index], (T)value, (T)comparand); - } + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((byte*)h.AddrOfPinnedObject() + offset, value); + } + finally + { + if (h.IsAllocated) + h.Free(); } } - static class Stats + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt16Volatile(Array obj, long offset, short value) { -#if !FIRST_PASS && UNSAFE_STATISTICS - private static readonly Dictionary dict = new Dictionary(); + GCHandle h = new(); - static Stats() - { - java.lang.Runtime.getRuntime().addShutdownHook(new DumpStats()); - } + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + Volatile.Write(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value); + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } - sealed class DumpStats : java.lang.Thread - { - public override void run() - { - List> list = new List>(dict); - list.Sort(delegate(KeyValuePair kv1, KeyValuePair kv2) { return kv1.Value.CompareTo(kv2.Value); }); - foreach (KeyValuePair kv in list) - { - Console.WriteLine("{0,10}: {1}", kv.Value, kv.Key); - } - } - } -#endif + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt32(Array obj, long offset, int value) + { + GCHandle h = new(); - [Conditional("UNSAFE_STATISTICS")] - internal static void Log(string key) + try { -#if !FIRST_PASS && UNSAFE_STATISTICS - lock (dict) - { - int count; - dict.TryGetValue(key, out count); - dict[key] = count + 1; - } -#endif + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((byte*)h.AddrOfPinnedObject() + offset, value); + } + finally + { + if (h.IsAllocated) + h.Free(); } + } - [Conditional("UNSAFE_STATISTICS")] - internal static void Log(string key, long offset) + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt32Volatile(Array obj, long offset, int value) + { + GCHandle h = new(); + + try { -#if !FIRST_PASS && UNSAFE_STATISTICS - FieldWrapper field = FieldWrapper.FromField(sun.misc.Unsafe.getField(offset)); - key += field.DeclaringType.Name + "::" + field.Name; - Log(key); -#endif + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + Volatile.Write(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value); + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt64(Array obj, long offset, long value) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((byte*)h.AddrOfPinnedObject() + offset, value); + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Writes a to the specified byte offset within the array. + /// + /// + /// + /// + static unsafe void WriteInt64Volatile(Array obj, long offset, long value) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + Interlocked.Exchange(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value); + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Executes CompareExchange against the specified byte offset with the array. + /// + /// + /// + /// + /// + /// + static unsafe int CompareExchangeInt32(Array obj, long offset, int value, int expected) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var r = Interlocked.CompareExchange(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value, expected); + return r; + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Executes CompareExchange against the specified byte offset with the array. + /// + /// + /// + /// + /// + /// + static unsafe int CompareExchangeInt32Unaligned(Array obj, long offset, int value, int expected) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var r = Interlocked.CompareExchange(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value, expected); + return r; } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Executes CompareExchange against the specified byte offset with the array. + /// + /// + /// + /// + /// + /// + static unsafe long CompareExchangeInt64(Array obj, long offset, long value, long expected) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var r = Interlocked.CompareExchange(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value, expected); + return r; + } + finally + { + h.Free(); + } + } + + /// + /// Executes CompareExchange against the specified byte offset with the array. + /// + /// + /// + /// + /// + /// + static unsafe long CompareExchangeInt64Unaligned(Array obj, long offset, long value, long expected) + { + GCHandle h = new(); + + try + { + h = GCHandle.Alloc(obj, GCHandleType.Pinned); + var r = Interlocked.CompareExchange(ref System.Runtime.CompilerServices.Unsafe.AsRef((byte*)h.AddrOfPinnedObject() + offset), value, expected); + return r; + } + finally + { + if (h.IsAllocated) + h.Free(); + } + } + + /// + /// Implements an Interlocked.Read method for long. + /// + /// + /// + static long InterlockedRead(ref long location) + { + return Interlocked.Read(ref location); + } + + /// + /// Implements an Interlocked.Read method for double. + /// + /// + /// + static double InterlockedRead(ref double location) + { + return Interlocked.CompareExchange(ref location, 0, 0); + } + + /// + /// Implements an Interlocked.Read method for long. + /// + /// + /// + static void InterlockedWrite(ref long location, long value) + { + Interlocked.Exchange(ref location, value); + } + + /// + /// Implements an Interlocked.Read method for double. + /// + /// + /// + static void InterlockedWrite(ref double location, double value) + { + Interlocked.Exchange(ref location, value); } } -} \ No newline at end of file +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/misc/VM.cs b/src/IKVM.Runtime/Java/Externs/sun/misc/VM.cs index fcd6a3832b..2d0473d327 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/misc/VM.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/misc/VM.cs @@ -24,56 +24,61 @@ Jeroen Frijters */ using System; using System.Diagnostics; -using System.Reflection; using IKVM.Internal; +using IKVM.Runtime; namespace IKVM.Java.Externs.sun.misc { static class VM - { + { - public static void initialize() - { + public static void initialize() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + JVM.EnsureInitialized(); +#endif + } - } + public static global::java.lang.ClassLoader latestUserDefinedLoader() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var trace = new StackTrace(2, false); + for (var i = 0; i < trace.FrameCount; i++) + { + var f = trace.GetFrame(i); + var m = f.GetMethod(); + if (m == null) + continue; - public static global::java.lang.ClassLoader latestUserDefinedLoader() - { - // testing shows that it is cheaper the get the full stack trace and then look at a few frames than getting the frames individually - StackTrace trace = new StackTrace(2, false); - for (int i = 0; i < trace.FrameCount; i++) - { - StackFrame frame = trace.GetFrame(i); - MethodBase method = frame.GetMethod(); - if (method == null) - { - continue; - } - Type type = method.DeclaringType; - if (type != null) - { - TypeWrapper tw = ClassLoaderWrapper.GetWrapperFromType(type); - if (tw != null) - { - ClassLoaderWrapper classLoader = tw.GetClassLoader(); - AssemblyClassLoader acl = classLoader as AssemblyClassLoader; - if (acl == null || acl.GetAssembly(tw) != typeof(object).Assembly) - { - global::java.lang.ClassLoader javaClassLoader = classLoader.GetJavaClassLoader(); - if (javaClassLoader != null) - { - return javaClassLoader; - } - } - } - } - } + // not to be considered from Java + if (global::IKVM.Java.Externs.sun.reflect.Reflection.IsHideFromStackWalk(m)) + continue; - return null; - } + if (m.DeclaringType != null && ClassLoaderWrapper.GetWrapperFromType(m.DeclaringType) is TypeWrapper tw and not null) + { + // check that the assembly isn't java.base or the IKVM runtime + var clw = tw.GetClassLoader(); + if (clw is AssemblyClassLoader acl) + if (acl.GetAssembly(tw) == typeof(object).Assembly || acl.GetAssembly(tw) == typeof(VM).Assembly) + continue; - } + // associated Java class loader is our nearest + var cl = clw.GetJavaClassLoader(); + if (cl != null) + return cl; + } + } + + return null; +#endif + } + + } } \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/net/PortConfig.cs b/src/IKVM.Runtime/Java/Externs/sun/net/PortConfig.cs index 39846faa1d..75c43b5d97 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/net/PortConfig.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/net/PortConfig.cs @@ -1,42 +1,92 @@ -/* - Copyright (C) 2007-2015 Jeroen Frijters - Copyright (C) 2009 Volker Berlin (i-net software) - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -namespace IKVM.Java.Externs.sun.net.spi +using System.IO; + +using IKVM.Runtime; + +namespace IKVM.Java.Externs.sun.net { + /// + /// Implements the native methods for 'sun.net.PortConfig'. + /// static class PortConfig { + const int WindowsDefaultLowerPortRange = 49152; + const int WindowsDefaultUpperPortRange = 65535; + const string LinuxIpLocalPortRangeFile = "/proc/sys/net/ipv4/ip_local_port_range"; + const int LinuxDefaultLowerPortRange = 32768; + const int LinuxDefaultUpperPortRange = 60999; + const int OSXDefaultLowerPortRange = 49152; + const int OSXDefaultUpperPortRange = 65535; + + /// + /// Implements the native method 'getLower0'. + /// + /// public static int getLower0() { - return 49152; + if (RuntimeUtil.IsWindows) + { + return WindowsDefaultLowerPortRange; + } + else if (RuntimeUtil.IsLinux) + { + if (File.Exists(LinuxIpLocalPortRangeFile) == false) + return LinuxDefaultLowerPortRange; + + var f = File.ReadAllText(LinuxIpLocalPortRangeFile); + var a = f.Split('\t'); + if (a.Length < 2) + return LinuxDefaultLowerPortRange; + + if (int.TryParse(a[0], out var i)) + return i; + + return LinuxDefaultLowerPortRange; + } + else if (RuntimeUtil.IsOSX) + { + return OSXDefaultLowerPortRange; + } + else + { + return -1; + } } + /// + /// Implements the native method 'getUpper0'. + /// + /// public static int getUpper0() { - return 65535; + if (RuntimeUtil.IsWindows) + { + return WindowsDefaultUpperPortRange; + } + else if (RuntimeUtil.IsLinux) + { + if (File.Exists(LinuxIpLocalPortRangeFile) == false) + return LinuxDefaultUpperPortRange; + + var f = File.ReadAllText(LinuxIpLocalPortRangeFile); + var a = f.Split('\t'); + if (a.Length < 2) + return LinuxDefaultUpperPortRange; + + if (int.TryParse(a[1], out var i)) + return i; + + return LinuxDefaultUpperPortRange; + } + else if (RuntimeUtil.IsOSX) + { + return OSXDefaultUpperPortRange; + } + else + { + return -1; + } } } diff --git a/src/IKVM.Runtime/Java/Externs/gnu/java/net/protocol/ikvmres/Handler.cs b/src/IKVM.Runtime/Java/Externs/sun/net/www/protocol/ikvmres/Handler.cs similarity index 90% rename from src/IKVM.Runtime/Java/Externs/gnu/java/net/protocol/ikvmres/Handler.cs rename to src/IKVM.Runtime/Java/Externs/sun/net/www/protocol/ikvmres/Handler.cs index 508517e1c5..7f32edadad 100644 --- a/src/IKVM.Runtime/Java/Externs/gnu/java/net/protocol/ikvmres/Handler.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/net/www/protocol/ikvmres/Handler.cs @@ -21,17 +21,17 @@ Jeroen Frijters jeroen@frijters.net */ -using System; using System.IO; using System.Reflection; using IKVM.Internal; +using IKVM.Runtime; #if NETCOREAPP using System.Runtime.Loader; #endif -namespace IKVM.Java.Externs.gnu.java.net.protocol.ikvmres +namespace IKVM.Java.Externs.sun.net.www.protocol.ikvmres { static class Handler @@ -39,10 +39,9 @@ static class Handler public static byte[] GenerateStub(global::java.lang.Class c) { - MemoryStream mem = new MemoryStream(); -#if !FIRST_PASS - bool includeNonPublicInterfaces = !"true".Equals(global::java.lang.Props.props.getProperty("ikvm.stubgen.skipNonPublicInterfaces"), StringComparison.OrdinalIgnoreCase); - StubGen.StubGenerator.WriteClass(mem, TypeWrapper.FromClass(c), includeNonPublicInterfaces, false, false, true); + using var mem = new MemoryStream(); +#if FIRST_PASS == false + StubGen.StubGenerator.WriteClass(mem, TypeWrapper.FromClass(c), true, true, true, true, false); #endif return mem.ToArray(); } diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramChannelImpl.cs index 5de84d01de..c811e1dba2 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramChannelImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramChannelImpl.cs @@ -1,147 +1,269 @@ -/* - Copyright (C) 2011 Jeroen Frijters +using System; +using System.Buffers; +using System.Linq.Expressions; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; +using IKVM.Runtime.Util.Java.Net; namespace IKVM.Java.Externs.sun.nio.ch { + /// + /// Implements the native methods of 'sun.nio.ch.DatagramChannelImpl'. + /// static class DatagramChannelImpl { +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static DatagramChannelImplAccessor datagramChannelImplAccessor; + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static DatagramChannelImplAccessor DatagramChannelImplAccessor => JVM.BaseAccessors.Get(ref datagramChannelImplAccessor); + + /// + /// Compiles a fast setter for a . + /// + /// + /// + /// + /// + static Action MakeFieldSetter(FieldInfo field) + { + var p = Expression.Parameter(typeof(T)); + var v = Expression.Parameter(typeof(V)); + var e = Expression.Lambda>(Expression.Assign(Expression.Field(field.DeclaringType.IsValueType ? Expression.Unbox(p, field.DeclaringType) : Expression.ConvertChecked(p, field.DeclaringType), field), v), p, v); + return e.Compile(); + } + +#if NETCOREAPP + + // HACK .NET Core has an explicit check for _isConnected https://github.com/dotnet/runtime/issues/77962 + static readonly FieldInfo SocketIsConnectedField = typeof(Socket).GetField("_isConnected", BindingFlags.NonPublic | BindingFlags.Instance); + static readonly Action SocketIsConnectedFieldSetter = SocketIsConnectedField != null ? MakeFieldSetter(SocketIsConnectedField) : null; + +#endif + + const IOControlCode SIO_UDP_CONNRESET = (IOControlCode)(-1744830452); + static readonly byte[] IOControlTrueBuffer = BitConverter.GetBytes(1); + static readonly byte[] IOControlFalseBuffer = BitConverter.GetBytes(0); + static readonly byte[] TempBuffer = new byte[1]; + + /// + /// Peek at the queue to see if there is an ICMP port unreachable. If there is, then receive it. + /// + /// + static void PurgeOutstandingICMP(Socket socket) + { + while (true) + { + // check for outstanding packet + if (socket.Poll(0, SelectMode.SelectRead) == false) + break; + + try + { + var ep = (EndPoint)new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + socket.EndReceiveFrom(socket.BeginReceiveFrom(TempBuffer, 0, TempBuffer.Length, SocketFlags.Peek, ref ep, null, null), ref ep); + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + try + { + var ep = (EndPoint)new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + socket.EndReceiveFrom(socket.BeginReceiveFrom(TempBuffer, 0, TempBuffer.Length, SocketFlags.Peek, ref ep, null, null), ref ep); + } + catch (SocketException e2) when (e2.SocketErrorCode == SocketError.ConnectionReset) + { + + } + + continue; + } + + break; + } + } + +#endif + public static void initIDs() { } - public static void disconnect0(global::java.io.FileDescriptor fd, bool isIPv6) + /// + /// Implements the native method 'disconnect0'. + /// + /// + /// + public static void disconnect0(object fd, bool isIPv6) { #if FIRST_PASS - throw new NotSupportedException(); + throw new NotImplementedException(); #else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + try { - fd.getSocket().Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0)); - Net.setConnectionReset(fd.getSocket(), false); +#if NETCOREAPP + // HACK .NET Core has an explicit check for _isConnected https://github.com/dotnet/runtime/issues/77962 + if (SocketIsConnectedFieldSetter != null) + SocketIsConnectedFieldSetter(socket, false); +#endif + + // NOTE we use async connect to work around the issue that the .NET Socket class disallows sync Connect after the socket has received WSAECONNRESET + socket.EndConnect(socket.BeginConnect(new IPEndPoint(IPAddress.IPv6Any, 0), null, null)); + + // disable WSAECONNRESET errors as socket is no longer connected + if (RuntimeUtil.IsWindows) + socket.IOControl(SIO_UDP_CONNRESET, IOControlFalseBuffer, null); } - catch (System.Net.Sockets.SocketException e) + catch (SocketException e) { - throw global::java.net.SocketUtil.convertSocketExceptionToIOException(e); + throw e.ToIOException(); } catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed."); + throw new global::java.net.SocketException("Socket closed."); } #endif } - public static int receive0(object self, global::java.io.FileDescriptor fd, byte[] buf, int pos, int len, bool connected) + /// + /// Implements the native 'receive0' method. + /// + /// + /// + /// + /// + /// + /// + public static unsafe int receive0(object self, object fd, long address, int len, bool connected) { #if FIRST_PASS - throw new NotSupportedException(); + throw new NotImplementedException(); #else - var impl = (global::sun.nio.ch.DatagramChannelImpl)self; - var remoteAddress = impl.remoteAddress(); - System.Net.EndPoint remoteEP; - if (fd.getSocket().AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) - remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.IPv6Any, 0); - else - remoteEP = new System.Net.IPEndPoint(0, 0); - - global::java.net.InetSocketAddress addr; - int length; - do + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + var remoteEndpoint = (EndPoint)new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + var length = 0; + + try { - for (; ; ) + var packet = ArrayPool.Shared.Rent(len); + + try { - try - { - length = fd.getSocket().ReceiveFrom(buf, pos, len, System.Net.Sockets.SocketFlags.None, ref remoteEP); - break; - } - catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == System.Net.Sockets.SocketError.ConnectionReset) - { - // A previous send failed (i.e. the remote host responded with a ICMP that the port is closed) and - // the winsock stack helpfully lets us know this, but we only care about this when we're connected, - // otherwise we'll simply retry the receive (note that we use SIO_UDP_CONNRESET to prevent these - // WSAECONNRESET exceptions, but when switching from connected to disconnected, some can slip through). - if (connected) - throw new global::java.net.PortUnreachableException(); - - continue; - } - catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == System.Net.Sockets.SocketError.MessageSize) - { - // The buffer size was too small for the packet, ReceiveFrom receives the part of the packet - // that fits in the buffer and then throws an exception, so we have to ignore the exception in this case. - length = len; - break; - } - catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == System.Net.Sockets.SocketError.WouldBlock) - { - return global::sun.nio.ch.IOStatus.UNAVAILABLE; - } - catch (System.Net.Sockets.SocketException e) - { - throw global::java.net.SocketUtil.convertSocketExceptionToIOException(e); - } - catch (ObjectDisposedException) - { - throw new global::java.net.SocketException("Socket is closed"); - } + length = socket.ReceiveFrom(packet, 0, len, SocketFlags.None, ref remoteEndpoint); + Marshal.Copy(packet, 0, (IntPtr)address, length); } + finally + { + ArrayPool.Shared.Return(packet); + } + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.MessageSize) + { + // buffer should be filled with as much as possible, and we should indicate that, but the rest is discarded + length = len; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // Windows may leave multiple ICMP packets on the socket, purge them + if (RuntimeUtil.IsWindows) + PurgeOutstandingICMP(socket); - var ep = (System.Net.IPEndPoint)remoteEP; - addr = new global::java.net.InetSocketAddress(global::java.net.SocketUtil.getInetAddressFromIPEndPoint(ep), ep.Port); + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionRefused) + { + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); } - while (remoteAddress != null && !addr.equals(remoteAddress)); - impl.sender = addr; + // check that we received an IP endpoint + if (remoteEndpoint is not IPEndPoint ipRemoteEndpoint) + throw new global::java.net.SocketException("Unexpected resulting endpoint type."); + + // update remote address if it has changed + var remoteAddress = DatagramChannelImplAccessor.InvokeRemoteAddress(self); + if (remoteAddress == null || ipRemoteEndpoint.ToInetSocketAddress().equals(remoteAddress) == false) + DatagramChannelImplAccessor.SetSender(self, ipRemoteEndpoint.ToInetSocketAddress()); + return length; #endif } - public static int send0(object obj, bool preferIPv6, global::java.io.FileDescriptor fd, byte[] buf, int pos, int len, global::java.net.InetAddress addr, int port) + /// + /// Implements the native method 'send0'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe int send0(object self, bool preferIPv6, object fd, long address, int len, global::java.net.InetAddress addr, int port) { #if FIRST_PASS - throw new NotSupportedException(); + throw new NotImplementedException(); #else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try { - return fd.getSocket().SendTo(buf, pos, len, System.Net.Sockets.SocketFlags.None, new System.Net.IPEndPoint(global::java.net.SocketUtil.getAddressFromInetAddress(addr, preferIPv6), port)); + var packet = ArrayPool.Shared.Rent(len); + + try + { + Marshal.Copy((IntPtr)address, packet, 0, len); + return socket.SendTo(packet, 0, len, SocketFlags.None, new IPEndPoint(addr.ToIPAddress(), port)); + } + finally + { + ArrayPool.Shared.Return(packet); + } } - catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == System.Net.Sockets.SocketError.WouldBlock) + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) { return global::sun.nio.ch.IOStatus.UNAVAILABLE; } - catch (System.Net.Sockets.SocketException e) + catch (SocketException e) { - throw global::java.net.SocketUtil.convertSocketExceptionToIOException(e); + throw e.ToIOException(); } catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.net.SocketException("Socket closed."); } #endif } diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramDispatcher.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramDispatcher.cs new file mode 100644 index 0000000000..c93ad77ced --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DatagramDispatcher.cs @@ -0,0 +1,398 @@ +using System; +using System.Buffers; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the native methods for 'DatagramDispatcher'. + /// + static class DatagramDispatcher + { + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + static readonly byte[] TempBuffer = new byte[1]; + + /// + /// Peek at the queue to see if there is an ICMP port unreachable. If there is, then receive it. + /// + /// + static void PurgeOutstandingICMP(Socket socket) + { + while (true) + { + // check for outstanding packet + if (socket.Poll(0, SelectMode.SelectRead) == false) + break; + + try + { + var ep = (EndPoint)new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + socket.EndReceiveFrom(socket.BeginReceiveFrom(TempBuffer, 0, TempBuffer.Length, SocketFlags.Peek, ref ep, null, null), ref ep); + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + try + { + var ep = (EndPoint)new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + socket.EndReceiveFrom(socket.BeginReceiveFrom(TempBuffer, 0, TempBuffer.Length, SocketFlags.Peek, ref ep, null, null), ref ep); + } + catch (SocketException e2) when (e2.SocketErrorCode == SocketError.ConnectionReset) + { + + } + + continue; + } + + break; + } + } + + [StructLayout(LayoutKind.Sequential)] + struct iovec + { + + public IntPtr iov_base; + public int iov_len; + + } + + /// + /// Implements the native method 'read'. + /// + /// + /// + /// + /// + /// + public static unsafe int read(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + var n = socket.Receive(buf, 0, len, SocketFlags.None); + if (n == 0) + return global::sun.nio.ch.IOStatus.EOF; + + Marshal.Copy(buf, 0, (IntPtr)address, n); + return n; + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + var n = socket.Receive(new Span((byte*)(IntPtr)address, len)); + if (n == 0) + return global::sun.nio.ch.IOStatus.EOF; + + return n; +#endif + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // Windows may leave multiple ICMP packets on the socket, purge them + if (RuntimeUtil.IsWindows) + PurgeOutstandingICMP(socket); + + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'readv'. + /// + /// + /// + /// + /// + /// + public static unsafe long readv(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + if (len == 0) + return 0; + + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try + { + // allocate list of array segments to hold received data + var vecs = new Span((byte*)(IntPtr)address, len); + var bufs = new ArraySegment[vecs.Length]; + + try + { + // prepare array segments for buffers + for (int i = 0; i < vecs.Length; i++) + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(vecs[i].iov_len), 0, vecs[i].iov_len); + + // receive into segments + var length = socket.Receive(bufs); + + // copy segments back into vectors + var l = length; + for (int i = 0; i < vecs.Length; i++) + { + var szz = Math.Min(l, vecs[i].iov_len); + Marshal.Copy(bufs[i].Array, 0, vecs[i].iov_base, szz); + l -= szz; + } + + // we should have accounted for all of the bytes + if (l != 0) + throw new global::java.lang.RuntimeException("Bytes remaining after read."); + + return length; + } + finally + { + for (int i = 0; i < bufs.Length; i++) + ArrayPool.Shared.Return(bufs[i].Array); + } + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // Windows may leave multiple ICMP packets on the socket, purge them + if (RuntimeUtil.IsWindows) + PurgeOutstandingICMP(socket); + + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'write'. + /// + /// + /// + /// + /// + /// + public static unsafe int write(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + Marshal.Copy((IntPtr)address, buf, 0, len); + return socket.Send(buf, 0, len, SocketFlags.None); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + return socket.Send(new ReadOnlySpan((byte*)(IntPtr)address, len)); +#endif + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // Windows may leave multiple ICMP packets on the socket, purge them + if (RuntimeUtil.IsWindows) + PurgeOutstandingICMP(socket); + + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'writev'. + /// + /// + /// + /// + /// + /// + public static unsafe long writev(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try + { + // allocate list of array segments to hold incoming buffers + var vecs = new ReadOnlySpan((void*)(IntPtr)address, len); + var bufs = new ArraySegment[vecs.Length]; + + try + { + // copy incoming buffers into segments + for (int i = 0; i < vecs.Length; i++) + { + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(vecs[i].iov_len), 0, vecs[i].iov_len); + Marshal.Copy(vecs[i].iov_base, bufs[i].Array, bufs[i].Offset, vecs[i].iov_len); + } + + // send segments + return socket.Send(bufs); + } + finally + { + // return allocated arrays + for (int i = 0; i < bufs.Length; i++) + if (bufs[i].Array != null) + ArrayPool.Shared.Return(bufs[i].Array); + } + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // Windows may leave multiple ICMP packets on the socket, purge them + if (RuntimeUtil.IsWindows) + PurgeOutstandingICMP(socket); + + throw new global::java.net.PortUnreachableException("ICMP Port Unreachable"); + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'close'. + /// + /// + /// + public static void close(object self, object fd) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + return; + + try + { + FileDescriptorAccessor.SetSocket(fd, null); + socket.Close(); + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousChannelExtensions.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousChannelExtensions.cs new file mode 100644 index 0000000000..e5213ea9af --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousChannelExtensions.cs @@ -0,0 +1,508 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using IKVM.Runtime.Util.Java.Util.Concurrent; + +#if FIRST_PASS == false + +using java.nio.channels; +using java.security; + +using sun.nio.ch; + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + static class DotNetAsynchronousChannelExtensions + { + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, TArg3 arg3, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, arg3, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, arg3, arg4, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, object attachment, CompletionHandler handler, Func> func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, arg3, arg4, arg5, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { f.setResult(a.GetAwaiter().GetResult()); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg arg, object attachment, CompletionHandler handler, Func func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { a.GetAwaiter().GetResult(); f.setResult(null); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, object attachment, CompletionHandler handler, Func func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { a.GetAwaiter().GetResult(); f.setResult(null); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + /// + /// Executes the specified function on the channel group. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static global::java.util.concurrent.Future Execute(this DotNetAsynchronousChannelGroup self, TChannel channel, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, object attachment, CompletionHandler handler, Func func) + where TChannel : AsynchronousChannel + { + var c = SynchronizationContext.Current; + + try + { + SynchronizationContext.SetSynchronizationContext(self.executor().ToSynchronizationContext()); + var cts = new CancellationTokenSource(); + var t = func(channel, arg1, arg2, arg3, arg4, arg5, global::java.lang.System.getSecurityManager() != null ? AccessController.getContext() : null, cts.Token); + + try + { + if (t.IsCompleted && t.IsFaulted) + t.GetAwaiter().GetResult(); + } + catch (Exception e) + { + if (handler != null) + { + Invoker.invoke(channel, handler, attachment, null, e); + return null; + } + + return CompletedFuture.withFailure(e); + } + + var f = new PendingFuture(channel, handler, attachment, null); + f.setContext(cts); + t.ContinueWith(a => { try { a.GetAwaiter().GetResult(); f.setResult(null); } catch (Exception e) { f.setFailure(e); }; Invoker.invoke(f); }); + return f; + } + finally + { + SynchronizationContext.SetSynchronizationContext(c); + } + } + + } + +} + +#endif diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousFileChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousFileChannelImpl.cs new file mode 100644 index 0000000000..6aec88d8b5 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousFileChannelImpl.cs @@ -0,0 +1,1018 @@ +using System; +using System.Threading.Tasks; +using System.IO; +using System.Threading; + +using System.ComponentModel; +using System.Runtime.InteropServices; + +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Nio; +using IKVM.Runtime; + +using Microsoft.Win32.SafeHandles; + +using Mono.Unix.Native; +using Mono.Unix; + +#if FIRST_PASS == false + +using java.io; +using java.nio; +using java.security; +using java.lang; +using java.nio.channels; + +using sun.nio.ch; + +#endif + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the native methods for 'DotNetAsynchronousFileChannelImpl'. + /// + static class DotNetAsynchronousFileChannelImpl + { + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + /// + /// Implements the native method for 'onCancel0'. + /// + /// + /// + public static void onCancel0(object self, object task) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + OnCancel((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, (PendingFuture)task); +#endif + } + + /// + /// Implements the native method for 'close0'. + /// + /// + public static void close0(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + Close((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self); +#endif + } + + /// + /// Implements the native method 'size0'. + /// + /// + public static long size0(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return Size((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self); +#endif + } + + /// + /// Implements the native method 'truncate0'. + /// + /// + public static object truncate0(object self, long size) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return Truncate((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, size); +#endif + } + + /// + /// Implements the native method 'force0'. + /// + /// + public static void force0(object self, bool metaData) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + Force((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, metaData); +#endif + } + + /// + /// Implements the native method for 'implLock0'. + /// + /// + /// + /// + /// + /// + /// + public static object implLock0(object self, long position, long size, bool shared, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((global::sun.nio.ch.DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, position, size, shared, attachment, (global::java.nio.channels.CompletionHandler)handler, LockAsync); +#endif + } + + /// + /// Implements the native method for 'tryLock0'. + /// + /// + /// + /// + /// + public static object tryLock0(object self, long position, long size, bool shared) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return TryLock((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, position, size, shared); +#endif + } + + /// + /// Implements the native method for 'tryLock0'. + /// + /// + /// + public static void implRelease0(object self, object fli) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + Release((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, (FileLockImpl)fli); +#endif + } + + /// + /// Implements the native method for 'implRead0'. + /// + /// + /// + /// + /// + /// + public static object implRead0(object self, object dst, long position, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((global::sun.nio.ch.DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, (global::java.nio.ByteBuffer)dst, position, attachment, (global::java.nio.channels.CompletionHandler)handler, ReadAsync); +#endif + } + + /// + /// Implements the native method for 'implWrite0'. + /// + /// + /// + /// + /// + /// + public static object implWrite0(object self, object src, long position, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((global::sun.nio.ch.DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousFileChannelImpl)self, (global::java.nio.ByteBuffer)src, position, attachment, (global::java.nio.channels.CompletionHandler)handler, WriteAsync); +#endif + } + + +#if FIRST_PASS == false + + /// + /// Invoked when the pending future is cancelled. + /// + /// + /// + /// + static void OnCancel(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, PendingFuture future) + { + // signal cancellation on associated cancellation token source + var cts = (CancellationTokenSource)future.getContext(); + cts?.Cancel(); + } + + /// + /// Invoked when the channel is closed. + /// + /// + /// + static void Close(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self) + { + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + return; + + try + { + self.closeLock.writeLock().@lock(); + FileDescriptorAccessor.SetStream(self.fdObj, null); + self.closed = true; + + try + { + stream.Close(); + } + catch + { + // ignore errors closing the stream + } + } + finally + { + self.closeLock.writeLock().unlock(); + } + + self.invalidateAllLocks(); + } + + /// + /// + /// + /// + /// + static long Size(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self) + { + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + throw new global::java.nio.channels.ClosedChannelException(); + + try + { + self.begin(); + + try + { + return stream.Length; + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + } + finally + { + self.end(); + } + } + + /// + /// + /// + /// + /// + static AsynchronousFileChannel Truncate(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, long size) + { + if (size < 0) + throw new IllegalArgumentException("Negative size"); + if (self.writing == false) + throw new global::java.nio.channels.NonWritableChannelException(); + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + throw new global::java.nio.channels.AsynchronousCloseException(); + + // matters for VFS files + if (stream.CanWrite == false) + throw new global::java.nio.channels.NonWritableChannelException(); + + try + { + self.begin(); + + if (size < stream.Length) + stream.SetLength(size); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.end(); + } + + return self; + } + + /// + /// + /// + /// + /// + static void Force(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, bool metaData) + { + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + throw new global::java.nio.channels.AsynchronousCloseException(); + + // matters for VFS files + if (stream.CanWrite == false) + throw new global::java.nio.channels.NonWritableChannelException(); + + try + { + self.begin(); + stream.Flush(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.end(); + } + } + + /// + /// Invokes the LockFileEx Win32 function. + /// + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32")] + static extern unsafe int LockFileEx(SafeFileHandle hFile, int dwFlags, int dwReserved, int nNumberOfBytesToLockLow, int nNumberOfBytesToLockHigh, NativeOverlapped* lpOverlapped); + + /// + /// Wraps the LockFileEx function as an async operation. + /// + /// + static unsafe ValueTask LockFileExAsync(SafeFileHandle hFile, int dwFlags, int dwReserved, int nNumberOfBytesToLockLow, int nNumberOfBytesToLockHigh, Overlapped overlapped) + { + var task = new TaskCompletionSource(); + var iocb = (IOCompletionCallback)((errorCode, numBytes, nativeOverlapped) => + { + try + { + if (errorCode == 0) + task.SetResult(null); + else + task.SetException(new Win32Exception((int)errorCode)); + } + catch (System.Exception e) + { + task.SetException(e); + } + finally + { + Overlapped.Free(nativeOverlapped); + } + }); + + var optr = overlapped.Pack(iocb, null); + if (LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh, optr) == 0) + { + Overlapped.Free(optr); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return new ValueTask(task.Task); + } + + /// + /// Invokes the UnlockFileEx Win32 function. + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern unsafe int UnlockFileEx(SafeFileHandle hFile, int dwReserved, int nNumberOfBytesToUnlockLow, int nNumberOfBytesToUnlockHigh, NativeOverlapped* lpOverlapped); + + /// + /// Implements the Lock logic as an asynchronous task. + /// + /// + /// + /// + /// + /// + static Task LockAsync(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, long position, long size, bool shared, AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + if (shared && self.reading == false) + throw new global::java.nio.channels.NonReadableChannelException(); + if (shared == false && self.writing == false) + throw new global::java.nio.channels.NonWritableChannelException(); + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + if (stream is not FileStream fs) + throw new global::java.io.IOException("Stream does not support locking."); + + var fli = self.addToFileLockTable(position, size, shared); + if (fli == null) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + + return ImplAsync(); + + async Task ImplAsync() + { + try + { + if (RuntimeUtil.IsWindows) + { + const int LOCKFILE_EXCLUSIVE_LOCK = 2; + + try + { + var o = new Overlapped(); + o.OffsetLow = (int)position; + o.OffsetHigh = (int)(position >> 32); + + var flags = 0; + if (shared == false) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + + await LockFileExAsync(fs.SafeFileHandle, flags, 0, (int)size, (int)(size >> 32), o); + + return fli; + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + } + else if (RuntimeUtil.IsLinux) + { + while (true) + { + try + { + if (cancellationToken.IsCancellationRequested) + return null; + + try + { + self.begin(); + + var fl = new Flock(); + fl.l_whence = SeekFlags.SEEK_SET; + fl.l_len = size == long.MaxValue ? 0 : size; + fl.l_start = position; + fl.l_type = shared ? LockType.F_RDLCK : LockType.F_WRLCK; + + // fails immediately with EAGAIN or EACCES if cannot obtain + if (Syscall.fcntl((int)fs.SafeFileHandle.DangerousGetHandle(), FcntlCommand.F_SETLK, ref fl) == 0) + return fli; + + var errno = Syscall.GetLastError(); + if (errno == Errno.EAGAIN || errno == Errno.EACCES) + continue; + + UnixMarshal.ThrowExceptionForError(errno); + } + finally + { + self.end(); + } + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + + await Task.Delay(TimeSpan.FromMilliseconds(100)); + } + } + else + { + while (true) + { + try + { + if (cancellationToken.IsCancellationRequested) + return null; + + try + { + self.begin(); + + fs.Lock(position, size); + return fli; + } + catch (System.IO.IOException) + { + // we failed to acquire the lock, try again next iteration + } + finally + { + self.end(); + } + + // try again shortly + await Task.Delay(TimeSpan.FromMilliseconds(100)); + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + } + } + } + catch + { + self.removeFromFileLockTable(fli); + } + + return null; + }; + } + + /// + /// Attempts to lock a file region. + /// + /// + /// + /// + /// + /// + static unsafe FileLock TryLock(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, long position, long size, bool shared) + { + if (shared && self.reading == false) + throw new global::java.nio.channels.NonReadableChannelException(); + if (shared == false && self.writing == false) + throw new global::java.nio.channels.NonWritableChannelException(); + + if (IsOpen(self) == false) + throw new global::java.nio.channels.ClosedChannelException(); + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + throw new global::java.nio.channels.AsynchronousCloseException(); + if (stream is not FileStream fs) + throw new global::java.io.IOException("File does not support locking."); + + var fli = self.addToFileLockTable(position, size, shared); + if (fli == null) + throw new global::java.nio.channels.ClosedChannelException(); + + try + { + try + { + self.begin(); + + if (RuntimeUtil.IsWindows) + { + const int LOCKFILE_FAIL_IMMEDIATELY = 1; + const int LOCKFILE_EXCLUSIVE_LOCK = 2; + + var o = new Overlapped(); + o.OffsetLow = (int)position; + o.OffsetHigh = (int)(position >> 32); + + var flags = LOCKFILE_FAIL_IMMEDIATELY; + if (shared == false) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + + // issue async request but wait for synchronous result + var t = LockFileExAsync(fs.SafeFileHandle, flags, 0, (int)size, (int)(size >> 32), o); + t.GetAwaiter().GetResult(); + + return fli; + } + else if (RuntimeUtil.IsLinux) + { + var fl = new Flock(); + fl.l_whence = SeekFlags.SEEK_SET; + fl.l_len = size == long.MaxValue ? 0 : size; + fl.l_start = position; + fl.l_type = shared ? LockType.F_RDLCK : LockType.F_WRLCK; + + // fails immediately with EAGAIN or EACCES if cannot obtain + if (Syscall.fcntl((int)fs.SafeFileHandle.DangerousGetHandle(), FcntlCommand.F_SETLK, ref fl) == 0) + return fli; + + var errno = Syscall.GetLastError(); + if (errno == Errno.EAGAIN || errno == Errno.EACCES) + { + self.removeFromFileLockTable(fli); + return null; + } + + UnixMarshal.ThrowExceptionForError(errno); + } + else + { + fs.Lock(position, size); + } + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.end(); + } + } + catch + { + self.removeFromFileLockTable(fli); + } + + return null; + } + + /// + /// Implements the Release logic as an asynchronous task. + /// + /// + static unsafe void Release(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, FileLockImpl fli) + { + if (IsOpen(self) == false) + return; + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + return; + + if (stream is not FileStream fs) + throw new global::java.io.IOException("File does not support locking."); + + try + { + self.begin(); + + var pos = fli.position(); + var size = fli.size(); + + if (RuntimeUtil.IsWindows) + { + const int ERROR_NOT_LOCKED = 158; + + try + { + var o = new Overlapped(); + o.OffsetLow = (int)pos; + o.OffsetHigh = (int)(pos >> 32); + var p = o.Pack(null, null); + + try + { + if (UnlockFileEx(fs.SafeFileHandle, 0, (int)size, (int)(size >> 32), p) == 0) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + finally + { + Overlapped.Free(p); + } + } + catch (Win32Exception e) when (e.ErrorCode == ERROR_NOT_LOCKED) + { + return; + } + } + else + { + fs.Unlock(pos, size); + } + } + catch (System.IO.IOException e) when (NotLockedHack.IsErrorNotLocked(e)) + { + return; + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.end(); + } + } + + /// + /// Implements the Read logic as an asynchronous task. + /// + /// + /// + /// + /// + /// + static Task ReadAsync(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, global::java.nio.ByteBuffer dst, long position, AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + if (self.reading == false) + throw new global::java.nio.channels.NonReadableChannelException(); + if (position < 0) + throw new global::java.lang.IllegalArgumentException("Negative position"); + if (dst.isReadOnly()) + throw new global::java.lang.IllegalArgumentException("Read-only buffer"); + + if (IsOpen(self) == false) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + if (stream.CanRead == false) + return Task.FromException(new global::java.io.IOException("Stream does not support reading.")); + + // check buffer + int pos = dst.position(); + int lim = dst.limit(); + int rem = pos <= lim ? lim - pos : 0; + if (pos > lim) + return Task.FromException(new global::java.lang.IllegalArgumentException("Position after limit.")); + + if (cancellationToken.IsCancellationRequested) + return null; + + return ImplAsync(); + + async Task ImplAsync() + { + var lck = FileDescriptorAccessor.GetSemaphore(self.fdObj); + if (lck == null) + { + lck = new SemaphoreSlim(1, 1); + if (FileDescriptorAccessor.CompareExchangeSemaphore(self.fdObj, lck, null) != null) + lck.Dispose(); + + lck = FileDescriptorAccessor.GetSemaphore(self.fdObj); + } + + try + { + await lck.WaitAsync(cancellationToken); + } + catch (OperationCanceledException) + { + return null; + } + + try + { + // move file to specified position + if (stream.Position != position) + { + if (stream.CanSeek == false) + throw new global::java.lang.IllegalArgumentException("Seek failed."); + + stream.Seek(position, SeekOrigin.Begin); + } + +#if NETFRAMEWORK + if (dst is DirectBuffer dir) + { + var tmp = new byte[rem]; + var n = await stream.ReadAsync(tmp, 0, rem, cancellationToken); + dst.put(tmp, 0, n); + return new global::java.lang.Integer(n); + } + else + { + var n = await stream.ReadAsync(dst.array(), dst.arrayOffset() + pos, rem, cancellationToken); + dst.position(pos + n); + return new global::java.lang.Integer(n); + } +#else + if (dst is DirectBuffer dir) + { + using var mgr = new DirectBufferMemoryManager(dir); + var mem = mgr.Memory.Slice(pos, rem); + var n = await stream.ReadAsync(mem, cancellationToken); + dst.position(pos + n); + return new global::java.lang.Integer(n); + } + else + { + var n = await stream.ReadAsync(dst.array(), dst.arrayOffset() + pos, rem, cancellationToken); + dst.position(pos + n); + return new global::java.lang.Integer(n); + } +#endif + } + catch (OperationCanceledException) + { + return null; + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (cancellationToken.IsCancellationRequested) + { + return null; + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + lck.Release(); + } + } + } + + /// + /// Implements the Write logic as an asynchronous task. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + static Task WriteAsync(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl self, global::java.nio.ByteBuffer src, long position, AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + if (self.writing == false) + throw new global::java.nio.channels.NonWritableChannelException(); + if (position < 0) + throw new global::java.lang.IllegalArgumentException("Negative position"); + + if (IsOpen(self) == false) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + + var stream = FileDescriptorAccessor.GetStream(self.fdObj); + if (stream == null) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + if (stream.CanWrite == false) + return Task.FromException(new global::java.io.IOException("Stream does not support writing.")); + + // check buffer + int pos = src.position(); + int lim = src.limit(); + int rem = pos <= lim ? lim - pos : 0; + if (rem == 0) + return Task.FromResult(new global::java.lang.Integer(0)); + if (pos > lim) + return Task.FromException(new global::java.lang.IllegalArgumentException("Position after limit.")); + + if (cancellationToken.IsCancellationRequested) + return null; + + return ImplAsync(); + + async Task ImplAsync() + { + var lck = FileDescriptorAccessor.GetSemaphore(self.fdObj); + if (lck == null) + { + lck = new SemaphoreSlim(1, 1); + if (FileDescriptorAccessor.CompareExchangeSemaphore(self.fdObj, lck, null) != null) + lck.Dispose(); + + lck = FileDescriptorAccessor.GetSemaphore(self.fdObj); + } + + try + { + await lck.WaitAsync(cancellationToken); + } + catch (OperationCanceledException) + { + return null; + } + + try + { + // move file to specified position + if (stream.Position != position) + { + if (stream.CanSeek == false) + throw new global::java.lang.IllegalArgumentException("Seek failed."); + + stream.Seek(position, SeekOrigin.Begin); + } + +#if NETFRAMEWORK + if (src is DirectBuffer dir) + { + var tmp = new byte[rem]; + src.get(tmp, 0, rem); + await stream.WriteAsync(tmp, 0, rem, cancellationToken); + return new global::java.lang.Integer(rem); + } + else + { + await stream.WriteAsync(src.array(), src.arrayOffset() + pos, rem, cancellationToken); + src.position(pos + rem); + return new global::java.lang.Integer(rem); + } +#else + if (src is DirectBuffer dir) + { + using var mgr = new DirectBufferMemoryManager(dir); + var mem = mgr.Memory.Slice(pos, rem); + await stream.WriteAsync(mem, cancellationToken); + src.position(pos + rem); + return new global::java.lang.Integer(rem); + } + else + { + await stream.WriteAsync(src.array(), src.arrayOffset() + pos, rem, cancellationToken); + src.position(pos + rem); + return new global::java.lang.Integer(rem); + } +#endif + } + catch (OperationCanceledException) + { + return null; + } + catch (ObjectDisposedException) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (IsOpen(self) == false) + { + throw new global::java.nio.channels.AsynchronousCloseException(); + } + catch (System.Exception) when (cancellationToken.IsCancellationRequested) + { + return null; + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + lck.Release(); + } + } + } + + static bool IsOpen(global::sun.nio.ch.DotNetAsynchronousFileChannelImpl ch) + { + Interlocked.MemoryBarrier(); + return ch.isOpen(); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.cs new file mode 100644 index 0000000000..37f6953cfc --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousServerSocketChannelImpl.cs @@ -0,0 +1,236 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; +using System.Threading; + +using IKVM.Runtime; +using IKVM.Runtime.Util.Java.Security; +using IKVM.Runtime.Util.Java.Net; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; +using IKVM.Runtime.Accessors.Java.Lang; + +#if FIRST_PASS == false + +using java.lang; +using java.io; +using java.nio.channels; +using java.util.concurrent; + +using sun.nio.ch; + +#endif + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the native methods for 'DotNetAsynchronousServerSocketChannelImpl'. + /// + static class DotNetAsynchronousServerSocketChannelImpl + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + static AccessControllerAccessor accessControllerAccessor; + static FileDescriptorAccessor fileDescriptorAccessor; + static DotNetAsynchronousServerSocketChannelImplAccessor dotNetAsynchronousServerSocketChannelImplAccessor; + static DotNetAsynchronousSocketChannelImplAccessor dotNetAsynchronousSocketChannelImplAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + + static AccessControllerAccessor AccessControllerAccessor => JVM.BaseAccessors.Get(ref accessControllerAccessor); + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static DotNetAsynchronousServerSocketChannelImplAccessor DotNetAsynchronousServerSocketChannelImplAccessor => JVM.BaseAccessors.Get(ref dotNetAsynchronousServerSocketChannelImplAccessor); + + static DotNetAsynchronousSocketChannelImplAccessor DotNetAsynchronousSocketChannelImplAccessor => JVM.BaseAccessors.Get(ref dotNetAsynchronousSocketChannelImplAccessor); + +#endif + + /// + /// Implements the native method for 'implAccept0'. + /// + /// + /// + /// + public static object implAccept0(object self, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return Accept((global::sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl)self, attachment, (CompletionHandler)handler); +#endif + } + + /// + /// Implements the native methodf or 'implClose0'. + /// + /// + public static void implClose0(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + Close((global::sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl)self); +#endif + } + +#if FIRST_PASS == false + + /// + /// Implements the Accept logic as an asynchronous task converted to a Future. + /// + /// + /// + /// + /// + static Future Accept(global::sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl self, object attachment, CompletionHandler handler) + { + return ((DotNetAsynchronousChannelGroup)self.group()).Execute(self, attachment, handler, AcceptAsync); + } + + /// + /// Creates a new client channel. + /// + /// + /// + static object CreateClientChannel(object server, object fd, object remote) + { + try + { + DotNetAsynchronousServerSocketChannelImplAccessor.InvokeBegin(server); + return DotNetAsynchronousSocketChannelImplAccessor.Init(DotNetAsynchronousServerSocketChannelImplAccessor.InvokeGroup(server), fd, remote); + } + finally + { + DotNetAsynchronousServerSocketChannelImplAccessor.InvokeEnd(server); + } + } + + /// + /// Implements the Accept logic as an asynchronous task. + /// + /// + /// + /// s + static Task AcceptAsync(global::sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl self, object accessControlContext, CancellationToken cancellationToken) + { + if (self.isOpen() == false) + return Task.FromException(new ClosedChannelException()); + + if (self.isAcceptKilled()) + throw new RuntimeException("Accept not allowed due to cancellation."); + + if (self.localAddress == null) + throw new NotYetBoundException(); + + if (self.accepting.compareAndSet(false, true) == false) + throw new AcceptPendingException(); + + return ImplAsync(); + + async Task ImplAsync() + { + try + { + try + { + // execute asynchronous Accept task + var listenSocket = FileDescriptorAccessor.GetSocket(self.fd); +#if NETFRAMEWORK + var acceptSocket = await Task.Factory.FromAsync(listenSocket.BeginAccept, listenSocket.EndAccept, 0, null); +#else + var acceptSocket = await listenSocket.AcceptAsync(); +#endif + + // connection accept completed after group has shutdown + if (self.group().isShutdown()) + throw new ShutdownChannelGroupException(); + + try + { + DotNetAsynchronousServerSocketChannelImplAccessor.InvokeBegin(self); + + // obtain new addresses + var local = ((IPEndPoint)acceptSocket.LocalEndPoint).ToInetSocketAddress(); + var remote = ((IPEndPoint)acceptSocket.RemoteEndPoint).ToInetSocketAddress(); + var fd = FileDescriptorAccessor.FromSocket(acceptSocket); + var client = CreateClientChannel(self, fd, remote); + + // check access to specified host + if (accessControlContext != null) + AccessControllerAccessor.InvokeDoPrivileged(new ActionPrivilegedAction(() => SecurityManagerAccessor.InvokeCheckAccept(SystemAccessor.InvokeGetSecurityManager(), (string)remote.getAddress().getHostAddress(), (int)remote.getPort())), accessControlContext); + + // return resulting channel + return client; + } + finally + { + DotNetAsynchronousServerSocketChannelImplAccessor.InvokeEnd(self); + } + } + catch (SecurityException) + { + throw; + } + catch (System.Net.Sockets.SocketException) when (self.isOpen() == false) + { + throw new AsynchronousCloseException(); + } + catch (System.Net.Sockets.SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new IOException(e); + } + } + finally + { + self.accepting.set(false); + } + } + } + + /// + /// Closes the socket associaed with the server socket channel. + /// + /// + static void Close(global::sun.nio.ch.DotNetAsynchronousServerSocketChannelImpl self) + { + if (self.fd == null) + return; + + var socket = FileDescriptorAccessor.GetSocket(self.fd); + if (socket == null) + return; + + try + { + // null socket before close, as close may take a minute to flush + FileDescriptorAccessor.SetSocket(self.fd, null); + socket.Close(); + } + catch (SocketException) + { + // ignore + } + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.cs new file mode 100644 index 0000000000..37bb018d46 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/DotNetAsynchronousSocketChannelImpl.cs @@ -0,0 +1,920 @@ +using System; +using System.Buffers; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; +using IKVM.Runtime.Util.Java.Net; +using IKVM.Runtime.Util.Java.Nio; +using IKVM.Runtime.Util.Java.Security; + +#if FIRST_PASS == false + +using java.io; +using java.lang; +using java.net; +using java.nio; +using java.nio.channels; +using java.security; +using java.util.concurrent; + +using sun.nio.ch; +using sun.security.util; + +#endif + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the native methods for 'DotNetAsynchronousSocketChannelImpl'. + /// + static class DotNetAsynchronousSocketChannelImpl + { + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static DotNetAsynchronousSocketChannelImplAccessor dotNetAsynchronousSocketChannelImplAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static DotNetAsynchronousSocketChannelImplAccessor DotNetAsynchronousSocketChannelImplAccessor => JVM.BaseAccessors.Get(ref dotNetAsynchronousSocketChannelImplAccessor); + +#endif + + /// + /// Implements the native method for 'onCancel0'. + /// + /// + /// + public static void onCancel0(object self, object task) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + OnCancel((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self, (PendingFuture)task); +#endif + } + + /// + /// Implements the native method for 'implClose0'. + /// + /// + public static void implClose0(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + Close((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self); +#endif + } + + /// + /// Implements the native method for 'implConnect0'. + /// + /// + /// + /// + /// + public static object implConnect0(object self, object remote, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self, (global::java.net.SocketAddress)remote, attachment, (CompletionHandler)handler, ConnectAsync); +#endif + } + + /// + /// Implements the native method for 'implRead0'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static object implRead0(object self, bool isScatteringRead, object dst, object[] dsts, long timeout, object unit, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self, isScatteringRead, (ByteBuffer)dst, (ByteBuffer[])dsts, timeout, (TimeUnit)unit, attachment, (CompletionHandler)handler, ReadAsync); +#endif + } + + /// + /// Implements the native method for 'implWrite0'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static object implWrite0(object self, bool gatheringWrite, object src, object[] srcs, long timeout, object unit, object attachment, object handler) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return ((DotNetAsynchronousChannelGroup)((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self).group()).Execute((global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl)self, gatheringWrite, (ByteBuffer)src, (ByteBuffer[])srcs, timeout, (TimeUnit)unit, attachment, (CompletionHandler)handler, WriteAsync); +#endif + } + + +#if FIRST_PASS == false + + /// + /// Invoked when the pending future is cancelled. + /// + /// + /// + /// + static void OnCancel(global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl self, PendingFuture future) + { + // signal cancellation on associated cancellation token source + var cts = (CancellationTokenSource)future.getContext(); + cts?.Cancel(); + } + + /// + /// + /// + /// + /// + static void Close(global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl self) + { + if (self.fd == null) + return; + + var socket = FileDescriptorAccessor.GetSocket(self.fd); + if (socket == null) + return; + + try + { + // null socket before close, as close may take a minute to flush + FileDescriptorAccessor.SetSocket(self.fd, null); + socket.Close(); + } + catch (System.Net.Sockets.SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + // ignore + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + } + + /// + /// Implements the Connect logic as an asynchronous task. + /// + /// + /// + /// + /// + static Task ConnectAsync(global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl self, global::java.net.SocketAddress remote, global::java.security.AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + if (self.isOpen() == false) + return Task.FromException(new ClosedChannelException()); + + var socket = FileDescriptorAccessor.GetSocket(self.fd); + if (socket == null) + return Task.FromException(new global::java.nio.channels.ClosedChannelException()); + + // cancel was invoked during the operation + if (cancellationToken.IsCancellationRequested) + throw new IllegalStateException("Connect not allowed due to timeout or cancellation."); + + // checks the validity of the socket address + var remoteAddress = global::sun.nio.ch.Net.checkAddress(remote); + if (accessControlContext != null) + AccessController.doPrivileged(new ActionPrivilegedAction(() => global::java.lang.System.getSecurityManager()?.checkConnect(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort())), accessControlContext); + + lock (self.stateLock) + { + // ensure channel is not already connected + if (self.state == AsynchronousSocketChannelImpl.ST_CONNECTED) + throw new AlreadyConnectedException(); + + // ensure channel is not already in process of being connected + if (self.state == AsynchronousSocketChannelImpl.ST_PENDING) + throw new ConnectionPendingException(); + + // ensure channel is bound to a local address + if (self.localAddress == null) + { + try + { + var any = new InetSocketAddress(0); + if (accessControlContext == null) + self.bind(any); + else + AccessController.doPrivileged(new ActionPrivilegedExceptionAction(() => self.bind(any)), accessControlContext); + } + catch (global::java.io.IOException e) + { + try + { + self.close(); + } + catch + { + // ignore + } + + self.state = AsynchronousSocketChannelImpl.ST_PENDING; + return Task.FromException(e); + } + } + } + + return ImplAsync(); + + async Task ImplAsync() + { + try + { + try + { + // execute asynchronous Connect task +#if NETFRAMEWORK + await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, remoteAddress.ToIPEndpoint(), null); +#else + await socket.ConnectAsync(remoteAddress.ToIPEndpoint()); +#endif + + try + { + self.begin(); + + // set the channel to connected + lock (self.stateLock) + { + self.state = AsynchronousSocketChannelImpl.ST_CONNECTED; + self.remoteAddress = remoteAddress; + } + + return; + } + finally + { + self.end(); + } + } + catch (System.Net.Sockets.SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + } + catch (System.Exception) + { + try + { + if (self.isOpen()) + self.close(); + } + catch + { + // ignore + } + + throw; + } + } + } + + /// + /// Implements the Accept logic as an asynchronous task. + /// + /// + /// This implementation differs based on whether we are doing a scattering read, whether there is a timeout specified and whether the buffers under consideration are direct. + /// + /// + /// + /// + /// + /// + /// + /// + /// + static Task ReadAsync(global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl self, bool isScatteringRead, ByteBuffer dst, ByteBuffer[] dsts, long timeout, TimeUnit unit, AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + var socket = FileDescriptorAccessor.GetSocket(self.fd); + if (socket == null) + throw new global::java.io.IOException("Socket is closed."); + + // we were told to do a scattering read, but only received one buffer, do a regular read + if (isScatteringRead && dsts.Length == 1) + dst = dsts[0]; + + return ImplAsync(); + + async Task ImplAsync() + { + try + { + // timeout was specified, wait for data to be available + var t = (int)System.Math.Min(TimeUnit.MILLISECONDS.convert(timeout, unit), int.MaxValue); + if (t > 0) + { + // non-optimal usage, but we have no way to combine timeout with true async + return await Task.Run(() => + { + var previousBlocking = socket.Blocking; + var previousReceiveTimeout = socket.ReceiveTimeout; + + try + { + socket.Blocking = true; + socket.ReceiveTimeout = t; + + if (dst != null) + { + int length = 0; + + int pos = dst.position(); + int lim = dst.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dst is DirectBuffer dir) + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(rem); + + try + { + length = socket.Receive(buf, rem, SocketFlags.None); + Marshal.Copy(buf, 0, (IntPtr)dir.address() + pos, length); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + unsafe + { + length = socket.Receive(new Span((byte*)(IntPtr)dir.address() + pos, rem), SocketFlags.None); + } +#endif + } + else + { + // synchronous read (inside of a Task) directly into the underlying array of the buffer + length = socket.Receive(dst.array(), dst.arrayOffset() + pos, rem, SocketFlags.None); + } + + dst.position(pos + length); + + // no data returned means socket is EOF + if (length == 0) + length = global::sun.nio.ch.IOStatus.EOF; + + return isScatteringRead ? Long.valueOf(length) : Integer.valueOf(length); + } + else + { + int length = 0; + var bufs = new ArraySegment[dsts.Length]; + + try + { + // prepare array segments for buffers + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dbf is DirectBuffer dir) + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(rem), 0, rem); + else + bufs[i] = new ArraySegment(dbf.array(), dbf.arrayOffset() + pos, rem); + } + + // receive data into segments + length = socket.Receive(bufs, SocketFlags.None); + + // copy segments back into buffers + var l = length; + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + var buf = bufs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + int len = System.Math.Min(l, rem); + + if (dbf is DirectBuffer dir) + Marshal.Copy(buf.Array, 0, (IntPtr)dir.address() + pos, len); + + dbf.position(pos + len); + l -= len; + } + + // we should have accounted for all of the bytes + if (l != 0) + throw new global::java.lang.RuntimeException("Bytes remaining after read."); + + // no data returned means socket is EOF + if (length == 0) + length = global::sun.nio.ch.IOStatus.EOF; + + // return total number of bytes read + return isScatteringRead ? Long.valueOf(length) : Integer.valueOf(length); + } + finally + { + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + var buf = bufs[i]; + + if (dbf is DirectBuffer dir) + ArrayPool.Shared.Return(buf.Array); + } + } + } + } + finally + { + socket.Blocking = previousBlocking; + socket.ReceiveTimeout = previousReceiveTimeout; + } + }); + } + else + { + var previousBlocking = socket.Blocking; + var previousReceiveTimeout = socket.ReceiveTimeout; + + try + { + socket.Blocking = false; + socket.ReceiveTimeout = 0; + + if (dst != null) + { + int length = 0; + + int pos = dst.position(); + int lim = dst.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dst is DirectBuffer dir) + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(rem); + + try + { + length = await Task.Factory.FromAsync((cb, state) => socket.BeginReceive(buf, 0, rem, SocketFlags.None, cb, state), socket.EndReceive, null); + Marshal.Copy(buf, 0, (IntPtr)dir.address() + pos, length); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + using var mgr = new DirectBufferMemoryManager(dir); + var mem = mgr.Memory.Slice(pos, rem); + length = await socket.ReceiveAsync(mem, SocketFlags.None, cancellationToken); +#endif + } + else + { +#if NETFRAMEWORK + length = await Task.Factory.FromAsync((cb, state) => socket.BeginReceive(dst.array(), dst.arrayOffset() + pos, rem, SocketFlags.None, cb, state), socket.EndReceive, null); +#else + length = await socket.ReceiveAsync(new Memory(dst.array(), dst.arrayOffset() + pos, rem), SocketFlags.None, cancellationToken); +#endif + } + + dst.position(pos + length); + + // no data returned means socket is EOF + if (length == 0) + length = global::sun.nio.ch.IOStatus.EOF; + + return isScatteringRead ? Long.valueOf(length) : Integer.valueOf(length); + } + else + { + int length = 0; + var bufs = new ArraySegment[dsts.Length]; + + try + { + // prepare array segments for buffers + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dbf is DirectBuffer dir) + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(rem), 0, rem); + else + bufs[i] = new ArraySegment(dbf.array(), dbf.arrayOffset() + pos, rem); + } + + // receive data into segments +#if NETFRAMEWORK + length = await Task.Factory.FromAsync(socket.BeginReceive, socket.EndReceive, bufs, SocketFlags.None, null); +#else + length = await socket.ReceiveAsync(bufs, SocketFlags.None); +#endif + + var l = length; + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + var buf = bufs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + int len = System.Math.Min(l, rem); + + if (dbf is DirectBuffer dir) + Marshal.Copy(buf.Array, 0, (IntPtr)dir.address() + pos, len); + + dbf.position(pos + len); + l -= len; + } + + // we should have accounted for all of the bytes + if (l != 0) + throw new RuntimeException("Bytes remaining after read."); + + // no data returned means socket is EOF + if (length == 0) + length = global::sun.nio.ch.IOStatus.EOF; + + // return total number of bytes read + return isScatteringRead ? Long.valueOf(length) : Integer.valueOf(length); + } + finally + { + for (int i = 0; i < dsts.Length; i++) + { + var dbf = dsts[i]; + var buf = bufs[i]; + + if (dbf is DirectBuffer dir) + ArrayPool.Shared.Return(buf.Array); + } + } + + } + } + finally + { + socket.Blocking = previousBlocking; + socket.ReceiveTimeout = previousReceiveTimeout; + } + } + } + catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == SocketError.TimedOut) + { + DotNetAsynchronousSocketChannelImplAccessor.InvokeEnableReading(self, true); + throw new global::java.nio.channels.InterruptedByTimeoutException(); + } + catch (System.Net.Sockets.SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.enableReading(); + } + } + } + + /// + /// Implements the Accept logic as an asynchronous task. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + static Task WriteAsync(global::sun.nio.ch.DotNetAsynchronousSocketChannelImpl self, bool gatheringWrite, ByteBuffer src, ByteBuffer[] srcs, long timeout, TimeUnit unit, AccessControlContext accessControlContext, CancellationToken cancellationToken) + { + var socket = FileDescriptorAccessor.GetSocket(self.fd); + if (socket == null) + throw new global::java.io.IOException("Socket is closed."); + + // we were told to do a scattering read, but only received one buffer, do a regular read + if (gatheringWrite && srcs.Length == 1) + src = srcs[0]; + + return ImplAsync(); + + async Task ImplAsync() + { + try + { + // timeout was specified, wait for data to be sent + var t = (int)System.Math.Min(TimeUnit.MILLISECONDS.convert(timeout, unit), int.MaxValue); + if (t > 0) + { + // non-optimal usage, but we have no way to combine timeout with true async + return await Task.Run(() => + { + var previousBlocking = socket.Blocking; + var previousSendTimeout = socket.SendTimeout; + + try + { + socket.Blocking = true; + socket.SendTimeout = t; + + if (src != null) + { + int length = 0; + + int pos = src.position(); + int lim = src.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (src is DirectBuffer dir) + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(rem); + + try + { + Marshal.Copy((IntPtr)dir.address() + pos, buf, 0, rem); + length = socket.Send(buf, rem, SocketFlags.None); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + unsafe + { + length = socket.Send(new ReadOnlySpan((byte*)(IntPtr)dir.address() + pos, rem), SocketFlags.None); + } +#endif + } + else + { + length = socket.Send(src.array(), src.arrayOffset() + pos, rem, SocketFlags.None); + } + + src.position(pos + length); + return gatheringWrite ? Long.valueOf(length) : Integer.valueOf(length); + } + else + { + int length = 0; + var bufs = new ArraySegment[srcs.Length]; + + try + { + // prepare array segments for buffers + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dbf is DirectBuffer dir) + { + var buf = ArrayPool.Shared.Rent(rem); + Marshal.Copy((IntPtr)dir.address() + pos, buf, 0, rem); + bufs[i] = new ArraySegment(buf, 0, rem); + } + else + bufs[i] = new ArraySegment(dbf.array(), dbf.arrayOffset() + pos, rem); + } + + // send data from segments + length = socket.Send(bufs, SocketFlags.None); + } + finally + { + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + var buf = bufs[i]; + + // return temporary buffer + if (dbf is DirectBuffer dir) + ArrayPool.Shared.Return(buf.Array); + } + } + + // advance buffers by amount sent + var l = length; + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + var len = System.Math.Min(l, rem); + + dbf.position(pos + len); + l -= len; + } + + // return total number of bytes written + return gatheringWrite ? Long.valueOf(length) : Integer.valueOf(length); + } + } + finally + { + socket.Blocking = previousBlocking; + socket.SendTimeout = previousSendTimeout; + } + }); + } + else + { + var previousBlocking = socket.Blocking; + var previousSendTimeout = socket.SendTimeout; + + try + { + socket.Blocking = false; + socket.SendTimeout = 0; + + if (src != null) + { + int length = 0; + + int pos = src.position(); + int lim = src.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (src is DirectBuffer dir) + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(rem); + + try + { + Marshal.Copy((IntPtr)dir.address() + pos, buf, 0, rem); + length = await Task.Factory.FromAsync((cb, state) => socket.BeginSend(buf, 0, rem, SocketFlags.None, cb, state), socket.EndSend, null); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + using var mgr = new DirectBufferMemoryManager(dir); + var mem = mgr.Memory.Slice(pos, rem); + length = await socket.SendAsync(mem, SocketFlags.None, cancellationToken); +#endif + } + else + { +#if NETFRAMEWORK + length = await Task.Factory.FromAsync((cb, state) => socket.BeginSend(src.array(), src.arrayOffset() + pos, rem, SocketFlags.None, cb, state), socket.EndSend, null); +#else + length = await socket.SendAsync(new Memory(src.array(), src.arrayOffset() + pos, rem), SocketFlags.None, cancellationToken); +#endif + } + + src.position(pos + length); + return gatheringWrite ? Long.valueOf(length) : Integer.valueOf(length); + } + else + { + int length = 0; + var bufs = new ArraySegment[srcs.Length]; + + try + { + // prepare array segments for buffers + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + + if (dbf is DirectBuffer dir) + { + var buf = ArrayPool.Shared.Rent(rem); + Marshal.Copy((IntPtr)dir.address() + pos, buf, 0, rem); + bufs[i] = new ArraySegment(buf, 0, rem); + } + else + bufs[i] = new ArraySegment(dbf.array(), dbf.arrayOffset() + pos, rem); + } + + // send data from segments +#if NETFRAMEWORK + length = await Task.Factory.FromAsync(socket.BeginSend, socket.EndSend, bufs, SocketFlags.None, null); +#else + length = await socket.SendAsync(bufs, SocketFlags.None); +#endif + } + finally + { + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + var buf = bufs[i]; + + // return temporary buffer + if (dbf is DirectBuffer dir) + ArrayPool.Shared.Return(buf.Array); + } + } + + // advance buffers by amount sent + var l = length; + for (int i = 0; i < srcs.Length; i++) + { + var dbf = srcs[i]; + int pos = dbf.position(); + int lim = dbf.limit(); + int rem = pos <= lim ? lim - pos : 0; + var len = System.Math.Min(l, rem); + + dbf.position(pos + len); + l -= len; + } + + // return total number of bytes written + return gatheringWrite ? Long.valueOf(length) : Integer.valueOf(length); + } + } + finally + { + socket.Blocking = previousBlocking; + socket.SendTimeout = previousSendTimeout; + } + } + } + catch (System.Net.Sockets.SocketException e) when (e.SocketErrorCode == SocketError.TimedOut) + { + DotNetAsynchronousSocketChannelImplAccessor.InvokeEnableWriting(self, true); + throw new global::java.nio.channels.InterruptedByTimeoutException(); + } + catch (System.Net.Sockets.SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new AsynchronousCloseException(); + } + catch (System.Exception e) + { + throw new global::java.io.IOException(e); + } + finally + { + self.enableWriting(); + } + } + + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileChannelImpl.cs index 278215beea..e9eecf7a5c 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileChannelImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileChannelImpl.cs @@ -1,33 +1,524 @@ using System; +using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; +using System.Threading; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; + +using Microsoft.Win32.SafeHandles; + +using Mono.Unix; using Mono.Unix.Native; namespace IKVM.Java.Externs.sun.nio.ch { /// - /// Implements the external methods for . + /// Implements the external methods for . /// static class FileChannelImpl { + const int MAP_RO = 0; + const int MAP_RW = 1; + const int MAP_PV = 2; + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileChannelImplAccessor fileChannelImplAccessor; + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static FileChannelImplAccessor FileChannelImplAccessor => JVM.BaseAccessors.Get(ref fileChannelImplAccessor); + +#endif + + [StructLayout(LayoutKind.Sequential)] + struct SYSTEM_INFO + { + + public ushort wProcessorArchitecture; + public ushort wReserved; + public uint dwPageSize; + public IntPtr lpMinimumApplicationAddress; + public IntPtr lpMaximumApplicationAddress; + public IntPtr dwActiveProcessorMask; + public uint dwNumberOfProcessors; + public uint dwProcessorType; + public uint dwAllocationGranularity; + public ushort wProcessorLevel; + public ushort wProcessorRevision; + + } + + /// + /// Invokes the GetSystemInfo Win32 function. + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern void GetSystemInfo(ref SYSTEM_INFO info); + /// - /// Implements the native method for 'ikvm_mmap'. + /// Gets the allocation granularity value for Windows. /// - public static IntPtr ikvm_mmap(FileStream stream, bool writeable, bool copy_on_write, long position, int size) + /// + static long GetAllocationGranularityWindows() + { + var i = new SYSTEM_INFO(); + GetSystemInfo(ref i); + return i.dwAllocationGranularity; + } + + /// + /// Gets the allocation granularity value for Linux. + /// + /// + static long GetAllocationGranularityLinux() + { + return Syscall.sysconf(SysconfName._SC_PAGESIZE); + } + + /// + /// Gets the allocation granularity value for OSX. + /// + /// + static long GetAllocationGranularityOSX() + { + return Syscall.sysconf(SysconfName._SC_PAGESIZE); + } + + /// + /// Implements the native method 'initIDs'. + /// + public static long initIDs() { #if FIRST_PASS throw new NotImplementedException(); #else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == false) - throw new global::java.lang.UnsupportedOperationException(); + if (RuntimeUtil.IsWindows) + return GetAllocationGranularityWindows(); + else if (RuntimeUtil.IsLinux) + return GetAllocationGranularityLinux(); + else if (RuntimeUtil.IsOSX) + return GetAllocationGranularityOSX(); + else + throw new global::java.io.IOException("Unsupported operation on platform."); +#endif + } + + /// + /// Implements the native method for 'map0'. + /// + public static long map0(object self, int prot, long position, long length) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsWindows) + return MapFileWindows((global::sun.nio.ch.FileChannelImpl)self, prot, position, length); + else + return MapFileUnix((global::sun.nio.ch.FileChannelImpl)self, prot, position, length); +#endif + } - return Syscall.mmap(IntPtr.Zero, (ulong)size, writeable ? MmapProts.PROT_WRITE | MmapProts.PROT_READ : MmapProts.PROT_READ, copy_on_write ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED, (int)stream.SafeFileHandle.DangerousGetHandle(), position); + /// + /// Implements the native method for 'unmap0'. + /// + /// + /// + /// + public static int unmap0(long address, long length) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (RuntimeUtil.IsWindows) + return UnmapWindows((IntPtr)address, length); + else + return UnmapUnix((IntPtr)address, length); #endif } + /// + /// Invokes the TransmitFile Win32 function. + /// + /// + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static unsafe extern int TransmitFile(IntPtr hSocket, IntPtr hFile, int nNumberOfBytesToWrite, int nNumberOfBytesPerSend, NativeOverlapped* lpOverlapped, void* lpTransmitBuffers, int dwReserved); + + /// + /// Implements the native method for 'transferTo0'. + /// + /// + /// + /// + /// + /// + /// + public static unsafe long transferTo0(object self, global::java.io.FileDescriptor src, long position, long count, global::java.io.FileDescriptor dst) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var source = FileDescriptorAccessor.GetStream(src); + if (source == null) + throw new global::java.io.IOException("Stream closed."); + if (source is not FileStream fs) + throw new global::java.io.IOException("Transfer failed. Cannot transfer from non-file stream."); + + try + { + if (RuntimeUtil.IsWindows) + { + var handle = IntPtr.Zero; + if (FileDescriptorAccessor.GetSocket(dst) is System.Net.Sockets.Socket s) + handle = s.Handle; + if (handle == IntPtr.Zero) + return global::sun.nio.ch.IOStatus.UNSUPPORTED_CASE; + + const int WSAEINVAL = 10022; + const int WSAENOTSOCK = 10038; + const int TF_USE_KERNEL_APC = 32; + const int PACKET_SIZE = 524288; + + // win32 expects + var chunkSize = (int)Math.Min(count, int.MaxValue); + + // move file to specified position + if (source.Position != position) + { + if (source.CanSeek == false) + return global::sun.nio.ch.IOStatus.UNSUPPORTED; + + source.Seek(position, SeekOrigin.Begin); + } + + int result = TransmitFile(handle, fs.SafeFileHandle.DangerousGetHandle(), chunkSize, PACKET_SIZE, null, null, TF_USE_KERNEL_APC); + if (result == 0) + { + return Marshal.GetLastWin32Error() switch + { + WSAEINVAL when count >= 0 => global::sun.nio.ch.IOStatus.UNSUPPORTED_CASE, + WSAENOTSOCK => global::sun.nio.ch.IOStatus.UNSUPPORTED_CASE, + _ => throw new global::java.io.IOException("Transfer failed.") + }; + } + + return chunkSize; + } + else + { + int handle = 0; + if (FileDescriptorAccessor.GetSocket(dst) is System.Net.Sockets.Socket s) + handle = (int)s.Handle; + if (FileDescriptorAccessor.GetStream(dst) is FileStream f) + handle = (int)f.SafeFileHandle.DangerousGetHandle(); + if (handle == 0) + return global::sun.nio.ch.IOStatus.UNSUPPORTED_CASE; + + var result = Syscall.sendfile(handle, (int)fs.SafeFileHandle.DangerousGetHandle(), ref position, (ulong)count); + if (result == -1) + { + return Stdlib.GetLastError() switch + { + Errno.EAGAIN => global::sun.nio.ch.IOStatus.UNAVAILABLE, + Errno.EINVAL when count >= 0 => global::sun.nio.ch.IOStatus.UNSUPPORTED_CASE, + Errno.EINTR => (long)global::sun.nio.ch.IOStatus.INTERRUPTED, + _ => throw new global::java.io.IOException("Transfer failed."), + }; + } + + return result; + } + } + catch (global::java.io.IOException) + { + throw; + } + catch (EndOfStreamException) + { + return global::sun.nio.ch.IOStatus.EOF; + } + catch (NotSupportedException) + { + return global::sun.nio.ch.IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Transfer failed.", e); + } +#endif + } + + /// + /// Implements the native method for 'position0'. + /// + /// + /// + /// + /// + public static long position0(object self, global::java.io.FileDescriptor fd, long offset) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + if (offset >= 0) + { + if (s.CanSeek == false) + return global::sun.nio.ch.IOStatus.UNSUPPORTED; + else + return s.Seek(offset, SeekOrigin.Begin); + } + + return s.Position; + } + catch (global::java.io.IOException) + { + throw; + } + catch (EndOfStreamException) + { + return global::sun.nio.ch.IOStatus.EOF; + } + catch (NotSupportedException) + { + return global::sun.nio.ch.IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Position failed.", e); + } +#endif + } + +#if FIRST_PASS == false + + /// + /// Invokes the CreateFileMapping Win32 function. + /// + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern SafeFileHandle CreateFileMapping(SafeFileHandle hFile, IntPtr lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName); + + /// + /// Invokes the MapViewOfFile Win32 function. + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern IntPtr MapViewOfFile(SafeFileHandle hFileMapping, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, IntPtr dwNumberOfBytesToMap); + + /// + /// Invokes the UnmapViewOfFile Win32 function. + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern int UnmapViewOfFile(IntPtr lpBaseAddress); + + /// + /// Implements memory mapped files on Windows. + /// + /// + /// + /// + /// + /// + /// + /// + /// + static long MapFileWindows(global::sun.nio.ch.FileChannelImpl self, int prot, long position, long length) + { + var s = FileDescriptorAccessor.GetStream(FileChannelImplAccessor.GetFd(self)); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + if (s is not FileStream fs) + throw new global::java.io.IOException("Map not supported."); + + try + { + const int ERROR_NOT_ENOUGH_MEMORY = 8; + + const int PAGE_READONLY = 2; + const int PAGE_READWRITE = 4; + const int PAGE_WRITECOPY = 8; + + const int FILE_MAP_WRITE = 2; + const int FILE_MAP_READ = 4; + const int FILE_MAP_COPY = 1; + + int fileProtect; + int mapAccess; + + switch (prot) + { + case MAP_RO: + fileProtect = PAGE_READONLY; + mapAccess = FILE_MAP_READ; + break; + case MAP_RW: + fileProtect = PAGE_READWRITE; + mapAccess = FILE_MAP_WRITE; + break; + case MAP_PV: + fileProtect = PAGE_WRITECOPY; + mapAccess = FILE_MAP_COPY; + break; + default: + throw new global::java.lang.Error(); + } + + var maxSize = length + position; + var hFileMapping = CreateFileMapping(fs.SafeFileHandle, IntPtr.Zero, fileProtect, (int)(maxSize >> 32), (int)maxSize, null); + var err = Marshal.GetLastWin32Error(); + if (hFileMapping.IsInvalid) + throw new global::java.io.IOException("File mapping failed.", new Win32Exception(err)); + + var h = MapViewOfFile(hFileMapping, mapAccess, (int)(position >> 32), (int)position, (IntPtr)length); + err = Marshal.GetLastWin32Error(); + hFileMapping.Close(); + + if (h == IntPtr.Zero) + { + if (err == ERROR_NOT_ENOUGH_MEMORY) + throw new global::java.lang.OutOfMemoryError("File mapping failed."); + + throw new global::java.io.IOException("File mapping failed.", new Win32Exception(err)); + } + + GC.AddMemoryPressure(length); + return (long)h; + } + finally + { + GC.KeepAlive(self); + } + } + + /// + /// Implements the unmapping of a file on Windows. + /// + /// + /// + /// + static int UnmapWindows(IntPtr address, long length) + { + UnmapViewOfFile(address); + GC.RemoveMemoryPressure(length); + return 0; + } + + /// + /// Implements memory mapped files on Unix. + /// + /// + /// + /// + /// + /// + /// + static long MapFileUnix(global::sun.nio.ch.FileChannelImpl self, int prot, long position, long length) + { + var s = FileDescriptorAccessor.GetStream(FileChannelImplAccessor.GetFd(self)); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + if (s is not FileStream fs) + throw new global::java.io.IOException("Map not supported."); + + try + { + MmapProts p = 0; + MmapFlags f = 0; + + switch (prot) + { + case MAP_RO: + p = MmapProts.PROT_READ; + f = MmapFlags.MAP_SHARED; + break; + case MAP_RW: + p = MmapProts.PROT_WRITE | MmapProts.PROT_READ; + f = MmapFlags.MAP_SHARED; + break; + case MAP_PV: + p = MmapProts.PROT_WRITE | MmapProts.PROT_READ; + f = MmapFlags.MAP_PRIVATE; + break; + } + + // inform the OS we will likely need this data shortly + if (Syscall.posix_fadvise((int)fs.SafeFileHandle.DangerousGetHandle(), position, length, PosixFadviseAdvice.POSIX_FADV_WILLNEED) is int e and not 0) + throw new global::java.io.IOException("File mapping failed.", new UnixIOException(e)); + + var i = Syscall.mmap(IntPtr.Zero, (ulong)length, p, f, (int)fs.SafeFileHandle.DangerousGetHandle(), position); + if (i == Syscall.MAP_FAILED) + { + var errno = Stdlib.GetLastError(); + if (errno == Errno.ENOMEM) + throw new global::java.lang.OutOfMemoryError("File mapping failed."); + + throw new global::java.io.IOException("File mapping failed."); + } + + GC.AddMemoryPressure(length); + return (long)(ulong)i; + } + finally + { + GC.KeepAlive(self); + } + } + + /// + /// Implements the unmapping of a mapped file on Unix. + /// + /// + /// + /// + static int UnmapUnix(IntPtr address, long length) + { + Syscall.munmap(address, (ulong)length); + GC.RemoveMemoryPressure(length); + return 0; + } + +#endif + } -} +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileDispatcherImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileDispatcherImpl.cs new file mode 100644 index 0000000000..262fde269e --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/FileDispatcherImpl.cs @@ -0,0 +1,999 @@ +using System; +using System.Buffers; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; + +using Microsoft.Win32.SafeHandles; + +using Mono.Unix; +using Mono.Unix.Native; + +using sun.nio.ch; + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the native methods for . + /// + static partial class FileDispatcherImpl + { + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + [StructLayout(LayoutKind.Sequential)] + struct iovec + { + + public IntPtr iov_base; + public int iov_len; + + } + + /// + /// Implements the native method 'read'. + /// + /// + /// + /// + /// + /// + /// + public static unsafe int read(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanRead == false) + return IOStatus.UNSUPPORTED; + + try + { + int r = -1; + +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + r = stream.Read(buf, 0, len); + Marshal.Copy(buf, 0, (IntPtr)address, r); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + r = stream.Read(new Span((byte*)(IntPtr)address, len)); +#endif + + return r == 0 ? IOStatus.EOF : r; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Read failed.", e); + } + finally + { + GC.KeepAlive(self); + } +#endif + } + + /// + /// Implements the native method 'pread'. + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe int pread(object self, object fd, long address, int len, long position) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanRead == false) + return IOStatus.UNSUPPORTED; + if (stream.CanSeek == false) + return IOStatus.UNSUPPORTED; + + if (RuntimeUtil.IsLinux && stream is FileStream fs) + { + try + { + if (fs.SafeFileHandle.IsInvalid) + return IOStatus.UNAVAILABLE; + + var n = Syscall.pread((int)fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)address, (ulong)len, position); + if (n == -1) + { + // translate return values + var errno = Stdlib.GetLastError(); + if (errno == Errno.EAGAIN) + return IOStatus.UNAVAILABLE; + if (errno == Errno.EINTR) + return IOStatus.INTERRUPTED; + + throw new global::java.io.IOException("Read failed.", new UnixIOException(errno)); + } + + return n > 0 ? (int)n : IOStatus.EOF; + } + finally + { + GC.KeepAlive(self); + } + } + else + { + var p = stream.Position; + + try + { + stream.Seek(position, SeekOrigin.Begin); + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Seek failed.", e); + } + + try + { + int length = -1; + +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + length = stream.Read(buf, 0, len); + Marshal.Copy(buf, 0, (IntPtr)address, length); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + length = stream.Read(new Span((void*)(IntPtr)address, len)); +#endif + + stream.Seek(p, SeekOrigin.Begin); + return length == 0 ? IOStatus.EOF : length; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Read failed.", e); + } + finally + { + GC.KeepAlive(self); + } + } +#endif + } + + /// + /// Implements the native method 'readv'. + /// + /// + /// + /// + /// + /// + public static unsafe long readv(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanRead == false) + return IOStatus.UNSUPPORTED; + + try + { + // map io vecors to read into + var vecs = new ReadOnlySpan((byte*)(IntPtr)address, len); + var length = 0; + + // issue independent reads for each vector + for (int i = 0; i < vecs.Length; i++) + { + int l; + +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(vecs[i].iov_len); + + try + { + l = stream.Read(buf, 0, vecs[i].iov_len); + Marshal.Copy(buf, 0, vecs[i].iov_base, l); + length += l; + + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + l = stream.Read(new Span((byte*)vecs[i].iov_base, vecs[i].iov_len)); + length += l; +#endif + + // we failed to read up to the requested amount, so we must be at the end + if (l < vecs[i].iov_len) + break; + } + + // if we read a total of zero bytes, we must be at the end of the file + return length == 0 ? IOStatus.EOF : length; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Read failed.", e); + } + finally + { + GC.KeepAlive(self); + } +#endif + } + + /// + /// Implements the native method 'write'. + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe int write0(object self, object fd, long address, int len, bool append) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanWrite == false) + return IOStatus.UNSUPPORTED; + + try + { + if (append) + { + if (stream.CanSeek == false) + return IOStatus.UNSUPPORTED; + + stream.Seek(0, SeekOrigin.End); + } + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Seek failed.", e); + } + + try + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + Marshal.Copy((IntPtr)address, buf, 0, len); + stream.Write(buf, 0, len); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + stream.Write(new ReadOnlySpan((byte*)(IntPtr)address, len)); +#endif + + return len; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Write failed.", e); + } + finally + { + GC.KeepAlive(self); + } +#endif + } + + /// + /// Implements the native method 'pwrite'. + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe int pwrite(object self, object fd, long address, int len, long position) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanWrite == false) + return IOStatus.UNSUPPORTED; + if (stream.CanSeek == false) + return IOStatus.UNSUPPORTED; + + if (RuntimeUtil.IsLinux && stream is FileStream fs) + { + try + { + if (fs.SafeFileHandle.IsInvalid) + return IOStatus.UNAVAILABLE; + + var n = Syscall.pwrite((int)fs.SafeFileHandle.DangerousGetHandle(), (void*)(IntPtr)address, (ulong)len, position); + if (n == -1) + { + var errno = Stdlib.GetLastError(); + if (errno == Errno.EAGAIN) + return IOStatus.UNAVAILABLE; + if (errno == Errno.EINTR) + return IOStatus.INTERRUPTED; + + throw new global::java.io.IOException("Write failed.", new UnixIOException(errno)); + } + + return (int)n; + } + finally + { + GC.KeepAlive(self); + } + } + else + { + var p = stream.Position; + + try + { + stream.Seek(position, SeekOrigin.Begin); + } + catch (EndOfStreamException) + { + return 0; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Seek failed.", e); + } + + try + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try + { + Marshal.Copy((IntPtr)address, buf, 0, len); + stream.Write(buf, 0, len); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + stream.Write(new ReadOnlySpan((byte*)(IntPtr)address, len)); +#endif + + stream.Seek(p, SeekOrigin.Begin); + return len; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Write failed.", e); + } + finally + { + GC.KeepAlive(self); + } + } +#endif + } + + /// + /// Implements the native method 'writev0'. + /// + /// + /// + /// + /// + /// + /// + public static unsafe long writev0(object self, object fd, long address, int len, bool append) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (len == 0) + return 0; + + var stream = FileDescriptorAccessor.GetStream(fd); + if (stream == null) + throw new global::java.io.IOException("Stream closed."); + + if (stream.CanWrite == false) + return IOStatus.UNSUPPORTED; + + try + { + if (append) + { + if (stream.CanSeek == false) + return IOStatus.UNSUPPORTED; + + stream.Seek(0, SeekOrigin.End); + } + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Seek failed.", e); + } + + try + { + // map io vecors to read into + var vecs = new ReadOnlySpan((byte*)(IntPtr)address, len); + var length = 0; + + // issue independent writes for each vector + for (int i = 0; i < vecs.Length; i++) + { +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(vecs[i].iov_len); + + try + { + Marshal.Copy(vecs[i].iov_base, buf, 0, vecs[i].iov_len); + stream.Write(buf, 0, vecs[i].iov_len); + length += vecs[i].iov_len; + + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + stream.Write(new ReadOnlySpan((byte*)vecs[i].iov_base, vecs[i].iov_len)); + length += vecs[i].iov_len; +#endif + } + + return length; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Write failed.", e); + } + finally + { + GC.KeepAlive(self); + } +#endif + } + + /// + /// Implements the native method 'force'. + /// + /// + /// + /// + /// + public static int force(object self, object fd, bool metaData) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + FileDescriptorAccessor.InvokeSync(fd); + return 0; +#endif + } + + /// + /// Implements the native method 'truncate'. + /// + /// + /// + /// + /// + public static int truncate(object self, object fd, long size) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + + if (s.Length < size) + return 0; + + try + { + s.SetLength(size); + return 0; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Truncate failed.", e); + } +#endif + } + + /// + /// Implements the native method 'size'. + /// + /// + /// + /// + public static long size(object self, object fd) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + return s.Length; + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Size failed.", e); + } +#endif + } + + /// + /// Invokes the LOCKFILEEX Win32 function. + /// + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern unsafe int LockFileEx(SafeFileHandle hFile, int dwFlags, int dwReserved, int nNumberOfBytesToLockLow, int nNumberOfBytesToLockHigh, NativeOverlapped* lpOverlapped); + + /// + /// Invokes the UNLOCKFILEEX Win32 function. + /// + /// + /// + /// + /// + /// + /// + [DllImport("kernel32", SetLastError = true)] + static extern unsafe int UnlockFileEx(SafeFileHandle hFile, int dwReserved, int nNumberOfBytesToUnlockLow, int nNumberOfBytesToUnlockHigh, NativeOverlapped* lpOverlapped); + + /// + /// Implements the native method 'lock'. + /// + /// + /// + /// + /// + /// + /// + /// + public static unsafe int @lock(object self, object fd, bool blocking, long pos, long size, bool shared) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + if (s is not FileStream fs) + throw new global::java.io.IOException("Not supported on non-file."); + + try + { + if (RuntimeUtil.IsWindows) + { + NativeOverlapped* optr = null; + + try + { + const int LOCKFILE_FAIL_IMMEDIATELY = 1; + const int LOCKFILE_EXCLUSIVE_LOCK = 2; + const int ERROR_LOCK_VIOLATION = 33; + + var o = new Overlapped(); + o.OffsetLow = (int)pos; + o.OffsetHigh = (int)(pos >> 32); + + int flags = 0; + if (blocking == false) + flags |= LOCKFILE_FAIL_IMMEDIATELY; + if (shared == false) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + + optr = o.Pack(null, null); + int result = LockFileEx(fs.SafeFileHandle, flags, 0, (int)size, (int)(size >> 32), optr); + if (result == 0) + { + var error = Marshal.GetLastWin32Error(); + if (error != ERROR_LOCK_VIOLATION) + throw new Win32Exception(error); + + if ((flags & LOCKFILE_FAIL_IMMEDIATELY) != 0) + return FileDispatcher.NO_LOCK; + + throw new Win32Exception(error); + } + + return FileDispatcher.LOCKED; + } + finally + { + Overlapped.Free(optr); + } + } + else if (RuntimeUtil.IsLinux) + { + var fl = new Flock(); + fl.l_whence = SeekFlags.SEEK_SET; + fl.l_len = size == long.MaxValue ? 0 : size; + fl.l_start = pos; + fl.l_type = shared ? LockType.F_RDLCK : LockType.F_WRLCK; + var cmd = blocking ? FcntlCommand.F_SETLKW : FcntlCommand.F_SETLK; + + var r = Syscall.fcntl((int)fs.SafeFileHandle.DangerousGetHandle(), cmd, ref fl); + if (r == -1) + { + var errno = Stdlib.GetLastError(); + if ((cmd == FcntlCommand.F_SETLK) && (errno == Errno.EAGAIN || errno == Errno.EACCES)) + return FileDispatcher.NO_LOCK; + if (errno == Errno.EINTR) + return FileDispatcher.INTERRUPTED; + + UnixMarshal.ThrowExceptionForError(errno); + } + + return FileDispatcher.LOCKED; + } + else + { + // fallback to .NET version for non-Windows + fs.Lock(pos, size); + return shared ? FileDispatcher.RET_EX_LOCK : FileDispatcher.LOCKED; + } + } + catch (EndOfStreamException) + { + return IOStatus.EOF; + } + catch (NotSupportedException) + { + return IOStatus.UNSUPPORTED; + } + catch (ObjectDisposedException) + { + return IOStatus.UNAVAILABLE; + } + catch (Exception e) + { + throw new global::java.io.IOException("Lock failed.", e); + } +#endif + } + + /// + /// Implements the native method 'release'. + /// + /// + /// + /// + /// + /// + public static unsafe void release(object self, object fd, long pos, long size) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + if (s is not FileStream fs) + throw new global::java.io.IOException("Not supported on non-file."); + + try + { + if (RuntimeUtil.IsWindows) + { + NativeOverlapped* optr = null; + + try + { + const int ERROR_NOT_LOCKED = 158; + + var o = new Overlapped(); + o.OffsetLow = (int)pos; + o.OffsetHigh = (int)(pos >> 32); + + optr = o.Pack(null, null); + int result = UnlockFileEx(fs.SafeFileHandle, 0, (int)size, (int)(size >> 32), optr); + if (result == 0) + { + var error = Marshal.GetLastWin32Error(); + if (error != ERROR_NOT_LOCKED) + throw new Win32Exception(error); + } + } + finally + { + if (optr != null) + Overlapped.Free(optr); + } + } + else if (RuntimeUtil.IsLinux) + { + var fl = new Flock(); + fl.l_whence = SeekFlags.SEEK_SET; + fl.l_len = size == long.MaxValue ? 0 : size; + fl.l_start = pos; + fl.l_type = LockType.F_UNLCK; + + var r = Syscall.fcntl((int)fs.SafeFileHandle.DangerousGetHandle(), FcntlCommand.F_SETLK, ref fl); + if (r == -1) + UnixMarshal.ThrowExceptionForLastErrorIf(r); + } + else + { + fs.Unlock(pos, size); + } + } + catch (System.IO.IOException e) when (NotLockedHack.IsErrorNotLocked(e) == false) + { + throw new global::java.io.IOException("Release failed.", e); + } + catch (Exception e) + { + throw new global::java.io.IOException("Release failed.", e); + } +#endif + } + + /// + /// Implements the native method 'close'. + /// + /// + /// + public static void close(object self, object fd) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var s = FileDescriptorAccessor.GetStream(fd); + if (s == null) + throw new global::java.io.IOException("Stream closed."); + + try + { + FileDescriptorAccessor.SetStream(fd, null); + s.Close(); + } + catch + { + + } +#endif + } + + /// + /// Implements the native method 'duplicateForMapping'. + /// + /// + /// + /// + public static object duplicateForMapping(object self, object fd) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var newfd = FileDescriptorAccessor.Init(); + return newfd; +#endif + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/IOUtil.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/IOUtil.cs new file mode 100644 index 0000000000..03f12fcf79 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/IOUtil.cs @@ -0,0 +1,143 @@ +using System; +using System.Net.Sockets; +using System.Security.Cryptography; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Implements the external methods for . + /// + static class IOUtil + { + +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + + /// + /// Implements the native method 'initIDs'. + /// + public static void initIDs() + { + + } + + /// + /// Implements the native method 'randomBytes'. + /// + public static bool randomBytes(byte[] someBytes) + { + rng.GetBytes(someBytes); + return true; + } + + /// + /// Implements the native method 'makePipe'. + /// + public static long makePipe(bool blocking) + { + throw new NotImplementedException(); + } + + /// + /// Implements the native method 'drain'. + /// + public static bool drain(int fd) + { + throw new NotSupportedException(); + } + + /// + /// Implements the native method 'configureBlocking'. + /// + public static void configureBlocking(object fd, bool blocking) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.io.IOException("Socket closed."); + + try + { + // defer operation if outstanding task + // .NET socket fails to set blocking if outstanding Connect + var task = FileDescriptorAccessor.GetTask(fd); + if (task != null) + FileDescriptorAccessor.SetTask(fd, task.ContinueWith(_ => socket.Blocking = blocking)); + else + socket.Blocking = blocking; + } + catch (SocketException e) + { + throw new global::java.net.SocketException(e.Message); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'fdVal'. + /// + public static int fdVal(object fd) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return FileDescriptorAccessor.GetFd(fd); +#endif + } + + /// + /// Implements the native method 'setfdVal'. + /// + public static void setfdVal(global::java.io.FileDescriptor fd, int value) + { + throw new NotSupportedException(); + } + + /// + /// Implements the native method 'fdLimit'. + /// + public static int fdLimit() + { + throw new NotSupportedException(); + } + + /// + /// Implements the native method 'iovMax'. + /// + public static int iovMax() + { + return 16; + } + + /// + /// Implements the native method 'load'. + /// + public static void load() + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/Net.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/Net.cs index 0d1ef93dec..11f2832e4a 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/Net.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/Net.cs @@ -4,8 +4,12 @@ using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading.Tasks; -using IKVM.Runtime.Java.Externs.java.net; +using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; using static IKVM.Java.Externs.java.net.SocketImplUtil; @@ -18,7 +22,10 @@ namespace IKVM.Java.Externs.sun.nio.ch static class Net { -#if !FIRST_PASS +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); [StructLayout(LayoutKind.Explicit)] unsafe struct sockaddr_storage @@ -124,9 +131,27 @@ struct group_source_req static readonly int MCAST_JOIN_SOURCE_GROUP; static readonly int MCAST_LEAVE_SOURCE_GROUP; + /// + /// Invokes the 'setsockopt' Posix function. + /// + /// + /// + /// + /// + /// + /// [DllImport("libc", SetLastError = true)] static unsafe extern int setsockopt(SafeHandle sockfd, int level, int optname, void* optval, int optlen); + /// + /// Invokes the 'getsockopt' Posix function. + /// + /// + /// + /// + /// + /// + /// [DllImport("libc", SetLastError = true)] static unsafe extern int getsockopt(SafeHandle sockfd, int level, int optname, void* optval, int* optlen); @@ -135,7 +160,7 @@ struct group_source_req /// static Net() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) { AF_UNSPEC = 0; AF_INET = 2; @@ -149,7 +174,7 @@ static Net() MCAST_JOIN_SOURCE_GROUP = 45; MCAST_LEAVE_SOURCE_GROUP = 46; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (RuntimeUtil.IsLinux) { AF_UNSPEC = 0; AF_INET = 2; @@ -171,15 +196,6 @@ static Net() #endif - /// - /// Implements the native method for 'initIDs'. - /// - /// - public static void initIDs() - { - - } - /// /// Implements the native method for 'isIPv6Available0'. /// @@ -195,7 +211,7 @@ public static bool isIPv6Available0() /// public static int isExclusiveBindAvailable() { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1 : -1; + return RuntimeUtil.IsWindows ? 1 : -1; } /// @@ -216,12 +232,135 @@ public static bool canJoin6WithIPv4Group0() return false; } + /// + /// Implements the native method for 'socket0'. + /// + /// + /// + /// + /// + /// + public static object socket0(bool preferIPv6, bool stream, bool reuse, bool fastLoopback) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return InvokeFunc(() => + { + var addressFamily = preferIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; + var socketType = stream ? SocketType.Stream : SocketType.Dgram; + var protocolType = stream ? ProtocolType.Tcp : ProtocolType.Udp; + var socket = new Socket(addressFamily, socketType, protocolType); + + if (preferIPv6) + socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); + + if (stream == false) + SetConnectionReset(socket, false); + + var fd = new global::java.io.FileDescriptor(); + FileDescriptorAccessor.SetSocket(fd, socket); + return fd; + }); +#endif + } + + /// + /// Implements the native method for 'bind0'. + /// + /// + /// + /// + /// + /// + /// + public static void bind0(global::java.io.FileDescriptor fd, bool preferIPv6, bool useExclBind, global::java.net.InetAddress addr, int port) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + InvokeAction(() => + { + InvokeWithSocket(fd, socket => + { + if (useExclBind && (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 1) + socket.ExclusiveAddressUse = true; + + socket.Bind(new IPEndPoint(addr.ToIPAddress(), port)); + }); + }); +#endif + } + + /// + /// Implements the native method for 'listen'. + /// + /// + /// + /// + public static void listen(object fd, int backlog) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + InvokeAction(() => + { + InvokeWithSocket(fd, socket => + { + socket.Listen(backlog); + }); + }); +#endif + } + + /// + /// Implements the native method for 'connect0'. + /// + /// + /// + /// + /// + /// + public static int connect0(bool preferIPv6, object fd, global::java.net.InetAddress remote, int remotePort) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return InvokeFunc(() => + { + return InvokeWithSocket(fd, socket => + { + var ep = new IPEndPoint(remote.ToIPAddress(), remotePort); + var datagram = socket.SocketType == SocketType.Dgram; + if (datagram || socket.Blocking) + { + socket.Connect(ep); + if (datagram) + SetConnectionReset(socket, true); + + return 1; + } + else + { + var task = FileDescriptorAccessor.GetTask(fd); + if (task != null) + throw new global::java.net.SocketException("Outstanding async request."); + + task = Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, ep, null); + FileDescriptorAccessor.SetTask(fd, task); + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + }); + }); +#endif + } + /// /// Implements the native method for 'shutdown'. /// /// /// - public static void shutdown(global::java.io.FileDescriptor fd, int how) + public static void shutdown(object fd, int how) { #if FIRST_PASS throw new NotImplementedException(); @@ -241,7 +380,7 @@ public static void shutdown(global::java.io.FileDescriptor fd, int how) /// /// /// - public static int localPort(global::java.io.FileDescriptor fd) + public static int localPort(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -261,8 +400,7 @@ public static int localPort(global::java.io.FileDescriptor fd) /// /// /// - /// - public static global::java.net.InetAddress localInetAddress(global::java.io.FileDescriptor fd) + public static global::java.net.InetAddress localInetAddress(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -283,8 +421,7 @@ public static int localPort(global::java.io.FileDescriptor fd) /// /// /// - /// - public static int remotePort(global::java.io.FileDescriptor fd) + public static int remotePort(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -304,7 +441,7 @@ public static int remotePort(global::java.io.FileDescriptor fd) /// /// /// - public static global::java.net.InetAddress remoteInetAddress(global::java.io.FileDescriptor fd) + public static global::java.net.InetAddress remoteInetAddress(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -313,7 +450,7 @@ public static int remotePort(global::java.io.FileDescriptor fd) { return InvokeWithSocket(fd, socket => { - return global::java.net.SocketUtil.getInetAddressFromIPEndPoint((System.Net.IPEndPoint)socket.RemoteEndPoint); + return ((IPEndPoint)socket.RemoteEndPoint).ToInetAddress(); }); }); #endif @@ -327,8 +464,7 @@ public static int remotePort(global::java.io.FileDescriptor fd) /// /// /// - /// - public static int getIntOption0(global::java.io.FileDescriptor fd, bool mayNeedConversion, int level, int opt) + public static int getIntOption0(object fd, bool mayNeedConversion, int level, int opt) { #if FIRST_PASS throw new NotImplementedException(); @@ -367,7 +503,7 @@ public static int getIntOption0(global::java.io.FileDescriptor fd, bool mayNeedC /// /// /// - public static void setIntOption0(global::java.io.FileDescriptor fd, bool mayNeedConversion, int level, int opt, int arg, bool isIPv6) + public static void setIntOption0(object fd, bool mayNeedConversion, int level, int opt, int arg, bool isIPv6) { #if FIRST_PASS throw new NotImplementedException(); @@ -407,6 +543,40 @@ public static void setIntOption0(global::java.io.FileDescriptor fd, bool mayNeed #endif } + /// + /// Implements the native method for 'poll'. + /// + /// + /// + /// + /// + /// + public static int poll(object fd, int events, long timeout) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + return InvokeFunc(() => + { + return InvokeWithSocket(fd, socket => + { + var selectMode = events switch + { + int e when e == global::sun.nio.ch.Net.POLLCONN => SelectMode.SelectWrite, + int e when e == global::sun.nio.ch.Net.POLLOUT => SelectMode.SelectWrite, + int e when e == global::sun.nio.ch.Net.POLLIN => SelectMode.SelectRead, + _ => throw new NotSupportedException(), + }; + + if (socket.Poll(timeout * 1000L > int.MaxValue ? int.MaxValue : (int)timeout * 1000, selectMode)) + return events; + + return 0; + }); + }); +#endif + } + /// /// Implements the native method for 'joinOrDrop4'. /// @@ -417,7 +587,7 @@ public static void setIntOption0(global::java.io.FileDescriptor fd, bool mayNeed /// /// /// - public unsafe static int joinOrDrop4(bool join, global::java.io.FileDescriptor fd, int group, int interf, int source) + public unsafe static int joinOrDrop4(bool join, object fd, int group, int interf, int source) { #if FIRST_PASS throw new NotImplementedException(); @@ -441,7 +611,7 @@ public unsafe static int joinOrDrop4(bool join, global::java.io.FileDescriptor f optionValue.imr_sourceaddr = Unsafe.As(ref imrSourceAddr); optionValue.imr_interface = Unsafe.As(ref imrInterface); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) { var v = ArrayPool.Shared.Rent(sizeof(ip_mreq_source)); @@ -457,7 +627,7 @@ public unsafe static int joinOrDrop4(bool join, global::java.io.FileDescriptor f ArrayPool.Shared.Return(v); } } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (RuntimeUtil.IsLinux) { #if NETCOREAPP if (setsockopt(socket.SafeHandle, IPPROTO_IP, join ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP, &optionValue, sizeof(ip_mreq_source)) != 0) @@ -487,8 +657,7 @@ public unsafe static int joinOrDrop4(bool join, global::java.io.FileDescriptor f /// /// /// - /// - public static unsafe int blockOrUnblock4(bool block, global::java.io.FileDescriptor fd, int group, int interf, int source) + public static unsafe int blockOrUnblock4(bool block, object fd, int group, int interf, int source) { #if FIRST_PASS throw new NotImplementedException(); @@ -535,7 +704,7 @@ public static unsafe int blockOrUnblock4(bool block, global::java.io.FileDescrip /// /// /// - public static unsafe int joinOrDrop6(bool join, global::java.io.FileDescriptor fd, byte[] group, int index, byte[] source) + public static unsafe int joinOrDrop6(bool join, object fd, byte[] group, int index, byte[] source) { #if FIRST_PASS throw new NotImplementedException(); @@ -573,7 +742,7 @@ public static unsafe int joinOrDrop6(bool join, global::java.io.FileDescriptor f groupSourceReq.gsr_group = Unsafe.As(ref groupSockAddr); groupSourceReq.gsr_source = Unsafe.As(ref sourceSockAddr); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeUtil.IsWindows) { var v = ArrayPool.Shared.Rent(sizeof(group_source_req)); @@ -589,7 +758,7 @@ public static unsafe int joinOrDrop6(bool join, global::java.io.FileDescriptor f ArrayPool.Shared.Return(v); } } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (RuntimeUtil.IsLinux) { #if NETCOREAPP if (setsockopt(socket.SafeHandle, IPPROTO_IPV6, join ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP, &groupSourceReq, sizeof(group_source_req)) != 0) @@ -620,7 +789,7 @@ public static unsafe int joinOrDrop6(bool join, global::java.io.FileDescriptor f /// /// /// - public static unsafe int blockOrUnblock6(bool block, global::java.io.FileDescriptor fd, byte[] group, int index, byte[] source) + public static unsafe int blockOrUnblock6(bool block, object fd, byte[] group, int index, byte[] source) { #if FIRST_PASS throw new NotImplementedException(); @@ -679,7 +848,7 @@ public static unsafe int blockOrUnblock6(bool block, global::java.io.FileDescrip /// /// /// - public static void setInterface4(global::java.io.FileDescriptor fd, int interf) + public static void setInterface4(object fd, int interf) { #if FIRST_PASS throw new NotImplementedException(); @@ -700,7 +869,7 @@ public static void setInterface4(global::java.io.FileDescriptor fd, int interf) /// /// /// - public static int getInterface4(global::java.io.FileDescriptor fd) + public static int getInterface4(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -721,7 +890,7 @@ public static int getInterface4(global::java.io.FileDescriptor fd) /// /// /// - public static void setInterface6(global::java.io.FileDescriptor fd, int index) + public static void setInterface6(object fd, int index) { #if FIRST_PASS throw new NotImplementedException(); @@ -742,7 +911,7 @@ public static void setInterface6(global::java.io.FileDescriptor fd, int index) /// /// /// - public static int getInterface6(global::java.io.FileDescriptor fd) + public static int getInterface6(object fd) { #if FIRST_PASS throw new NotImplementedException(); @@ -758,182 +927,68 @@ public static int getInterface6(global::java.io.FileDescriptor fd) } /// - /// Implements the native method for 'socket0'. + /// Implements the native method for 'initIDs'. /// - /// - /// - /// /// - public static global::java.io.FileDescriptor socket0(bool preferIPv6, bool stream, bool reuse) + public static void initIDs() { -#if FIRST_PASS - throw new NotImplementedException(); -#else - return InvokeFunc(() => - { - var addressFamily = preferIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; - var socketType = stream ? SocketType.Stream : SocketType.Dgram; - var protocolType = stream ? ProtocolType.Tcp : ProtocolType.Udp; - var socket = new Socket(addressFamily, socketType, protocolType); - - if (preferIPv6) - socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); - if (!stream) - setConnectionReset(socket, false); - - var fd = new global::java.io.FileDescriptor(); - fd.setSocket(socket); - return fd; - }); -#endif } /// - /// Implements the native method for 'bind0'. + /// Implements the native method 'pollinValue'. /// - /// - /// - /// - /// - /// - /// - public static void bind0(global::java.io.FileDescriptor fd, bool preferIPv6, bool useExclBind, global::java.net.InetAddress addr, int port) - { -#if FIRST_PASS - throw new NotImplementedException(); -#else - InvokeAction(() => - { - InvokeWithSocket(fd, socket => - { - if (useExclBind && (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 1) - socket.ExclusiveAddressUse = true; - - socket.Bind(new System.Net.IPEndPoint(addr.ToIPAddress(), port)); - }); - }); -#endif - } + /// + public static short pollinValue() => 0x0001; /// - /// Implements the native method for 'listen'. + /// Implements the native method 'polloutValue'. /// - /// - /// - /// - public static void listen(global::java.io.FileDescriptor fd, int backlog) - { -#if FIRST_PASS - throw new NotImplementedException(); -#else - InvokeAction(() => - { - InvokeWithSocket(fd, socket => - { - socket.Listen(backlog); - }); - }); -#endif - } - - internal static void setConnectionReset(Socket socket, bool enable) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // Windows 2000 introduced a "feature" that causes it to return WSAECONNRESET from receive, - // if a previous send resulted in an ICMP port unreachable. For unconnected datagram sockets, - // we disable this feature by using this ioctl. - const int IOC_IN = unchecked((int)0x80000000); - const int IOC_VENDOR = 0x18000000; - const int SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; - socket.IOControl(SIO_UDP_CONNRESET, new byte[] { enable ? (byte)1 : (byte)0 }, null); - } - } + /// + public static short polloutValue() => 0x0004; /// - /// Implements the native method for 'connect0'. + /// Implements the native method 'pollerrValue'. /// - /// - /// - /// - /// /// - public static int connect0(bool preferIPv6, global::java.io.FileDescriptor fd, global::java.net.InetAddress remote, int remotePort) - { -#if FIRST_PASS - throw new NotImplementedException(); -#else - return InvokeFunc(() => - { - return InvokeWithSocket(fd, socket => - { - var ep = new IPEndPoint(remote.ToIPAddress(), remotePort); - var datagram = socket.SocketType == SocketType.Dgram; - if (datagram || fd.isSocketBlocking()) - { - socket.Connect(ep); - if (datagram) - setConnectionReset(socket, true); - - return 1; - } - else - { - fd.setAsyncResult(socket.BeginConnect(ep, null, null)); - return global::sun.nio.ch.IOStatus.UNAVAILABLE; - } - }); - }); -#endif - } + public static short pollerrValue() => 0x0008; /// - /// Implements the native method for 'poll'. + /// Implements the native method 'pollhupValue'. /// - /// - /// - /// /// - /// - public static int poll(global::java.io.FileDescriptor fd, int events, long timeout) - { -#if FIRST_PASS - throw new NotImplementedException(); -#else - return InvokeFunc(() => - { - return InvokeWithSocket(fd, socket => - { - var selectMode = events switch - { - int e when e == global::sun.nio.ch.Net.POLLCONN => SelectMode.SelectWrite, - int e when e == global::sun.nio.ch.Net.POLLOUT => SelectMode.SelectWrite, - int e when e == global::sun.nio.ch.Net.POLLIN => SelectMode.SelectRead, - _ => throw new NotSupportedException(), - }; - - if (socket.Poll(timeout * 1000L > int.MaxValue ? int.MaxValue : (int)timeout * 1000, selectMode)) - return events; - - return 0; - }); - }); -#endif - } - - public static short pollinValue() => 0x0001; - - public static short polloutValue() => 0x0004; - - public static short pollerrValue() => 0x0008; - public static short pollhupValue() => 0x0010; + /// + /// Implements the native method 'pollnvalValue'. + /// + /// public static short pollnvalValue() => 0x0020; + /// + /// Implements the native method 'pollconnValue'. + /// + /// public static short pollconnValue() => 0x0002; + /// + /// Windows 2000 introduced a "feature" that causes it to return WSAECONNRESET from receive, + /// if a previous send resulted in an ICMP port unreachable. For unconnected datagram sockets, + /// we disable this feature by using this ioctl. + /// + /// + /// + internal static void SetConnectionReset(Socket socket, bool enable) + { + if (RuntimeUtil.IsWindows) + { + const int IOC_IN = unchecked((int)0x80000000); + const int IOC_VENDOR = 0x18000000; + const int SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; + socket.IOControl(SIO_UDP_CONNRESET, new byte[] { enable ? (byte)1 : (byte)0 }, null); + } + } + #if !FIRST_PASS /// @@ -945,11 +1000,11 @@ public static int poll(global::java.io.FileDescriptor fd, int events, long timeo /// /// /// - static void InvokeWithSocket(global::java.io.FileDescriptor fd, Action action) + static void InvokeWithSocket(object fd, Action action) { - var socket = fd?.getSocket(); + var socket = FileDescriptorAccessor.GetSocket(fd); if (socket == null) - throw new global::java.net.SocketException("Socket closed"); + throw new global::java.net.SocketException("Socket closed."); action(socket); } @@ -963,11 +1018,11 @@ static void InvokeWithSocket(global::java.io.FileDescriptor fd, Action a /// /// /// - static TResult InvokeWithSocket(global::java.io.FileDescriptor fd, Func func) + static TResult InvokeWithSocket(object fd, Func func) { - var socket = fd?.getSocket(); + var socket = FileDescriptorAccessor.GetSocket(fd); if (socket == null) - throw new global::java.net.SocketException("Socket closed"); + throw new global::java.net.SocketException("Socket closed."); return func(socket); } diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/NotLockedHack.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/NotLockedHack.cs new file mode 100644 index 0000000000..e454e27546 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/NotLockedHack.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace IKVM.Java.Externs.sun.nio.ch +{ + + /// + /// Attempts to lock an unlocked file to capture teh error message, since IOException provides no way to know + /// whether the reason corresponds to not being locked. + /// + static class NotLockedHack + { + + static readonly string message; + + /// + /// Initializes the static instance. + /// + static NotLockedHack() + { + try + { + var tmp = Path.GetTempFileName(); + + using (var fs = new FileStream(tmp, FileMode.Create)) + { + try + { + fs.Unlock(0, 1); + } + catch (System.IO.IOException e) + { + message = e.Message; + } + } + + File.Delete(tmp); + } + catch (Exception) + { + + } + } + + /// + /// Returns true if the exception represents the file not being locked. + /// + /// + /// + public static bool IsErrorNotLocked(IOException e) + { + return e.Message == message; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/ServerSocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/ServerSocketChannelImpl.cs index 5752b16681..ba2b144666 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/ServerSocketChannelImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/ServerSocketChannelImpl.cs @@ -1,49 +1,100 @@ -/* - Copyright (C) 2011 Jeroen Frijters +using System; +using System.Net.Sockets; +using System.Runtime.InteropServices; - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; namespace IKVM.Java.Externs.sun.nio.ch { + /// + /// Implements the native methods for 'ServerSocketChannelImpl'. + /// static class ServerSocketChannelImpl { - public static int accept0(object self, global::java.io.FileDescriptor ssfd, global::java.io.FileDescriptor newfd, object isaa) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + [Flags] + enum HANDLE_FLAGS + { + + NONE = 0, + INHERIT = 0x00000001, + PROTECT_FROM_CLOSE = 0x00000002, + + } + +#if NETFRAMEWORK + + /// + /// Invokes the Win32 SetHandleInformation function. + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags); +#else + + /// + /// Invokes the Win32 SetHandleInformation function. + /// + /// + /// + /// + /// + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetHandleInformation(SafeHandle hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags); + +#endif + + + /// + /// Implements the native method 'accept0'. + /// + /// + /// + /// + /// + /// + /// + public static int accept0(object self, object ssfd, object newfd, object isaa) { #if FIRST_PASS throw new NotSupportedException(); #else + var socket = FileDescriptorAccessor.GetSocket(ssfd); + if (socket == null) + throw new global::java.io.IOException("Socket closed."); + try { - System.Net.Sockets.Socket netSocket = ssfd.getSocket(); - if (netSocket.Blocking || netSocket.Poll(0, System.Net.Sockets.SelectMode.SelectRead)) + if (socket.Blocking || socket.Poll(0, SelectMode.SelectRead)) { - System.Net.Sockets.Socket accsock = netSocket.Accept(); - newfd.setSocket(accsock); - System.Net.IPEndPoint ep = (System.Net.IPEndPoint)accsock.RemoteEndPoint; - ((global::java.net.InetSocketAddress[])isaa)[0] = new global::java.net.InetSocketAddress(global::java.net.SocketUtil.getInetAddressFromIPEndPoint(ep), ep.Port); + var newSocket = socket.Accept(); + if (newSocket == null) + throw new global::java.net.SocketException("Invalid socket."); + +// // allow socket handle to be inherited by child processes on Windows +// if (RuntimeUtil.IsWindows) +//#if NETFRAMEWORK +// SetHandleInformation(newSocket.Handle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.NONE); +//#else +// SetHandleInformation(newSocket.SafeHandle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.NONE); +//#endif + + FileDescriptorAccessor.SetSocket(newfd, newSocket); + var ep = (System.Net.IPEndPoint)newSocket.RemoteEndPoint; + ((object[])isaa)[0] = ep.ToInetSocketAddress(); return 1; } else @@ -51,17 +102,24 @@ public static int accept0(object self, global::java.io.FileDescriptor ssfd, glob return global::sun.nio.ch.IOStatus.UNAVAILABLE; } } - catch (System.Net.Sockets.SocketException x) + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) { - throw global::java.net.SocketUtil.convertSocketExceptionToIOException(x); + throw new global::java.net.SocketException("Socket closed."); } - catch (System.ObjectDisposedException) + catch (Exception e) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.io.IOException(e); } #endif } + /// + /// Implements the native method 'initIDs'. + /// public static void initIDs() { diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketChannelImpl.cs index 5ee9d7402b..a1760ccfbb 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketChannelImpl.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketChannelImpl.cs @@ -1,51 +1,50 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - using System; +using System.Buffers; +using System.Net.Sockets; -using FileDescriptor = java.io.FileDescriptor; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; namespace IKVM.Java.Externs.sun.nio.ch { + /// + /// Implements the native methods for 'SocketChannelImpl'. + /// static class SocketChannelImpl { - public static int checkConnect(FileDescriptor fd, bool block, bool ready) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + /// + /// Implements the native method 'checkConnect'. + /// + /// + /// + /// + /// + public static int checkConnect(object fd, bool block, bool ready) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.io.IOException("Socket closed."); + try { - IAsyncResult asyncConnect = fd.getAsyncResult(); - if (block || ready || asyncConnect.IsCompleted) + var task = FileDescriptorAccessor.GetTask(fd); + if (block || ready || task.IsCompleted) { - fd.setAsyncResult(null); - fd.getSocket().EndConnect(asyncConnect); - // work around for blocking issue - fd.getSocket().Blocking = fd.isSocketBlocking(); + FileDescriptorAccessor.SetTask(fd, null); + task.GetAwaiter().GetResult(); return 1; } else @@ -53,34 +52,79 @@ public static int checkConnect(FileDescriptor fd, bool block, bool ready) return global::sun.nio.ch.IOStatus.UNAVAILABLE; } } - catch (System.Net.Sockets.SocketException x) + catch (SocketException e) { - throw new global::java.net.ConnectException(x.Message); + throw new global::java.net.ConnectException(e.Message); } catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); } #endif } - public static int sendOutOfBandData(FileDescriptor fd, byte data) + /// + /// Implements the native method 'sendOutOfBandData'. + /// + /// + /// + /// + public static int sendOutOfBandData(object fd, byte data) { #if FIRST_PASS - return 0; + throw new NotImplementedException(); #else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.io.IOException("Socket closed."); + try { - fd.getSocket().Send(new byte[] { data }, 1, System.Net.Sockets.SocketFlags.OutOfBand); - return 1; +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(1); + + try + { + buf[0] = data; + return socket.Send(buf, 1, SocketFlags.OutOfBand); + } + finally + { + ArrayPool.Shared.Return(buf); + } +#else + unsafe + { + var buf = (Span)stackalloc byte[1]; + buf[0] = data; + return socket.Send(buf, SocketFlags.OutOfBand); + } +#endif + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + if (RuntimeUtil.IsWindows) + throw new global::java.net.SocketException("Resource temporarily unavailable"); + else if (RuntimeUtil.IsOSX) + throw new global::java.net.SocketException("No buffer space available"); + else + return global::sun.nio.ch.IOStatus.UNAVAILABLE; } - catch (System.Net.Sockets.SocketException e) + catch (SocketException e) { - throw new global::java.net.ConnectException(e.Message); + throw e.ToIOException(); } catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); } #endif } diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketDispatcher.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketDispatcher.cs index 9a24ca5395..85c63b0807 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketDispatcher.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/SocketDispatcher.cs @@ -1,152 +1,363 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; -using System.Collections.Generic; +using System; +using System.Buffers; using System.Net.Sockets; +using System.Runtime.InteropServices; -using IKVM.Java.Externs.java.net; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Util.Java.Net; namespace IKVM.Java.Externs.sun.nio.ch { + /// + /// Implements the native methods for 'SocketDispatcher'. + /// static class SocketDispatcher { - public static long read(object nd, global::java.io.FileDescriptor fd, global::java.nio.ByteBuffer[] bufs, int offset, int length) +#if FIRST_PASS == false + + static FileDescriptorAccessor fileDescriptorAccessor; + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + +#endif + + [StructLayout(LayoutKind.Sequential)] + struct iovec + { + + public IntPtr iov_base; + public int iov_len; + + } + + /// + /// Implements the native method 'read'. + /// + /// + /// + /// + /// + /// + public static unsafe int read(object self, object fd, long address, int len) { #if FIRST_PASS - throw new NotSupportedException(); + throw new NotImplementedException(); #else - global::java.nio.ByteBuffer[] altBufs = null; - var list = new List>(length); - for (int i = 0; i < length; i++) + if (len == 0) + return 0; + + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try { - var bb = bufs[i + offset]; - if (!bb.hasArray()) +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try { - if (altBufs == null) - { - altBufs = new global::java.nio.ByteBuffer[bufs.Length]; - } - bb = altBufs[i + offset] = global::java.nio.ByteBuffer.allocate(bb.remaining()); + var n = socket.Receive(buf, 0, len, SocketFlags.None); + if (n == 0) + return global::sun.nio.ch.IOStatus.EOF; + + Marshal.Copy(buf, 0, (IntPtr)address, n); + return n; + } + finally + { + ArrayPool.Shared.Return(buf); } - list.Add(new ArraySegment(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining())); +#else + var n = socket.Receive(new Span((byte*)(IntPtr)address, len)); + if (n == 0) + return global::sun.nio.ch.IOStatus.EOF; + + return n; +#endif } - int count; - try + catch (SocketException e) when (e.SocketErrorCode == SocketError.Shutdown) + { + return global::sun.nio.ch.IOStatus.EOF; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) { - count = fd.getSocket().Receive(list); + throw new global::java.net.SocketException("Socket closed."); } - catch (SocketException x) + catch (Exception e) { - if (x.SocketErrorCode == SocketError.WouldBlock) + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'readv'. + /// + /// + /// + /// + /// + /// + public static unsafe long readv(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + if (len == 0) + return 0; + + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try + { + // allocate list of array segments to hold received data + var vecs = new Span((byte*)(IntPtr)address, len); + var bufs = new ArraySegment[vecs.Length]; + + try { - count = 0; + // prepare array segments for buffers + for (int i = 0; i < vecs.Length; i++) + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(vecs[i].iov_len), 0, vecs[i].iov_len); + + // receive into segments + var length = socket.Receive(bufs); + + // copy segments back into vectors + var l = length; + for (int i = 0; i < vecs.Length; i++) + { + var szz = Math.Min(l, vecs[i].iov_len); + Marshal.Copy(bufs[i].Array, 0, vecs[i].iov_base, szz); + l -= szz; + } + + // we should have accounted for all of the bytes + if (l != 0) + throw new global::java.lang.RuntimeException("Bytes remaining after read."); + + return length; } - else + finally { - throw x.ToIOException(); + for (int i = 0; i < bufs.Length; i++) + ArrayPool.Shared.Return(bufs[i].Array); } } + catch (SocketException e) when (e.SocketErrorCode == SocketError.Shutdown) + { + return global::sun.nio.ch.IOStatus.EOF; + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) + { + throw e.ToIOException(); + } catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.net.SocketException("Socket closed."); } - int total = count; - for (int i = 0; total > 0 && i < length; i++) + catch (Exception e) + { + throw new global::java.io.IOException(e); + } +#endif + } + + /// + /// Implements the native method 'write'. + /// + /// + /// + /// + /// + /// + public static unsafe int write(object self, object fd, long address, int len) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try { - global::java.nio.ByteBuffer bb = bufs[i + offset]; - global::java.nio.ByteBuffer abb; - int consumed = Math.Min(total, bb.remaining()); - if (altBufs != null && (abb = altBufs[i + offset]) != null) +#if NETFRAMEWORK + var buf = ArrayPool.Shared.Rent(len); + + try { - abb.position(consumed); - abb.flip(); - bb.put(abb); + Marshal.Copy((IntPtr)address, buf, 0, len); + return socket.Send(buf, 0, len, SocketFlags.None); } - else + finally { - bb.position(bb.position() + consumed); + ArrayPool.Shared.Return(buf); } - total -= consumed; +#else + return socket.Send(new ReadOnlySpan((byte*)(IntPtr)address, len)); +#endif + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) + { + return global::sun.nio.ch.IOStatus.UNAVAILABLE; + } + catch (SocketException e) + { + throw e.ToIOException(); + } + catch (ObjectDisposedException) + { + throw new global::java.net.SocketException("Socket closed."); + } + catch (Exception e) + { + throw new global::java.io.IOException(e); } - return count; #endif } - public static long write(object nd, global::java.io.FileDescriptor fd, global::java.nio.ByteBuffer[] bufs, int offset, int length) + /// + /// Implements the native method 'writev'. + /// + /// + /// + /// + /// + /// + public static unsafe long writev(object self, object fd, long address, int len) { #if FIRST_PASS throw new NotSupportedException(); #else - global::java.nio.ByteBuffer[] altBufs = null; - var list = new List>(length); - for (int i = 0; i < length; i++) + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + throw new global::java.net.SocketException("Socket closed."); + + try { - var bb = bufs[i + offset]; - if (!bb.hasArray()) + // allocate list of array segments to hold incoming buffers + var vecs = new ReadOnlySpan((void*)(IntPtr)address, len); + var bufs = new ArraySegment[vecs.Length]; + + try { - if (altBufs == null) + // copy incoming buffers into segments + for (int i = 0; i < vecs.Length; i++) { - altBufs = new global::java.nio.ByteBuffer[bufs.Length]; + bufs[i] = new ArraySegment(ArrayPool.Shared.Rent(vecs[i].iov_len), 0, vecs[i].iov_len); + Marshal.Copy(vecs[i].iov_base, bufs[i].Array, bufs[i].Offset, vecs[i].iov_len); } - var abb = global::java.nio.ByteBuffer.allocate(bb.remaining()); - int pos = bb.position(); - abb.put(bb); - bb.position(pos); - abb.flip(); - bb = altBufs[i + offset] = abb; + + // send segments + return socket.Send(bufs); + } + finally + { + // return allocated arrays + for (int i = 0; i < bufs.Length; i++) + if (bufs[i].Array != null) + ArrayPool.Shared.Return(bufs[i].Array); } - list.Add(new ArraySegment(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining())); } - int count; - try + catch (SocketException e) when (e.SocketErrorCode == SocketError.WouldBlock) { - count = fd.getSocket().Send(list); + return global::sun.nio.ch.IOStatus.UNAVAILABLE; } catch (SocketException e) { - if (e.SocketErrorCode == SocketError.WouldBlock) - count = 0; - else - throw e.ToIOException(); + throw e.ToIOException(); } - catch (ObjectDisposedException e) + catch (ObjectDisposedException) { - throw new global::java.net.SocketException("Socket is closed"); + throw new global::java.net.SocketException("Socket closed."); } - int total = count; - for (int i = 0; total > 0 && i < length; i++) + catch (Exception e) { - var bb = bufs[i + offset]; - int consumed = Math.Min(total, bb.remaining()); - bb.position(bb.position() + consumed); - total -= consumed; + throw new global::java.io.IOException(e); } +#endif + } - return count; + /// + /// Implements the native method 'preClose'. + /// + /// + /// + public static void preClose(object self, object fd) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + return; + + try + { + if (socket.LingerState.Enabled == false) + if (socket.Connected) + socket.Shutdown(SocketShutdown.Send); + } + catch (ObjectDisposedException) + { + return; + } + catch (SocketException) + { + // ignore + } +#endif + } + + /// + /// Implements the native method 'close'. + /// + /// + /// + public static void close(object self, object fd) + { +#if FIRST_PASS + throw new NotSupportedException(); +#else + var socket = FileDescriptorAccessor.GetSocket(fd); + if (socket == null) + return; + + try + { + FileDescriptorAccessor.SetSocket(fd, null); + socket.Close(); + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + catch (Exception e) + { + throw new global::java.io.IOException(e); + } #endif } diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.cs deleted file mode 100644 index dbbb0c70eb..0000000000 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.cs +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; - -using IKVM.Runtime.Util.Sun.Nio.Ch; - -namespace IKVM.Java.Externs.sun.nio.ch -{ - - static class WindowsAsynchronousServerSocketChannelImpl - { - -#if !FIRST_PASS - - sealed class Accept : OperationBase - { - - protected override IAsyncResult Begin(System.Net.Sockets.Socket listenSocket, System.Net.Sockets.Socket acceptSocket, AsyncCallback callback, object state) - { - return listenSocket.BeginAccept(acceptSocket, 0, callback, state); - } - - protected override int End(System.Net.Sockets.Socket socket, IAsyncResult ar) - { - socket.EndAccept(ar); - return 0; - } - } - -#endif - - public static void initIDs() - { - - } - - public static int accept0(global::java.io.FileDescriptor listenSocket, global::java.io.FileDescriptor acceptSocket, object handler) - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - return new Accept().Do(listenSocket.getSocket(), acceptSocket.getSocket(), handler); -#endif - } - - public static void updateAcceptContext(global::java.io.FileDescriptor listenSocket, global::java.io.FileDescriptor acceptSocket) - { - - } - - public static void closesocket0(long socket) - { - - } - - } - -} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.cs deleted file mode 100644 index df6e56080a..0000000000 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.cs +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; -using System.Collections.Generic; - -using IKVM.Runtime.Util.Sun.Nio.Ch; - -namespace IKVM.Java.Externs.sun.nio.ch -{ - - static class WindowsAsynchronousSocketChannelImpl - { - -#if !FIRST_PASS - - sealed class Connect : OperationBase - { - - protected override IAsyncResult Begin(System.Net.Sockets.Socket socket, System.Net.IPEndPoint remoteEP, AsyncCallback callback, object state) - { - return socket.BeginConnect(remoteEP, callback, state); - } - - protected override int End(System.Net.Sockets.Socket socket, IAsyncResult ar) - { - socket.EndConnect(ar); - return 0; - } - } - - static List> ByteBuffersToList(global::java.nio.ByteBuffer[] bufs) - { - var list = new List>(bufs.Length); - foreach (var bb in bufs) - list.Add(new ArraySegment(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining())); - - return list; - } - - sealed class Receive : OperationBase - { - - protected override IAsyncResult Begin(System.Net.Sockets.Socket socket, global::java.nio.ByteBuffer[] bufs, AsyncCallback callback, object state) - { - return socket.BeginReceive(ByteBuffersToList(bufs), System.Net.Sockets.SocketFlags.None, callback, state); - } - - protected override int End(System.Net.Sockets.Socket socket, IAsyncResult ar) - { - return socket.EndReceive(ar); - } - - } - - sealed class Send : OperationBase - { - - protected override IAsyncResult Begin(System.Net.Sockets.Socket socket, global::java.nio.ByteBuffer[] bufs, AsyncCallback callback, object state) - { - return socket.BeginSend(ByteBuffersToList(bufs), System.Net.Sockets.SocketFlags.None, callback, state); - } - - protected override int End(System.Net.Sockets.Socket socket, IAsyncResult ar) - { - return socket.EndSend(ar); - } - - } - -#endif - - public static int connect0(global::java.io.FileDescriptor fd, bool preferIPv6, global::java.net.InetAddress remote, int remotePort, object handler) - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - return new Connect().Do(fd.getSocket(), new System.Net.IPEndPoint(global::java.net.SocketUtil.getAddressFromInetAddress(remote, preferIPv6), remotePort), handler); -#endif - } - - public static void updateConnectContext(global::java.io.FileDescriptor fd) - { - - } - - public static int read0(global::java.io.FileDescriptor fd, global::java.nio.ByteBuffer[] bufs, object handler) - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - return new Receive().Do(fd.getSocket(), bufs, handler); -#endif - } - - public static int write0(global::java.io.FileDescriptor fd, global::java.nio.ByteBuffer[] bufs, object handler) - { -#if FIRST_PASS - throw new NotSupportedException(); -#else - return new Send().Do(fd.getSocket(), bufs, handler); -#endif - } - - public static void shutdown0(long socket, int how) - { - - } - - public static void closesocket0(long socket) - { - - } - - public static void initIDs() - { - - } - - } - -} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetBasicFileAttributeView.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetBasicFileAttributeView.cs new file mode 100644 index 0000000000..97b2ce652e --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetBasicFileAttributeView.cs @@ -0,0 +1,130 @@ +using System; +using System.IO; +using System.Security; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; +using IKVM.Runtime.Vfs; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetBasicFileAttributeView'. + /// + static class DotNetBasicFileAttributeView + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + static FileTimeAccessor fileTimeAccessor; + static DotNetBasicFileAttributeViewAccessor dotNetBasicFileAttributeViewAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + + static FileTimeAccessor FileTimeAccessor => JVM.BaseAccessors.Get(ref fileTimeAccessor); + + static DotNetBasicFileAttributeViewAccessor DotNetBasicFileAttributeViewAccessor => JVM.BaseAccessors.Get(ref dotNetBasicFileAttributeViewAccessor); + +#endif + + /// + /// Converts the given to a . + /// + /// + /// + static DateTime? ToDateTime(object fileTime) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else +#if NETFRAMEWORK + var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#else + var epoch = DateTime.UnixEpoch; +#endif + return fileTime != null ? epoch + TimeSpan.FromMilliseconds(FileTimeAccessor.InvokeToMillis(fileTime)) : null; +#endif + } + + /// + /// Implements the native method 'setTimes'. + /// + /// + /// + /// + /// + /// + /// + public static void setTimes(object self, object lastModifiedTime, object lastAccessTime, object createdTime) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var path = DotNetBasicFileAttributeViewAccessor.GetPath(self); + if (path == null) + throw new global::java.lang.NullPointerException(); + + var sm = SystemAccessor.InvokeGetSecurityManager(); + if (sm != null) + SecurityManagerAccessor.InvokeCheckWrite(sm, path); + + if (VfsTable.Default.IsPath(path)) + throw new global::java.io.IOException("Cannot modify VFS entries."); + + try + { + if (File.Exists(path)) + { + if (lastModifiedTime != null) + File.SetLastWriteTimeUtc(path, (DateTime)ToDateTime(lastModifiedTime)); + if (lastAccessTime != null) + File.SetLastAccessTimeUtc(path, (DateTime)ToDateTime(lastAccessTime)); + if (createdTime != null) + File.SetCreationTimeUtc(path, (DateTime)ToDateTime(createdTime)); + } + else if (Directory.Exists(path)) + { + if (lastModifiedTime != null) + Directory.SetLastWriteTimeUtc(path, (DateTime)ToDateTime(lastModifiedTime)); + if (lastAccessTime != null) + Directory.SetLastAccessTimeUtc(path, (DateTime)ToDateTime(lastAccessTime)); + if (createdTime != null) + Directory.SetCreationTimeUtc(path, (DateTime)ToDateTime(createdTime)); + } + else + { + throw new global::java.nio.file.NoSuchFileException(path); + } + } + catch (ArgumentException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (SecurityException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (UnauthorizedAccessException e) + { + throw new global::java.io.IOException(e.Message); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs new file mode 100644 index 0000000000..1f6fc0aad1 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDirectoryStream.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Linq; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Ikvm.Util; +using IKVM.Runtime.Accessors.Java.Nio.File; +using IKVM.Runtime.Accessors.Sun.Nio.Fs; + + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetDirectoryStream'. + /// + static class DotNetDirectoryStream + { + +#if FIRST_PASS == false + + static EnumeratorIteratorAccessor enumeratorIteratorAccessor; + static DirectoryStreamFilterAccessor directoryStreamFilterAccessor; + static DotNetPathAccessor dotNetPathAccessor; + static DotNetDirectoryStreamAccessor dotNetDirectoryStreamAccessor; + + static EnumeratorIteratorAccessor EnumeratorIteratorAccessor => JVM.BaseAccessors.Get(ref enumeratorIteratorAccessor); + + static DirectoryStreamFilterAccessor DirectoryStreamFilterAccessor => JVM.BaseAccessors.Get(ref directoryStreamFilterAccessor); + + static DotNetPathAccessor DotNetPathAccessor => JVM.BaseAccessors.Get(ref dotNetPathAccessor); + + static DotNetDirectoryStreamAccessor DotNetDirectoryStreamAccessor => JVM.BaseAccessors.Get(ref dotNetDirectoryStreamAccessor); + +#endif + + /// + /// Implements the native method 'iterator'. + /// + /// + /// + public static object iterator(object self) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var path = DotNetDirectoryStreamAccessor.GetPath(self); + if (path == null) + throw new global::java.lang.NullPointerException(); + + var fileSystem = DotNetPathAccessor.GetFs(path); + if (fileSystem == null) + throw new global::java.lang.NullPointerException(); + + var directoryPath = DotNetPathAccessor.GetPath(path); + if (directoryPath == null) + throw new global::java.lang.NullPointerException(); + + var files = DotNetDirectoryStreamAccessor.GetFiles(self); + if (files == null) + throw new global::java.lang.NullPointerException(); + + var filter = DotNetDirectoryStreamAccessor.GetFilter(self); + if (filter == null) + throw new global::java.lang.NullPointerException(); + + try + { + bool ApplyFilter(object i) + { + try + { + return DirectoryStreamFilterAccessor.InvokeAccept(filter, i); + } + catch (global::java.io.IOException e) + { + throw new global::java.nio.file.DirectoryIteratorException(e); + } + } + + return EnumeratorIteratorAccessor.Init(files.Select(i => DotNetPathAccessor.Init(fileSystem, Path.Combine(directoryPath, i))).Where(ApplyFilter).GetEnumerator()); + } + catch (global::java.lang.Throwable) + { + throw; + } + catch (Exception e) + { + if (File.Exists(directoryPath)) + throw new global::java.nio.file.NotDirectoryException(directoryPath); + if (Directory.Exists(directoryPath) == false) + throw new global::java.nio.file.NotDirectoryException(directoryPath); + + throw new global::java.io.IOException(e.Message); + } +#endif + } + + /// + /// Implements the native method 'close'. + /// + /// + public static void close(object self) + { + + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributeView.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributeView.cs new file mode 100644 index 0000000000..76b7b1b150 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributeView.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Vfs; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetDosFileAttributeView'. + /// + static class DotNetDosFileAttributeView + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + +#endif + + /// + /// Implements the native method 'setAttribute0'. + /// + /// + /// + /// + public static void setAttribute0(string path, int attr, bool value) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var sm = SystemAccessor.InvokeGetSecurityManager(); + if (sm != null) + SecurityManagerAccessor.InvokeCheckWrite(sm, path); + + if (VfsTable.Default.IsPath(path)) + throw new global::java.io.IOException("VFS entries cannot be modified."); + + try + { + var info = new FileInfo(path); + + if (value) + info.Attributes |= (FileAttributes)attr; + else + info.Attributes &= ~(FileAttributes)attr; + } + catch (FileNotFoundException e) + { + throw new global::java.nio.file.NoSuchFileException(path); + } + catch (ArgumentException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributes.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributes.cs new file mode 100644 index 0000000000..94db52ec56 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetDosFileAttributes.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Security; + +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Accessors.Sun.Nio.Ch; +using IKVM.Runtime.Vfs; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetDosFileAttributes'. + /// + static class DotNetDosFileAttributes + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + static FileTimeAccessor fileTimeAccessor; + static DotNetDosFileAttributesAccessor dotNetDosFileAttributesAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static FileTimeAccessor FileTimeAccessor => JVM.BaseAccessors.Get(ref fileTimeAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + + static DotNetDosFileAttributesAccessor DotNetDosFileAttributesAccessor => JVM.BaseAccessors.Get(ref dotNetDosFileAttributesAccessor); + +#endif + + /// + /// Creates a object from a . + /// + /// + /// + static object ToFileTime(DateTime? dateTime) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else +#if NETFRAMEWORK + var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#else + var epoch = DateTime.UnixEpoch; +#endif + return dateTime != null ? FileTimeAccessor.InvokeFromMillis((long)((DateTime)dateTime - epoch).TotalMilliseconds) : null; +#endif + } + + /// + /// Implements the native method 'read'. + /// + /// + /// + /// + /// + public static object read(string path) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var sm = SystemAccessor.InvokeGetSecurityManager(); + if (sm != null) + SecurityManagerAccessor.InvokeCheckRead(sm, path); + + if (VfsTable.Default.IsPath(path)) + { + var entry = VfsTable.Default.GetEntry(path); + + if (entry is VfsFile vfsFile) + return DotNetDosFileAttributesAccessor.Init( + FileTimeAccessor.InvokeFromMillis(0), + FileTimeAccessor.InvokeFromMillis(0), + FileTimeAccessor.InvokeFromMillis(0), + null, + false, + false, + true, + false, + vfsFile.Size, + true, + false, + false, + false); + + if (entry is VfsDirectory vfsDirectory) + return DotNetDosFileAttributesAccessor.Init( + FileTimeAccessor.InvokeFromMillis(0), + FileTimeAccessor.InvokeFromMillis(0), + FileTimeAccessor.InvokeFromMillis(0), + null, + true, + false, + false, + false, + 0, + false, + false, + false, + false); + + throw new global::java.nio.file.NoSuchFileException(path); + } + + try + { + var fileInfo = new FileInfo(path); + if (fileInfo.Exists) + return DotNetDosFileAttributesAccessor.Init( + ToFileTime(fileInfo.CreationTimeUtc), + ToFileTime(fileInfo.LastAccessTimeUtc), + ToFileTime(fileInfo.LastWriteTimeUtc), + null, + false, + false, + true, + false, + fileInfo.Length, + fileInfo.IsReadOnly, + (fileInfo.Attributes & FileAttributes.Hidden) != 0, + (fileInfo.Attributes & FileAttributes.Archive) != 0, + (fileInfo.Attributes & FileAttributes.System) != 0); + + var directoryInfo = new DirectoryInfo(path); + if (directoryInfo.Exists) + return DotNetDosFileAttributesAccessor.Init( + ToFileTime(directoryInfo.CreationTimeUtc), + ToFileTime(directoryInfo.LastAccessTimeUtc), + ToFileTime(directoryInfo.LastWriteTimeUtc), + null, + true, + false, + false, + false, + 0, + (directoryInfo.Attributes & FileAttributes.ReadOnly) != 0, + (directoryInfo.Attributes & FileAttributes.Hidden) != 0, + (directoryInfo.Attributes & FileAttributes.Archive) != 0, + (directoryInfo.Attributes & FileAttributes.System) != 0); + + throw new global::java.nio.file.NoSuchFileException(path); + } + catch (ArgumentException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (IOException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (NotSupportedException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (SecurityException e) + { + throw new global::java.io.IOException(e.Message); + } + catch (UnauthorizedAccessException e) + { + throw new global::java.io.IOException(e.Message); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs new file mode 100644 index 0000000000..19fb4482a4 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetFileSystemProvider.cs @@ -0,0 +1,262 @@ +using System; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Security; +using System.Security.AccessControl; + +using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Accessors.Sun.Nio.Fs; +using IKVM.Runtime.Vfs; + +using Microsoft.Win32.SafeHandles; + +using Mono.Unix; +using Mono.Unix.Native; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetFileSystemProvider'. + /// + static class DotNetFileSystemProvider + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + static SecurityManagerAccessor securityManagerAccessor; + static FileDescriptorAccessor fileDescriptorAccessor; + static DotNetPathAccessor dotNetPathAccessor; + static DotNetDirectoryStreamAccessor dotNetDirectoryStreamAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static SecurityManagerAccessor SecurityManagerAccessor => JVM.BaseAccessors.Get(ref securityManagerAccessor); + + static FileDescriptorAccessor FileDescriptorAccessor => JVM.BaseAccessors.Get(ref fileDescriptorAccessor); + + static DotNetPathAccessor DotNetPathAccessor => JVM.BaseAccessors.Get(ref dotNetPathAccessor); + + static DotNetDirectoryStreamAccessor DotNetDirectoryStreamAccessor => JVM.BaseAccessors.Get(ref dotNetDirectoryStreamAccessor); + +#if NETCOREAPP + + static readonly PropertyInfo SafeFileHandleIsAsyncProperty = typeof(SafeFileHandle).GetProperty("IsAsync", BindingFlags.Public | BindingFlags.Instance); + static readonly Action SafeFileHandleIsAsyncPropertySetter = SafeFileHandleIsAsyncProperty != null ? (o, v) => SafeFileHandleIsAsyncProperty.SetValue(o, v) : null; + +#endif + +#endif + + /// + /// Implements the native method 'open0'. + /// + /// + /// + /// + /// + /// + /// + /// + public static object open0(string path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, object sm) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (sm != null) + { + if ((rights & FileSystemRights.Read) != 0) + SecurityManagerAccessor.InvokeCheckRead(sm, path); + if ((rights & FileSystemRights.Write) != 0) + SecurityManagerAccessor.InvokeCheckWrite(sm, path); + if ((rights & FileSystemRights.AppendData) != 0) + SecurityManagerAccessor.InvokeCheckWrite(sm, path); + if ((options & FileOptions.DeleteOnClose) != 0) + SecurityManagerAccessor.InvokeCheckDelete(sm, path); + } + + var access = (FileAccess)0; + if ((rights & FileSystemRights.Read) != 0) + access |= FileAccess.Read; + if ((rights & FileSystemRights.Write) != 0) + access |= FileAccess.Write; + if ((rights & FileSystemRights.AppendData) != 0) + access |= FileAccess.Write; + if (access == 0) + access = FileAccess.ReadWrite; + + try + { + if (VfsTable.Default.IsPath(path)) + { + if (VfsTable.Default.GetEntry(path) is VfsFile vfsFile) + return FileDescriptorAccessor.FromStream(vfsFile.Open(mode, access)); + + throw new global::java.lang.UnsupportedOperationException(); + } + +#if NETFRAMEWORK + return FileDescriptorAccessor.FromStream(new FileStream(path, mode, rights, share, bufferSize, options)); +#else + if (RuntimeUtil.IsWindows) + { + return FileDescriptorAccessor.FromStream(new FileStream(path, mode, access, share, bufferSize, options)); + } + else + { + var flags = (OpenFlags)0; + if (mode == FileMode.Create) + flags |= OpenFlags.O_CREAT | OpenFlags.O_TRUNC; + if (mode == FileMode.Open) + flags |= OpenFlags.O_EXCL; + if (mode == FileMode.OpenOrCreate) + flags |= OpenFlags.O_CREAT; + if (mode == FileMode.Append) + flags |= OpenFlags.O_APPEND; + if (mode == FileMode.CreateNew) + flags |= OpenFlags.O_CREAT | OpenFlags.O_EXCL; + + if ((rights & FileSystemRights.Read) != 0 && (rights & FileSystemRights.Write) != 0) + flags |= OpenFlags.O_RDWR; + if ((rights & FileSystemRights.Read) != 0 && (rights & FileSystemRights.Write) == 0) + flags |= OpenFlags.O_RDONLY; + if ((rights & FileSystemRights.Read) == 0 && (rights & FileSystemRights.Write) != 0) + flags |= OpenFlags.O_WRONLY; + + if ((options & FileOptions.Asynchronous) != 0) + flags |= OpenFlags.O_ASYNC; + if ((options & FileOptions.WriteThrough) != 0) + flags |= OpenFlags.O_SYNC; + + var r = Syscall.open(path, flags, FilePermissions.DEFFILEMODE); + if (r == -1) + { + var error = Stdlib.GetLastError(); + if (error == Errno.EACCES) + throw new global::java.nio.file.AccessDeniedException(path); + if (error == Errno.EEXIST) + throw new global::java.nio.file.FileAlreadyExistsException(path); + if (error == Errno.ENOENT) + throw new global::java.nio.file.NoSuchFileException(path); + if (error == Errno.ENOTDIR) + throw new global::java.nio.file.NoSuchFileException(path); + if (error == Errno.EROFS) + throw new global::java.nio.file.FileAlreadyExistsException(path); + + throw new UnixIOException(error); + } + + var h = new SafeFileHandle((IntPtr)r, true); + + // .NET Core 5+ maintains an IsAsync property validated by FileStream + // this property isn't set properly on Unix + // https://github.com/dotnet/runtime/issues/85560 + if ((options & FileOptions.Asynchronous) != 0) + SafeFileHandleIsAsyncPropertySetter?.Invoke(h, true); + + return FileDescriptorAccessor.FromStream(new FileStream(h, access, bufferSize, (options & FileOptions.Asynchronous) != 0)); + } +#endif + } + catch (ArgumentException e) + { + throw new global::java.nio.file.FileSystemException(path, null, e.Message); + } + catch (FileNotFoundException) + { + throw new global::java.nio.file.NoSuchFileException(path); + } + catch (DirectoryNotFoundException) + { + throw new global::java.nio.file.NoSuchFileException(path); + } + catch (PlatformNotSupportedException e) + { + throw new global::java.lang.UnsupportedOperationException(e.Message); + } + catch (IOException) when (mode == FileMode.CreateNew && File.Exists(path)) + { + throw new global::java.nio.file.FileAlreadyExistsException(path); + } + catch (IOException e) + { + throw new global::java.nio.file.FileSystemException(path, null, e.Message); + } + catch (SecurityException) + { + throw new global::java.nio.file.AccessDeniedException(path); + } + catch (UnauthorizedAccessException) + { + throw new global::java.nio.file.AccessDeniedException(path); + } +#endif + } + + /// + /// Implements the native method 'newDirectoryStream'. + /// + /// + /// + /// + /// + public static object newDirectoryStream(object self, object dir, object filter) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (dir == null) + throw new global::java.lang.NullPointerException(); + if (filter == null) + throw new global::java.lang.NullPointerException(); + + var path = DotNetPathAccessor.GetPath(dir); + if (path == null) + throw new global::java.lang.NullPointerException(); + + var sm = SystemAccessor.InvokeGetSecurityManager(); + if (sm != null) + SecurityManagerAccessor.InvokeCheckRead(sm, path); + + try + { + if (VfsTable.Default.IsPath(path)) + { + if (VfsTable.Default.GetEntry(path) is not VfsDirectory vfsDirectory) + throw new global::java.nio.file.NotDirectoryException(path); + + return DotNetDirectoryStreamAccessor.Init(dir, vfsDirectory.List(), filter); + } + + if (File.Exists(path)) + throw new global::java.nio.file.NotDirectoryException(path); + + if (Directory.Exists(path) == false) + throw new global::java.nio.file.NotDirectoryException(path); + + return DotNetDirectoryStreamAccessor.Init(dir, Directory.EnumerateFileSystemEntries(path), filter); + } + catch (global::java.lang.Throwable) + { + throw; + } + catch (Exception) when (File.Exists(path)) + { + throw new global::java.nio.file.NotDirectoryException(path); + } + catch (Exception e) + { + throw new global::java.io.IOException(e.Message); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/NetPath.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetPath.cs similarity index 96% rename from src/IKVM.Runtime/Java/Externs/sun/nio/fs/NetPath.cs rename to src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetPath.cs index d5d27bbcf1..a88ae0eb05 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/NetPath.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetPath.cs @@ -31,7 +31,7 @@ Jeroen Frijters namespace IKVM.Java.Externs.sun.nio.fs { - static class NetPath + static class DotNetPath { public static string toRealPathImpl(string path) @@ -42,7 +42,7 @@ public static string toRealPathImpl(string path) path = global::java.io.DefaultFileSystem.getFileSystem().canonicalize(path); if (VfsTable.Default.IsPath(path)) { - if (VfsTable.Default.GetPath(path) is VfsFile file) + if (VfsTable.Default.GetEntry(path) is VfsFile file) { if (file.CanOpen(FileMode.Open, FileAccess.Read) == false) throw new global::java.nio.file.AccessDeniedException(path); diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetUnixUriUtils.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetUnixUriUtils.cs new file mode 100644 index 0000000000..94d9dac83e --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetUnixUriUtils.cs @@ -0,0 +1,24 @@ +using IKVM.Runtime.Vfs; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetUnixUriUtils'. + /// + static class DotNetUnixUriUtils + { + + /// + /// Implements the native method 'isVfsDirectory'. + /// + /// + /// + public static bool isVfsDirectory(string path) + { + return VfsTable.Default.GetEntry(path) is VfsDirectory; + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetWindowsUriSupport.cs b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetWindowsUriSupport.cs new file mode 100644 index 0000000000..09f6b96c33 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/nio/fs/DotNetWindowsUriSupport.cs @@ -0,0 +1,24 @@ +using IKVM.Runtime.Vfs; + +namespace IKVM.Java.Externs.sun.nio.fs +{ + + /// + /// Implements the native methods for 'DotNetWindowsUriSupport'. + /// + static class DotNetWindowsUriSupport + { + + /// + /// Implements the native method 'isVfsDirectory'. + /// + /// + /// + public static bool isVfsDirectory(string path) + { + return VfsTable.Default.GetEntry(path) is VfsDirectory; + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs b/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs index b582fccbc2..b355a3246b 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/reflect/Reflection.cs @@ -23,7 +23,6 @@ Jeroen Frijters */ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; @@ -34,93 +33,57 @@ Jeroen Frijters namespace IKVM.Java.Externs.sun.reflect { + /// + /// Provides the implementations of the native methods in . + /// static class Reflection { -#if CLASSGC - - sealed class State - { - internal HideFromJavaFlags Value; - internal volatile bool HasValue; - } - - private static readonly ConditionalWeakTable isHideFromJavaCache = new ConditionalWeakTable(); - - internal static HideFromJavaFlags GetHideFromJavaFlags(MethodBase mb) - { - if (mb.Name.StartsWith("__<", StringComparison.Ordinal)) - { - return HideFromJavaFlags.All; - } - State state = isHideFromJavaCache.GetValue(mb, delegate { return new State(); }); - if (!state.HasValue) - { - state.Value = AttributeHelper.GetHideFromJavaFlags(mb); - state.HasValue = true; - } - return state.Value; - } - -#else - - private static readonly Dictionary isHideFromJavaCache = new Dictionary(); + static readonly ConditionalWeakTable> hideFromJavaFlagCache = new ConditionalWeakTable>(); + /// + /// Gets the that should be considered applied to the given method. + /// + /// + /// internal static HideFromJavaFlags GetHideFromJavaFlags(MethodBase mb) { - if (mb.Name.StartsWith("__<", StringComparison.Ordinal)) - { - return HideFromJavaFlags.All; - } - RuntimeMethodHandle handle; - try - { - handle = mb.MethodHandle; - } - catch (InvalidOperationException) - { - // DynamicMethods don't have a RuntimeMethodHandle and we always want to hide them anyway - return HideFromJavaFlags.All; - } - catch (NotSupportedException) - { - // DynamicMethods don't have a RuntimeMethodHandle and we always want to hide them anyway - return HideFromJavaFlags.All; - } - lock (isHideFromJavaCache) - { - HideFromJavaFlags cached; - if (isHideFromJavaCache.TryGetValue(handle, out cached)) - { - return cached; - } - } - HideFromJavaFlags flags = AttributeHelper.GetHideFromJavaFlags(mb); - lock (isHideFromJavaCache) - { - isHideFromJavaCache[handle] = flags; - } - return flags; + return hideFromJavaFlagCache.GetValue(mb, _ => new Lazy(() => GetHideFromJavaFlagsImpl(_), true)).Value; + } + + /// + /// Gets the that should be considered applied to the given method. + /// + /// + /// + static HideFromJavaFlags GetHideFromJavaFlagsImpl(MethodBase mb) + { + return mb.Name.StartsWith("__<", StringComparison.Ordinal) ? HideFromJavaFlags.All : AttributeHelper.GetHideFromJavaFlags(mb); } -#endif + /// + /// Returns true if the given method should not be considered in walks of the stack from the point of view of Java. + /// + /// + /// internal static bool IsHideFromStackWalk(MethodBase mb) { - Type type = mb.DeclaringType; - return type == null - || type.Assembly == typeof(object).Assembly - || type.Assembly == typeof(Reflection).Assembly - || type.Assembly == IKVM.Java.Externs.java.lang.SecurityManager.jniAssembly - || type == typeof(global::java.lang.reflect.Method) - || type == typeof(global::java.lang.reflect.Constructor) - || (GetHideFromJavaFlags(mb) & HideFromJavaFlags.StackWalk) != 0 - ; + var type = mb.DeclaringType; + if (type == null || + type.Assembly == typeof(object).Assembly || + type.Assembly == typeof(Reflection).Assembly || + type == typeof(global::java.lang.reflect.Method) || + type == typeof(global::java.lang.reflect.Constructor) || + (GetHideFromJavaFlags(mb) & HideFromJavaFlags.StackWalk) != 0) + return true; + + return false; } public static global::java.lang.Class getCallerClass() { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else throw new global::java.lang.InternalError("CallerSensitive annotation expected at frame 1"); #endif @@ -131,33 +94,27 @@ internal static bool IsHideFromStackWalk(MethodBase mb) public static global::java.lang.Class getCallerClass(int realFramesToSkip) { #if FIRST_PASS - return null; + throw new NotImplementedException(); #else if (realFramesToSkip <= 0) - { return global::ikvm.@internal.ClassLiteral.Value; - } + for (int i = 2; ;) { - MethodBase method = new StackFrame(i++, false).GetMethod(); + var method = new StackFrame(i++, false).GetMethod(); if (method == null) - { return null; - } + if (IsHideFromStackWalk(method)) - { continue; - } + // HACK we skip HideFromJavaFlags.StackTrace too because we want to skip the LambdaForm methods // that are used by late binding if ((GetHideFromJavaFlags(method) & HideFromJavaFlags.StackTrace) != 0) - { continue; - } + if (--realFramesToSkip == 0) - { return ClassLoaderWrapper.GetWrapperFromType(method.DeclaringType).ClassObject; - } } #endif } @@ -173,8 +130,8 @@ public static int getClassAccessFlags(global::java.lang.Class clazz) public static bool checkInternalAccess(global::java.lang.Class currentClass, global::java.lang.Class memberClass) { - TypeWrapper current = TypeWrapper.FromClass(currentClass); - TypeWrapper member = TypeWrapper.FromClass(memberClass); + var current = TypeWrapper.FromClass(currentClass); + var member = TypeWrapper.FromClass(memberClass); return member.IsInternal && member.InternalsVisibleTo(current); } diff --git a/src/IKVM.Runtime/Java/Externs/sun/reflect/ReflectionFactory.cs b/src/IKVM.Runtime/Java/Externs/sun/reflect/ReflectionFactory.cs index fd12759383..ce8aaf7aab 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/reflect/ReflectionFactory.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/reflect/ReflectionFactory.cs @@ -22,6 +22,8 @@ Jeroen Frijters */ using System; +using System.Linq; +using System.Linq.Expressions; using System.Reflection; #if !NO_REF_EMIT using System.Reflection.Emit; @@ -30,2063 +32,2124 @@ Jeroen Frijters using System.Security; using IKVM.Internal; +using IKVM.Runtime; +using IKVM.Runtime.Accessors.Java.Io; +using IKVM.Runtime.Accessors.Java.Lang; +using IKVM.Runtime.Extensions; +using IKVM.Runtime.Util.Java.Security; namespace IKVM.Java.Externs.sun.reflect { static class ReflectionFactory - { + { + +#if FIRST_PASS == false + + static SystemAccessor systemAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + +#endif #if !FIRST_PASS - private static object ConvertPrimitive(TypeWrapper tw, object value) - { - if (tw == PrimitiveTypeWrapper.BOOLEAN) - { - if (value is global::java.lang.Boolean) - { - return ((global::java.lang.Boolean)value).booleanValue(); - } - } - else if (tw == PrimitiveTypeWrapper.BYTE) - { - if (value is global::java.lang.Byte) - { - return ((global::java.lang.Byte)value).byteValue(); - } - } - else if (tw == PrimitiveTypeWrapper.CHAR) - { - if (value is global::java.lang.Character) - { - return ((global::java.lang.Character)value).charValue(); - } - } - else if (tw == PrimitiveTypeWrapper.SHORT) - { - if (value is global::java.lang.Short || value is global::java.lang.Byte) - { - return ((global::java.lang.Number)value).shortValue(); - } - } - else if (tw == PrimitiveTypeWrapper.INT) - { - if (value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) - { - return ((global::java.lang.Number)value).intValue(); - } - else if (value is global::java.lang.Character) - { - return (int)((global::java.lang.Character)value).charValue(); - } - } - else if (tw == PrimitiveTypeWrapper.LONG) - { - if (value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) - { - return ((global::java.lang.Number)value).longValue(); - } - else if (value is global::java.lang.Character) - { - return (long)((global::java.lang.Character)value).charValue(); - } - } - else if (tw == PrimitiveTypeWrapper.FLOAT) - { - if (value is global::java.lang.Float || value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) - { - return ((global::java.lang.Number)value).floatValue(); - } - else if (value is global::java.lang.Character) - { - return (float)((global::java.lang.Character)value).charValue(); - } - } - else if (tw == PrimitiveTypeWrapper.DOUBLE) - { - if (value is global::java.lang.Double || value is global::java.lang.Float || value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) - { - return ((global::java.lang.Number)value).doubleValue(); - } - else if (value is global::java.lang.Character) - { - return (double)((global::java.lang.Character)value).charValue(); - } - } - throw new global::java.lang.IllegalArgumentException(); - } - - private static object[] ConvertArgs(ClassLoaderWrapper loader, TypeWrapper[] argumentTypes, object[] args) - { - object[] nargs = new object[args == null ? 0 : args.Length]; - if (nargs.Length != argumentTypes.Length) - { - throw new global::java.lang.IllegalArgumentException("wrong number of arguments"); - } - for (int i = 0; i < nargs.Length; i++) - { - if (argumentTypes[i].IsPrimitive) - { - nargs[i] = ConvertPrimitive(argumentTypes[i], args[i]); - } - else - { - if (args[i] != null && !argumentTypes[i].EnsureLoadable(loader).IsInstance(args[i])) - { - throw new global::java.lang.IllegalArgumentException(); - } - nargs[i] = argumentTypes[i].GhostWrap(args[i]); - } - } - return nargs; - } - - private sealed class MethodAccessorImpl : global::sun.reflect.MethodAccessor - { - private readonly MethodWrapper mw; - - internal MethodAccessorImpl(MethodWrapper mw) - { - this.mw = mw; - mw.Link(); - mw.ResolveMethod(); - } - - [IKVM.Attributes.HideFromJava] - public object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) - { - if (!mw.IsStatic && !mw.DeclaringType.IsInstance(obj)) - { - if (obj == null) - { - throw new global::java.lang.NullPointerException(); - } - throw new global::java.lang.IllegalArgumentException("object is not an instance of declaring class"); - } - args = ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args); - // if the method is an interface method, we must explicitly run , - // because .NET reflection doesn't - if (mw.DeclaringType.IsInterface) - { - mw.DeclaringType.RunClassInit(); - } - if (mw.HasCallerID) - { - args = ArrayUtil.Concat(args, callerID); - } - object retval; - try - { - retval = mw.Invoke(obj, args); - } - catch (Exception x) - { - throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(x)); - } - if (mw.ReturnType.IsPrimitive && mw.ReturnType != PrimitiveTypeWrapper.VOID) - { - retval = JVM.Box(retval); - } - else - { - retval = mw.ReturnType.GhostUnwrap(retval); - } - return retval; - } - } - - private sealed class ConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor - { - private readonly MethodWrapper mw; - - internal ConstructorAccessorImpl(MethodWrapper mw) - { - this.mw = mw; - mw.Link(); - mw.ResolveMethod(); - } - - [IKVM.Attributes.HideFromJava] - public object newInstance(object[] args) - { - args = ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args); - try - { - return mw.CreateInstance(args); - } - catch (Exception x) - { - throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(x)); - } - } - } - - private sealed class SerializationConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor - { - private readonly MethodWrapper mw; - private readonly Type type; - - internal SerializationConstructorAccessorImpl(global::java.lang.reflect.Constructor constructorToCall, global::java.lang.Class classToInstantiate) - { - this.type = TypeWrapper.FromClass(classToInstantiate).TypeAsBaseType; - MethodWrapper mw = MethodWrapper.FromExecutable(constructorToCall); - if (mw.DeclaringType != CoreClasses.java.lang.Object.Wrapper) - { - this.mw = mw; - mw.Link(); - mw.ResolveMethod(); - } - } - - [IKVM.Attributes.HideFromJava] - [SecuritySafeCritical] - public object newInstance(object[] args) - { - object obj = FormatterServices.GetUninitializedObject(type); - if (mw != null) - { - mw.Invoke(obj, ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args)); - } - return obj; - } - } + static object ConvertPrimitive(TypeWrapper tw, object value) + { + if (tw == PrimitiveTypeWrapper.BOOLEAN) + { + if (value is global::java.lang.Boolean boolean) + return boolean.booleanValue(); + } + else if (tw == PrimitiveTypeWrapper.BYTE) + { + if (value is global::java.lang.Byte @byte) + return @byte.byteValue(); + } + else if (tw == PrimitiveTypeWrapper.CHAR) + { + if (value is global::java.lang.Character character) + return character.charValue(); + } + else if (tw == PrimitiveTypeWrapper.SHORT) + { + if (value is global::java.lang.Short || value is global::java.lang.Byte) + { + return ((global::java.lang.Number)value).shortValue(); + } + } + else if (tw == PrimitiveTypeWrapper.INT) + { + if (value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) + { + return ((global::java.lang.Number)value).intValue(); + } + else if (value is global::java.lang.Character) + { + return (int)((global::java.lang.Character)value).charValue(); + } + } + else if (tw == PrimitiveTypeWrapper.LONG) + { + if (value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) + { + return ((global::java.lang.Number)value).longValue(); + } + else if (value is global::java.lang.Character) + { + return (long)((global::java.lang.Character)value).charValue(); + } + } + else if (tw == PrimitiveTypeWrapper.FLOAT) + { + if (value is global::java.lang.Float || value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) + { + return ((global::java.lang.Number)value).floatValue(); + } + else if (value is global::java.lang.Character) + { + return (float)((global::java.lang.Character)value).charValue(); + } + } + else if (tw == PrimitiveTypeWrapper.DOUBLE) + { + if (value is global::java.lang.Double || value is global::java.lang.Float || value is global::java.lang.Long || value is global::java.lang.Integer || value is global::java.lang.Short || value is global::java.lang.Byte) + { + return ((global::java.lang.Number)value).doubleValue(); + } + else if (value is global::java.lang.Character) + { + return (double)((global::java.lang.Character)value).charValue(); + } + } + throw new global::java.lang.IllegalArgumentException(); + } + + static object[] ConvertArgs(ClassLoaderWrapper loader, TypeWrapper[] argumentTypes, object[] args) + { + var nargs = new object[args == null ? 0 : args.Length]; + if (nargs.Length != argumentTypes.Length) + throw new global::java.lang.IllegalArgumentException("wrong number of arguments"); + + for (int i = 0; i < nargs.Length; i++) + { + if (argumentTypes[i].IsPrimitive) + { + nargs[i] = ConvertPrimitive(argumentTypes[i], args[i]); + } + else + { + if (args[i] != null && !argumentTypes[i].EnsureLoadable(loader).IsInstance(args[i])) + throw new global::java.lang.IllegalArgumentException(); + + nargs[i] = argumentTypes[i].GhostWrap(args[i]); + } + } + + return nargs; + } + + sealed class MethodAccessorImpl : global::sun.reflect.MethodAccessor + { + + readonly MethodWrapper mw; + + /// + /// Initializes a new instance. + /// + /// + internal MethodAccessorImpl(MethodWrapper mw) + { + this.mw = mw ?? throw new ArgumentNullException(nameof(mw)); + mw.Link(); + mw.ResolveMethod(); + } + + [IKVM.Attributes.HideFromJava] + public object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) + { + if (!mw.IsStatic && !mw.DeclaringType.IsInstance(obj)) + { + if (obj == null) + throw new global::java.lang.NullPointerException(); + + throw new global::java.lang.IllegalArgumentException("object is not an instance of declaring class"); + } + + args = ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args); + + // if the method is an interface method, we must explicitly run , + // because .NET reflection doesn't + if (mw.DeclaringType.IsInterface) + mw.DeclaringType.RunClassInit(); + + if (mw.HasCallerID) + args = ArrayUtil.Concat(args, callerID); + + object retval; + try + { + retval = mw.Invoke(obj, args); + } + catch (Exception e) + { + throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(e)); + } + + if (mw.ReturnType.IsPrimitive && mw.ReturnType != PrimitiveTypeWrapper.VOID) + retval = JVM.Box(retval); + else + retval = mw.ReturnType.GhostUnwrap(retval); + + return retval; + } + } + + sealed class ConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor + { + + readonly MethodWrapper mw; + + /// + /// Initializes a new instance. + /// + /// + internal ConstructorAccessorImpl(MethodWrapper mw) + { + this.mw = mw; + mw.Link(); + mw.ResolveMethod(); + } + + [IKVM.Attributes.HideFromJava] + public object newInstance(object[] args) + { + args = ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args); + try + { + return mw.CreateInstance(args); + } + catch (Exception x) + { + throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(x)); + } + } + } + + sealed class SerializationConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor + { + + readonly MethodWrapper mw; + readonly Type type; + + /// + /// Initializes a new instance. + /// + /// + /// + internal SerializationConstructorAccessorImpl(global::java.lang.reflect.Constructor constructorToCall, global::java.lang.Class classToInstantiate) + { + this.type = TypeWrapper.FromClass(classToInstantiate).TypeAsBaseType; + var mw = MethodWrapper.FromExecutable(constructorToCall); + if (mw.DeclaringType != CoreClasses.java.lang.Object.Wrapper) + { + this.mw = mw; + mw.Link(); + mw.ResolveMethod(); + } + } + + [IKVM.Attributes.HideFromJava] + [SecuritySafeCritical] + public object newInstance(object[] args) + { + var obj = FormatterServices.GetUninitializedObject(type); + if (mw != null) + mw.Invoke(obj, ConvertArgs(mw.DeclaringType.GetClassLoader(), mw.GetParameters(), args)); + + return obj; + } + + } #if !NO_REF_EMIT - private sealed class BoxUtil - { - - private static readonly MethodInfo valueOfByte = typeof(global::java.lang.Byte).GetMethod("valueOf", new Type[] { typeof(byte) }); - private static readonly MethodInfo valueOfBoolean = typeof(global::java.lang.Boolean).GetMethod("valueOf", new Type[] { typeof(bool) }); - private static readonly MethodInfo valueOfChar = typeof(global::java.lang.Character).GetMethod("valueOf", new Type[] { typeof(char) }); - private static readonly MethodInfo valueOfShort = typeof(global::java.lang.Short).GetMethod("valueOf", new Type[] { typeof(short) }); - private static readonly MethodInfo valueOfInt = typeof(global::java.lang.Integer).GetMethod("valueOf", new Type[] { typeof(int) }); - private static readonly MethodInfo valueOfFloat = typeof(global::java.lang.Float).GetMethod("valueOf", new Type[] { typeof(float) }); - private static readonly MethodInfo valueOfLong = typeof(global::java.lang.Long).GetMethod("valueOf", new Type[] { typeof(long) }); - private static readonly MethodInfo valueOfDouble = typeof(global::java.lang.Double).GetMethod("valueOf", new Type[] { typeof(double) }); - private static readonly MethodInfo byteValue = typeof(global::java.lang.Byte).GetMethod("byteValue", Type.EmptyTypes); - private static readonly MethodInfo booleanValue = typeof(global::java.lang.Boolean).GetMethod("booleanValue", Type.EmptyTypes); - private static readonly MethodInfo charValue = typeof(global::java.lang.Character).GetMethod("charValue", Type.EmptyTypes); - private static readonly MethodInfo shortValue = typeof(global::java.lang.Short).GetMethod("shortValue", Type.EmptyTypes); - private static readonly MethodInfo intValue = typeof(global::java.lang.Integer).GetMethod("intValue", Type.EmptyTypes); - private static readonly MethodInfo floatValue = typeof(global::java.lang.Float).GetMethod("floatValue", Type.EmptyTypes); - private static readonly MethodInfo longValue = typeof(global::java.lang.Long).GetMethod("longValue", Type.EmptyTypes); - private static readonly MethodInfo doubleValue = typeof(global::java.lang.Double).GetMethod("doubleValue", Type.EmptyTypes); - - internal static void EmitUnboxArg(CodeEmitter ilgen, TypeWrapper type) - { - if (type == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Byte)); - ilgen.Emit(OpCodes.Call, byteValue); - } - else if (type == PrimitiveTypeWrapper.BOOLEAN) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Boolean)); - ilgen.Emit(OpCodes.Call, booleanValue); - } - else if (type == PrimitiveTypeWrapper.CHAR) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Character)); - ilgen.Emit(OpCodes.Call, charValue); - } - else if (type == PrimitiveTypeWrapper.SHORT - || type == PrimitiveTypeWrapper.INT - || type == PrimitiveTypeWrapper.FLOAT - || type == PrimitiveTypeWrapper.LONG - || type == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Byte)); - CodeEmitterLabel next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Byte)); - ilgen.Emit(OpCodes.Call, byteValue); - ilgen.Emit(OpCodes.Conv_I1); - Expand(ilgen, type); - CodeEmitterLabel done = ilgen.DefineLabel(); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - if (type == PrimitiveTypeWrapper.SHORT) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Short)); - ilgen.Emit(OpCodes.Call, shortValue); - } - else - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Short)); - next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Short)); - ilgen.Emit(OpCodes.Call, shortValue); - Expand(ilgen, type); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Character)); - next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Character)); - ilgen.Emit(OpCodes.Call, charValue); - Expand(ilgen, type); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - if (type == PrimitiveTypeWrapper.INT) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Integer)); - ilgen.Emit(OpCodes.Call, intValue); - } - else - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Integer)); - next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Integer)); - ilgen.Emit(OpCodes.Call, intValue); - Expand(ilgen, type); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - if (type == PrimitiveTypeWrapper.LONG) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Long)); - ilgen.Emit(OpCodes.Call, longValue); - } - else - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Long)); - next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Long)); - ilgen.Emit(OpCodes.Call, longValue); - Expand(ilgen, type); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - if (type == PrimitiveTypeWrapper.FLOAT) - { - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Float)); - ilgen.Emit(OpCodes.Call, floatValue); - } - else if (type == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Float)); - next = ilgen.DefineLabel(); - ilgen.EmitBrfalse(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Float)); - ilgen.Emit(OpCodes.Call, floatValue); - ilgen.EmitBr(done); - ilgen.MarkLabel(next); - ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Double)); - ilgen.Emit(OpCodes.Call, doubleValue); - } - else - { - throw new InvalidOperationException(); - } - } - } - } - ilgen.MarkLabel(done); - } - else - { - type.EmitCheckcast(ilgen); - } - } - - internal static void BoxReturnValue(CodeEmitter ilgen, TypeWrapper type) - { - if (type == PrimitiveTypeWrapper.VOID) - { - ilgen.Emit(OpCodes.Ldnull); - } - else if (type == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Call, valueOfByte); - } - else if (type == PrimitiveTypeWrapper.BOOLEAN) - { - ilgen.Emit(OpCodes.Call, valueOfBoolean); - } - else if (type == PrimitiveTypeWrapper.CHAR) - { - ilgen.Emit(OpCodes.Call, valueOfChar); - } - else if (type == PrimitiveTypeWrapper.SHORT) - { - ilgen.Emit(OpCodes.Call, valueOfShort); - } - else if (type == PrimitiveTypeWrapper.INT) - { - ilgen.Emit(OpCodes.Call, valueOfInt); - } - else if (type == PrimitiveTypeWrapper.FLOAT) - { - ilgen.Emit(OpCodes.Call, valueOfFloat); - } - else if (type == PrimitiveTypeWrapper.LONG) - { - ilgen.Emit(OpCodes.Call, valueOfLong); - } - else if (type == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Call, valueOfDouble); - } - } - - private static void Expand(CodeEmitter ilgen, TypeWrapper type) - { - if (type == PrimitiveTypeWrapper.FLOAT) - { - ilgen.Emit(OpCodes.Conv_R4); - } - else if (type == PrimitiveTypeWrapper.LONG) - { - ilgen.Emit(OpCodes.Conv_I8); - } - else if (type == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Conv_R8); - } - } - } - - private sealed class FastMethodAccessorImpl : global::sun.reflect.MethodAccessor - { - internal static readonly ConstructorInfo invocationTargetExceptionCtor; - internal static readonly ConstructorInfo illegalArgumentExceptionCtor; - internal static readonly MethodInfo get_TargetSite; - internal static readonly MethodInfo GetCurrentMethod; - - private delegate object Invoker(object obj, object[] args, global::ikvm.@internal.CallerID callerID); - private Invoker invoker; - - static FastMethodAccessorImpl() - { - invocationTargetExceptionCtor = typeof(global::java.lang.reflect.InvocationTargetException).GetConstructor(new Type[] { typeof(Exception) }); - illegalArgumentExceptionCtor = typeof(global::java.lang.IllegalArgumentException).GetConstructor(Type.EmptyTypes); - get_TargetSite = typeof(Exception).GetMethod("get_TargetSite"); - GetCurrentMethod = typeof(MethodBase).GetMethod("GetCurrentMethod"); - } - - private sealed class RunClassInit - { - private FastMethodAccessorImpl outer; - private TypeWrapper tw; - private Invoker invoker; - - internal RunClassInit(FastMethodAccessorImpl outer, TypeWrapper tw, Invoker invoker) - { - this.outer = outer; - this.tw = tw; - this.invoker = invoker; - } - - [IKVM.Attributes.HideFromJava] - internal object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) - { - // FXBUG pre-SP1 a DynamicMethod that calls a static method doesn't trigger the cctor, so we do that explicitly. - // even on .NET 2.0 SP2, interface method invocations don't run the interface cctor - // NOTE when testing, please test both the x86 and x64 CLR JIT, because they have different bugs (even on .NET 2.0 SP2) - tw.RunClassInit(); - outer.invoker = invoker; - return invoker(obj, args, callerID); - } - } - - internal FastMethodAccessorImpl(MethodWrapper mw) - { - TypeWrapper[] parameters; - try - { - mw.DeclaringType.Finish(); - parameters = mw.GetParameters(); - for (int i = 0; i < parameters.Length; i++) - { - // the EnsureLoadable shouldn't fail, because we don't allow a global::java.lang.reflect.Method - // to "escape" if it has an unloadable type in the signature - parameters[i] = parameters[i].EnsureLoadable(mw.DeclaringType.GetClassLoader()); - parameters[i].Finish(); - } - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - mw.ResolveMethod(); - DynamicMethod dm = DynamicMethodUtils.Create("__", mw.DeclaringType.TypeAsBaseType, !mw.IsPublic || !mw.DeclaringType.IsPublic, typeof(object), new Type[] { typeof(object), typeof(object[]), typeof(global::ikvm.@internal.CallerID) }); - CodeEmitter ilgen = CodeEmitter.Create(dm); - CodeEmitterLocal ret = ilgen.DeclareLocal(typeof(object)); - if (!mw.IsStatic) - { - // check target for null - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.EmitNullCheck(); - } - - // check args length - CodeEmitterLabel argsLengthOK = ilgen.DefineLabel(); - if (parameters.Length == 0) - { - // zero length array may be null - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.EmitBrfalse(argsLengthOK); - } - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Ldlen); - ilgen.EmitLdc_I4(parameters.Length); - ilgen.EmitBeq(argsLengthOK); - ilgen.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.MarkLabel(argsLengthOK); - - int thisCount = mw.IsStatic ? 0 : 1; - CodeEmitterLocal[] args = new CodeEmitterLocal[parameters.Length + thisCount]; - if (!mw.IsStatic) - { - args[0] = ilgen.DeclareLocal(mw.DeclaringType.TypeAsSignatureType); - } - for (int i = thisCount; i < args.Length; i++) - { - args[i] = ilgen.DeclareLocal(parameters[i - thisCount].TypeAsSignatureType); - } - ilgen.BeginExceptionBlock(); - if (!mw.IsStatic) - { - ilgen.Emit(OpCodes.Ldarg_0); - mw.DeclaringType.EmitCheckcast(ilgen); - mw.DeclaringType.EmitConvStackTypeToSignatureType(ilgen, null); - ilgen.Emit(OpCodes.Stloc, args[0]); - } - for (int i = thisCount; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.EmitLdc_I4(i - thisCount); - ilgen.Emit(OpCodes.Ldelem_Ref); - TypeWrapper tw = parameters[i - thisCount]; - BoxUtil.EmitUnboxArg(ilgen, tw); - tw.EmitConvStackTypeToSignatureType(ilgen, null); - ilgen.Emit(OpCodes.Stloc, args[i]); - } - CodeEmitterLabel label1 = ilgen.DefineLabel(); - ilgen.EmitLeave(label1); - ilgen.BeginCatchBlock(typeof(InvalidCastException)); - ilgen.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.BeginCatchBlock(typeof(NullReferenceException)); - ilgen.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - - // this is the actual call - ilgen.MarkLabel(label1); - ilgen.BeginExceptionBlock(); - for (int i = 0; i < args.Length; i++) - { - if (i == 0 && !mw.IsStatic && (mw.DeclaringType.IsNonPrimitiveValueType || mw.DeclaringType.IsGhost)) - { - ilgen.Emit(OpCodes.Ldloca, args[i]); - } - else - { - ilgen.Emit(OpCodes.Ldloc, args[i]); - } - } - if (mw.HasCallerID) - { - ilgen.Emit(OpCodes.Ldarg_2); - } - if (mw.IsStatic) - { - mw.EmitCall(ilgen); - } - else - { - mw.EmitCallvirtReflect(ilgen); - } - mw.ReturnType.EmitConvSignatureTypeToStackType(ilgen); - BoxUtil.BoxReturnValue(ilgen, mw.ReturnType); - ilgen.Emit(OpCodes.Stloc, ret); - CodeEmitterLabel label2 = ilgen.DefineLabel(); - ilgen.EmitLeave(label2); - ilgen.BeginCatchBlock(typeof(Exception)); - CodeEmitterLabel label = ilgen.DefineLabel(); - CodeEmitterLabel labelWrap = ilgen.DefineLabel(); - // If the exception we caught is a global::java.lang.reflect.InvocationTargetException, we know it must be - // wrapped, because .NET won't throw that exception and we also cannot check the target site, - // because it may be the same as us if a method is recursively invoking itself. - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.reflect.InvocationTargetException)); - ilgen.EmitBrtrue(labelWrap); - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Callvirt, get_TargetSite); - ilgen.Emit(OpCodes.Call, GetCurrentMethod); - ilgen.Emit(OpCodes.Ceq); - ilgen.EmitBrtrue(label); - ilgen.MarkLabel(labelWrap); - ilgen.Emit(OpCodes.Ldc_I4_0); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); - ilgen.Emit(OpCodes.Newobj, invocationTargetExceptionCtor); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - - ilgen.MarkLabel(label2); - ilgen.Emit(OpCodes.Ldloc, ret); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - invoker = (Invoker)dm.CreateDelegate(typeof(Invoker)); - if ((mw.IsStatic || mw.DeclaringType.IsInterface) && mw.DeclaringType.HasStaticInitializer) - { - invoker = new Invoker(new RunClassInit(this, mw.DeclaringType, invoker).invoke); - } - } - - [IKVM.Attributes.HideFromJava] - public object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) - { - try - { - return invoker(obj, args, callerID); - } - catch (MethodAccessException x) - { - // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - } - - private sealed class FastConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor - { - private delegate object Invoker(object[] args); - private Invoker invoker; - - internal FastConstructorAccessorImpl(global::java.lang.reflect.Constructor constructor) - { - MethodWrapper mw = MethodWrapper.FromExecutable(constructor); - TypeWrapper[] parameters; - try - { - mw.DeclaringType.Finish(); - parameters = mw.GetParameters(); - for (int i = 0; i < parameters.Length; i++) - { - // the EnsureLoadable shouldn't fail, because we don't allow a global::java.lang.reflect.Method - // to "escape" if it has an unloadable type in the signature - parameters[i] = parameters[i].EnsureLoadable(mw.DeclaringType.GetClassLoader()); - parameters[i].Finish(); - } - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - mw.ResolveMethod(); - DynamicMethod dm = DynamicMethodUtils.Create("__", mw.DeclaringType.TypeAsTBD, !mw.IsPublic || !mw.DeclaringType.IsPublic, typeof(object), new Type[] { typeof(object[]) }); - CodeEmitter ilgen = CodeEmitter.Create(dm); - CodeEmitterLocal ret = ilgen.DeclareLocal(typeof(object)); - - // check args length - CodeEmitterLabel argsLengthOK = ilgen.DefineLabel(); - if (parameters.Length == 0) - { - // zero length array may be null - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.EmitBrfalse(argsLengthOK); - } - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldlen); - ilgen.EmitLdc_I4(parameters.Length); - ilgen.EmitBeq(argsLengthOK); - ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.MarkLabel(argsLengthOK); - - CodeEmitterLocal[] args = new CodeEmitterLocal[parameters.Length]; - for (int i = 0; i < args.Length; i++) - { - args[i] = ilgen.DeclareLocal(parameters[i].TypeAsSignatureType); - } - ilgen.BeginExceptionBlock(); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.EmitLdc_I4(i); - ilgen.Emit(OpCodes.Ldelem_Ref); - TypeWrapper tw = parameters[i]; - BoxUtil.EmitUnboxArg(ilgen, tw); - tw.EmitConvStackTypeToSignatureType(ilgen, null); - ilgen.Emit(OpCodes.Stloc, args[i]); - } - CodeEmitterLabel label1 = ilgen.DefineLabel(); - ilgen.EmitLeave(label1); - ilgen.BeginCatchBlock(typeof(InvalidCastException)); - ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.BeginCatchBlock(typeof(NullReferenceException)); - ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - - // this is the actual call - ilgen.MarkLabel(label1); - ilgen.BeginExceptionBlock(); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, args[i]); - } - mw.EmitNewobj(ilgen); - ilgen.Emit(OpCodes.Stloc, ret); - CodeEmitterLabel label2 = ilgen.DefineLabel(); - ilgen.EmitLeave(label2); - ilgen.BeginCatchBlock(typeof(Exception)); - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Callvirt, FastMethodAccessorImpl.get_TargetSite); - ilgen.Emit(OpCodes.Call, FastMethodAccessorImpl.GetCurrentMethod); - ilgen.Emit(OpCodes.Ceq); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitBrtrue(label); - ilgen.Emit(OpCodes.Ldc_I4_0); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); - ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.invocationTargetExceptionCtor); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - - ilgen.MarkLabel(label2); - ilgen.Emit(OpCodes.Ldloc, ret); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - invoker = (Invoker)dm.CreateDelegate(typeof(Invoker)); - } - - [IKVM.Attributes.HideFromJava] - public object newInstance(object[] args) - { - try - { - return invoker(args); - } - catch (MethodAccessException x) - { - // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - } - - private sealed class FastSerializationConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor - { - private static readonly MethodInfo GetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }); - private static readonly MethodInfo GetUninitializedObjectMethod = typeof(FormatterServices).GetMethod("GetUninitializedObject", new Type[] { typeof(Type) }); - private delegate object InvokeCtor(); - private InvokeCtor invoker; - - internal FastSerializationConstructorAccessorImpl(global::java.lang.reflect.Constructor constructorToCall, global::java.lang.Class classToInstantiate) - { - MethodWrapper constructor = MethodWrapper.FromExecutable(constructorToCall); - if (constructor.GetParameters().Length != 0) - { - throw new NotImplementedException("Serialization constructor cannot have parameters"); - } - constructor.Link(); - constructor.ResolveMethod(); - Type type; - try - { - TypeWrapper wrapper = TypeWrapper.FromClass(classToInstantiate); - wrapper.Finish(); - type = wrapper.TypeAsBaseType; - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - DynamicMethod dm = DynamicMethodUtils.Create("__", constructor.DeclaringType.TypeAsBaseType, true, typeof(object), null); - CodeEmitter ilgen = CodeEmitter.Create(dm); - ilgen.Emit(OpCodes.Ldtoken, type); - ilgen.Emit(OpCodes.Call, GetTypeFromHandleMethod); - ilgen.Emit(OpCodes.Call, GetUninitializedObjectMethod); - ilgen.Emit(OpCodes.Dup); - constructor.EmitCall(ilgen); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - invoker = (InvokeCtor)dm.CreateDelegate(typeof(InvokeCtor)); - } - - [IKVM.Attributes.HideFromJava] - public object newInstance(object[] args) - { - try - { - return invoker(); - } - catch (MethodAccessException x) - { - // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - } + sealed class BoxUtil + { + + static readonly MethodInfo valueOfByte = typeof(global::java.lang.Byte).GetMethod("valueOf", new Type[] { typeof(byte) }); + static readonly MethodInfo valueOfBoolean = typeof(global::java.lang.Boolean).GetMethod("valueOf", new Type[] { typeof(bool) }); + static readonly MethodInfo valueOfChar = typeof(global::java.lang.Character).GetMethod("valueOf", new Type[] { typeof(char) }); + static readonly MethodInfo valueOfShort = typeof(global::java.lang.Short).GetMethod("valueOf", new Type[] { typeof(short) }); + static readonly MethodInfo valueOfInt = typeof(global::java.lang.Integer).GetMethod("valueOf", new Type[] { typeof(int) }); + static readonly MethodInfo valueOfFloat = typeof(global::java.lang.Float).GetMethod("valueOf", new Type[] { typeof(float) }); + static readonly MethodInfo valueOfLong = typeof(global::java.lang.Long).GetMethod("valueOf", new Type[] { typeof(long) }); + static readonly MethodInfo valueOfDouble = typeof(global::java.lang.Double).GetMethod("valueOf", new Type[] { typeof(double) }); + static readonly MethodInfo byteValue = typeof(global::java.lang.Byte).GetMethod("byteValue", Type.EmptyTypes); + static readonly MethodInfo booleanValue = typeof(global::java.lang.Boolean).GetMethod("booleanValue", Type.EmptyTypes); + static readonly MethodInfo charValue = typeof(global::java.lang.Character).GetMethod("charValue", Type.EmptyTypes); + static readonly MethodInfo shortValue = typeof(global::java.lang.Short).GetMethod("shortValue", Type.EmptyTypes); + static readonly MethodInfo intValue = typeof(global::java.lang.Integer).GetMethod("intValue", Type.EmptyTypes); + static readonly MethodInfo floatValue = typeof(global::java.lang.Float).GetMethod("floatValue", Type.EmptyTypes); + static readonly MethodInfo longValue = typeof(global::java.lang.Long).GetMethod("longValue", Type.EmptyTypes); + static readonly MethodInfo doubleValue = typeof(global::java.lang.Double).GetMethod("doubleValue", Type.EmptyTypes); + + internal static void EmitUnboxArg(CodeEmitter ilgen, TypeWrapper type) + { + if (type == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Byte)); + ilgen.Emit(OpCodes.Call, byteValue); + } + else if (type == PrimitiveTypeWrapper.BOOLEAN) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Boolean)); + ilgen.Emit(OpCodes.Call, booleanValue); + } + else if (type == PrimitiveTypeWrapper.CHAR) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Character)); + ilgen.Emit(OpCodes.Call, charValue); + } + else if (type == PrimitiveTypeWrapper.SHORT + || type == PrimitiveTypeWrapper.INT + || type == PrimitiveTypeWrapper.FLOAT + || type == PrimitiveTypeWrapper.LONG + || type == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Byte)); + CodeEmitterLabel next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Byte)); + ilgen.Emit(OpCodes.Call, byteValue); + ilgen.Emit(OpCodes.Conv_I1); + Expand(ilgen, type); + CodeEmitterLabel done = ilgen.DefineLabel(); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + if (type == PrimitiveTypeWrapper.SHORT) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Short)); + ilgen.Emit(OpCodes.Call, shortValue); + } + else + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Short)); + next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Short)); + ilgen.Emit(OpCodes.Call, shortValue); + Expand(ilgen, type); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Character)); + next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Character)); + ilgen.Emit(OpCodes.Call, charValue); + Expand(ilgen, type); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + if (type == PrimitiveTypeWrapper.INT) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Integer)); + ilgen.Emit(OpCodes.Call, intValue); + } + else + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Integer)); + next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Integer)); + ilgen.Emit(OpCodes.Call, intValue); + Expand(ilgen, type); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + if (type == PrimitiveTypeWrapper.LONG) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Long)); + ilgen.Emit(OpCodes.Call, longValue); + } + else + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Long)); + next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Long)); + ilgen.Emit(OpCodes.Call, longValue); + Expand(ilgen, type); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + if (type == PrimitiveTypeWrapper.FLOAT) + { + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Float)); + ilgen.Emit(OpCodes.Call, floatValue); + } + else if (type == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Isinst, typeof(global::java.lang.Float)); + next = ilgen.DefineLabel(); + ilgen.EmitBrfalse(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Float)); + ilgen.Emit(OpCodes.Call, floatValue); + ilgen.EmitBr(done); + ilgen.MarkLabel(next); + ilgen.Emit(OpCodes.Castclass, typeof(global::java.lang.Double)); + ilgen.Emit(OpCodes.Call, doubleValue); + } + else + { + throw new InvalidOperationException(); + } + } + } + } + ilgen.MarkLabel(done); + } + else + { + type.EmitCheckcast(ilgen); + } + } + + internal static void BoxReturnValue(CodeEmitter ilgen, TypeWrapper type) + { + if (type == PrimitiveTypeWrapper.VOID) + { + ilgen.Emit(OpCodes.Ldnull); + } + else if (type == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Call, valueOfByte); + } + else if (type == PrimitiveTypeWrapper.BOOLEAN) + { + ilgen.Emit(OpCodes.Call, valueOfBoolean); + } + else if (type == PrimitiveTypeWrapper.CHAR) + { + ilgen.Emit(OpCodes.Call, valueOfChar); + } + else if (type == PrimitiveTypeWrapper.SHORT) + { + ilgen.Emit(OpCodes.Call, valueOfShort); + } + else if (type == PrimitiveTypeWrapper.INT) + { + ilgen.Emit(OpCodes.Call, valueOfInt); + } + else if (type == PrimitiveTypeWrapper.FLOAT) + { + ilgen.Emit(OpCodes.Call, valueOfFloat); + } + else if (type == PrimitiveTypeWrapper.LONG) + { + ilgen.Emit(OpCodes.Call, valueOfLong); + } + else if (type == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Call, valueOfDouble); + } + } + + static void Expand(CodeEmitter ilgen, TypeWrapper type) + { + if (type == PrimitiveTypeWrapper.FLOAT) + { + ilgen.Emit(OpCodes.Conv_R4); + } + else if (type == PrimitiveTypeWrapper.LONG) + { + ilgen.Emit(OpCodes.Conv_I8); + } + else if (type == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Conv_R8); + } + } + } + + /// + /// Fast implementation of using dynamic methods. + /// + sealed class FastMethodAccessorImpl : global::sun.reflect.MethodAccessor + { + + internal static readonly ConstructorInfo invocationTargetExceptionCtor; + internal static readonly ConstructorInfo illegalArgumentExceptionCtor; + internal static readonly MethodInfo get_TargetSite; + internal static readonly MethodInfo GetCurrentMethod; + + delegate object Invoker(object obj, object[] args, global::ikvm.@internal.CallerID callerID); + Invoker invoker; + + /// + /// Initializes a new instance. + /// + static FastMethodAccessorImpl() + { + invocationTargetExceptionCtor = typeof(global::java.lang.reflect.InvocationTargetException).GetConstructor(new Type[] { typeof(Exception) }); + illegalArgumentExceptionCtor = typeof(global::java.lang.IllegalArgumentException).GetConstructor(Type.EmptyTypes); + get_TargetSite = typeof(Exception).GetMethod("get_TargetSite"); + GetCurrentMethod = typeof(MethodBase).GetMethod("GetCurrentMethod"); + } + + sealed class RunClassInit + { + + readonly FastMethodAccessorImpl outer; + readonly TypeWrapper tw; + readonly Invoker invoker; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal RunClassInit(FastMethodAccessorImpl outer, TypeWrapper tw, Invoker invoker) + { + this.outer = outer; + this.tw = tw; + this.invoker = invoker; + } + + [IKVM.Attributes.HideFromJava] + internal object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) + { + // FXBUG pre-SP1 a DynamicMethod that calls a static method doesn't trigger the cctor, so we do that explicitly. + // even on .NET 2.0 SP2, interface method invocations don't run the interface cctor + // NOTE when testing, please test both the x86 and x64 CLR JIT, because they have different bugs (even on .NET 2.0 SP2) + tw.RunClassInit(); + outer.invoker = invoker; + return invoker(obj, args, callerID); + } + + } + + /// + /// Initializes a new instance. + /// + /// + internal FastMethodAccessorImpl(MethodWrapper mw) + { + try + { + mw.DeclaringType.Finish(); + var parameters = mw.GetParameters(); + + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = parameters[i].EnsureLoadable(mw.DeclaringType.GetClassLoader()); + parameters[i].Finish(); + } + + // resolve the runtime method info + mw.ResolveMethod(); + + // generate new dynamic method + var np = !mw.IsPublic || !mw.DeclaringType.IsPublic; + var dm = DynamicMethodUtil.Create($"____{mw.DeclaringType.Name.Replace(".", "_")}__{mw.Name}", mw.DeclaringType.TypeAsBaseType, np, typeof(object), new Type[] { typeof(object), typeof(object[]), typeof(global::ikvm.@internal.CallerID) }); + var il = CodeEmitter.Create(dm); + var rt = il.DeclareLocal(typeof(object)); + + // labels + var marshalLabel = il.DefineLabel(); + var callLabel = il.DefineLabel(); + var returnLabel = il.DefineLabel(); + + // do a null check for instance argument + if (mw.IsStatic == false) + { + il.Emit(OpCodes.Ldarg_0); + il.EmitNullCheck(); + } + + // zero length array may be null + if (parameters.Length == 0) + { + il.Emit(OpCodes.Ldarg_1); + il.EmitBrfalse(marshalLabel); + } + + // parameters length must match number of parameters on array + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldlen); + il.EmitLdc_I4(parameters.Length); + il.EmitBeq(marshalLabel); + il.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); + il.Emit(OpCodes.Throw); + + // begin parameter conversion + il.MarkLabel(marshalLabel); + + // emit self and argument locals + var self = mw.IsStatic == false ? il.DeclareLocal(mw.DeclaringType.TypeAsSignatureType) : null; + var args = new CodeEmitterLocal[parameters.Length]; + for (var i = 0; i < args.Length; i++) + args[i] = il.DeclareLocal(parameters[i].TypeAsSignatureType); + + // try block for conversion code + il.BeginExceptionBlock(); + + // emit conversion code for the 'self' argument + if (self != null) + { + il.Emit(OpCodes.Ldarg_0); + mw.DeclaringType.EmitCheckcast(il); + mw.DeclaringType.EmitConvStackTypeToSignatureType(il, null); + il.Emit(OpCodes.Stloc, self); + } + + // emit conversion code for the remainder of the arguments + for (var i = 0; i < args.Length; i++) + { + il.Emit(OpCodes.Ldarg_1); + il.EmitLdc_I4(i); + il.Emit(OpCodes.Ldelem_Ref); + + var tw = parameters[i]; + BoxUtil.EmitUnboxArg(il, tw); + tw.EmitConvStackTypeToSignatureType(il, null); + il.Emit(OpCodes.Stloc, args[i]); + } + + // exception handler + il.EmitLeave(callLabel); + il.BeginCatchBlock(typeof(InvalidCastException)); + il.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); + il.Emit(OpCodes.Throw); + il.BeginCatchBlock(typeof(NullReferenceException)); + il.Emit(OpCodes.Newobj, illegalArgumentExceptionCtor); + il.Emit(OpCodes.Throw); + il.EndExceptionBlock(); + + // begin exception block for actual call + il.MarkLabel(callLabel); + il.BeginExceptionBlock(); + + // first constructor arg for the delegate: null for static, reference for valuetype/ghost, reference for instance + if (self != null && (mw.DeclaringType.IsNonPrimitiveValueType || mw.DeclaringType.IsGhost)) + il.Emit(OpCodes.Ldloca, self); + else if (self != null) + il.Emit(OpCodes.Ldloc, self); + + // load the remainder of the arguments + for (var i = 0; i < args.Length; i++) + il.Emit(OpCodes.Ldloc, args[i]); + + // method requires caller ID passed as final argument + if (mw.HasCallerID) + il.EmitLdarg(2); + + // final method call + il.Emit(self == null ? OpCodes.Call : OpCodes.Callvirt, mw.GetMethod()); + + // convert and store return value + mw.ReturnType.EmitConvSignatureTypeToStackType(il); + BoxUtil.BoxReturnValue(il, mw.ReturnType); + il.Emit(OpCodes.Stloc, rt); + il.EmitLeave(returnLabel); + + // catch the results of the call + il.BeginCatchBlock(typeof(Exception)); + + // labels + var exceptionWrapLabel = il.DefineLabel(); + var exceptionThrowLabel = il.DefineLabel(); + + // If the exception we caught is a global::java.lang.reflect.InvocationTargetException, we know it must be + // wrapped, because .NET won't throw that exception and we also cannot check the target site, + // because it may be the same as us if a method is recursively invoking itself. + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Isinst, typeof(global::java.lang.reflect.InvocationTargetException)); + il.EmitBrtrue(exceptionWrapLabel); + + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Callvirt, get_TargetSite); + il.Emit(OpCodes.Call, GetCurrentMethod); + il.Emit(OpCodes.Ceq); + il.EmitBrtrue(exceptionThrowLabel); + + il.MarkLabel(exceptionWrapLabel); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); + il.Emit(OpCodes.Newobj, invocationTargetExceptionCtor); + + il.MarkLabel(exceptionThrowLabel); + il.Emit(OpCodes.Throw); + il.EndExceptionBlock(); + + il.MarkLabel(returnLabel); + il.Emit(OpCodes.Ldloc, rt); + il.Emit(OpCodes.Ret); + il.DoEmit(); + + // generate invoker + invoker = (Invoker)dm.CreateDelegate(typeof(Invoker)); + + // invoker needs to run clinit, wrap in an invoker that does so + if ((mw.IsStatic || mw.DeclaringType.IsInterface) && mw.DeclaringType.HasStaticInitializer) + invoker = new Invoker(new RunClassInit(this, mw.DeclaringType, invoker).invoke); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + } + + [IKVM.Attributes.HideFromJava] + public object invoke(object obj, object[] args, global::ikvm.@internal.CallerID callerID) + { + try + { + return invoker(obj, args, callerID); + } + catch (MethodAccessException x) + { + // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + } + + sealed class FastConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor + { + + delegate object Invoker(object[] args); + Invoker invoker; + + internal FastConstructorAccessorImpl(global::java.lang.reflect.Constructor constructor) + { + var mw = MethodWrapper.FromExecutable(constructor); + + TypeWrapper[] parameters; + try + { + mw.DeclaringType.Finish(); + parameters = mw.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + // the EnsureLoadable shouldn't fail, because we don't allow a global::java.lang.reflect.Method + // to "escape" if it has an unloadable type in the signature + parameters[i] = parameters[i].EnsureLoadable(mw.DeclaringType.GetClassLoader()); + parameters[i].Finish(); + } + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + + mw.ResolveMethod(); + var dm = DynamicMethodUtil.Create("__", mw.DeclaringType.TypeAsTBD, !mw.IsPublic || !mw.DeclaringType.IsPublic, typeof(object), new Type[] { typeof(object[]) }); + var ilgen = CodeEmitter.Create(dm); + var ret = ilgen.DeclareLocal(typeof(object)); + + // check args length + var argsLengthOK = ilgen.DefineLabel(); + if (parameters.Length == 0) + { + // zero length array may be null + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.EmitBrfalse(argsLengthOK); + } + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldlen); + ilgen.EmitLdc_I4(parameters.Length); + ilgen.EmitBeq(argsLengthOK); + ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); + ilgen.Emit(OpCodes.Throw); + ilgen.MarkLabel(argsLengthOK); + + var args = new CodeEmitterLocal[parameters.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = ilgen.DeclareLocal(parameters[i].TypeAsSignatureType); + + ilgen.BeginExceptionBlock(); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.EmitLdc_I4(i); + ilgen.Emit(OpCodes.Ldelem_Ref); + TypeWrapper tw = parameters[i]; + BoxUtil.EmitUnboxArg(ilgen, tw); + tw.EmitConvStackTypeToSignatureType(ilgen, null); + ilgen.Emit(OpCodes.Stloc, args[i]); + } + CodeEmitterLabel label1 = ilgen.DefineLabel(); + ilgen.EmitLeave(label1); + ilgen.BeginCatchBlock(typeof(InvalidCastException)); + ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); + ilgen.Emit(OpCodes.Throw); + ilgen.BeginCatchBlock(typeof(NullReferenceException)); + ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.illegalArgumentExceptionCtor); + ilgen.Emit(OpCodes.Throw); + ilgen.EndExceptionBlock(); + + // this is the actual call + ilgen.MarkLabel(label1); + ilgen.BeginExceptionBlock(); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, args[i]); + } + mw.EmitNewobj(ilgen); + ilgen.Emit(OpCodes.Stloc, ret); + CodeEmitterLabel label2 = ilgen.DefineLabel(); + ilgen.EmitLeave(label2); + ilgen.BeginCatchBlock(typeof(Exception)); + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Callvirt, FastMethodAccessorImpl.get_TargetSite); + ilgen.Emit(OpCodes.Call, FastMethodAccessorImpl.GetCurrentMethod); + ilgen.Emit(OpCodes.Ceq); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitBrtrue(label); + ilgen.Emit(OpCodes.Ldc_I4_0); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); + ilgen.Emit(OpCodes.Newobj, FastMethodAccessorImpl.invocationTargetExceptionCtor); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Throw); + ilgen.EndExceptionBlock(); + + ilgen.MarkLabel(label2); + ilgen.Emit(OpCodes.Ldloc, ret); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + invoker = (Invoker)dm.CreateDelegate(typeof(Invoker)); + } + + [IKVM.Attributes.HideFromJava] + public object newInstance(object[] args) + { + try + { + return invoker(args); + } + catch (MethodAccessException x) + { + // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + } + + private sealed class FastSerializationConstructorAccessorImpl : global::sun.reflect.ConstructorAccessor + { + private static readonly MethodInfo GetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }); + private static readonly MethodInfo GetUninitializedObjectMethod = typeof(FormatterServices).GetMethod("GetUninitializedObject", new Type[] { typeof(Type) }); + private delegate object InvokeCtor(); + private InvokeCtor invoker; + + internal FastSerializationConstructorAccessorImpl(global::java.lang.reflect.Constructor constructorToCall, global::java.lang.Class classToInstantiate) + { + MethodWrapper constructor = MethodWrapper.FromExecutable(constructorToCall); + if (constructor.GetParameters().Length != 0) + { + throw new NotImplementedException("Serialization constructor cannot have parameters"); + } + constructor.Link(); + constructor.ResolveMethod(); + Type type; + try + { + TypeWrapper wrapper = TypeWrapper.FromClass(classToInstantiate); + wrapper.Finish(); + type = wrapper.TypeAsBaseType; + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + DynamicMethod dm = DynamicMethodUtil.Create("__", constructor.DeclaringType.TypeAsBaseType, true, typeof(object), null); + CodeEmitter ilgen = CodeEmitter.Create(dm); + ilgen.Emit(OpCodes.Ldtoken, type); + ilgen.Emit(OpCodes.Call, GetTypeFromHandleMethod); + ilgen.Emit(OpCodes.Call, GetUninitializedObjectMethod); + ilgen.Emit(OpCodes.Dup); + constructor.EmitCall(ilgen); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + invoker = (InvokeCtor)dm.CreateDelegate(typeof(InvokeCtor)); + } + + [IKVM.Attributes.HideFromJava] + public object newInstance(object[] args) + { + try + { + return invoker(); + } + catch (MethodAccessException x) + { + // this can happen if we're calling a non-public method and the call stack doesn't have ReflectionPermission.MemberAccess + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + } #endif // !NO_REF_EMIT - sealed class ActivatorConstructorAccessor : global::sun.reflect.ConstructorAccessor - { - private readonly Type type; - - internal ActivatorConstructorAccessor(MethodWrapper mw) - { - this.type = mw.DeclaringType.TypeAsBaseType; - } - - public object newInstance(object[] objarr) - { - if (objarr != null && objarr.Length != 0) - { - throw new global::java.lang.IllegalArgumentException(); - } - try - { - return Activator.CreateInstance(type); - } - catch (TargetInvocationException x) - { - throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(x.InnerException)); - } - } - - internal static bool IsSuitable(MethodWrapper mw) - { - MethodBase mb = mw.GetMethod(); - return mb != null - && mb.IsConstructor - && mb.IsPublic - && mb.DeclaringType.IsPublic - && mb.DeclaringType == mw.DeclaringType.TypeAsBaseType - && mb.GetParameters().Length == 0; - } - } - - private abstract class FieldAccessorImplBase : global::sun.reflect.FieldAccessor, IReflectionException - { - protected static readonly ushort inflationThreshold = 15; - protected readonly FieldWrapper fw; - protected readonly bool isFinal; - protected ushort numInvocations; - - static FieldAccessorImplBase() - { - string str = global::java.lang.Props.props.getProperty("ikvm.reflect.field.inflationThreshold"); - int value; - if (str != null && int.TryParse(str, out value)) - { - if (value >= ushort.MinValue && value <= ushort.MaxValue) - { - inflationThreshold = (ushort)value; - } - } - } - - private FieldAccessorImplBase(FieldWrapper fw, bool isFinal) - { - this.fw = fw; - this.isFinal = isFinal; - } - - private string GetQualifiedFieldName() - { - return fw.DeclaringType.Name + "." + fw.Name; - } - - private string GetFieldTypeName() - { - return fw.FieldTypeWrapper.IsPrimitive - ? fw.FieldTypeWrapper.ClassObject.getName() - : fw.FieldTypeWrapper.Name; - } - - public global::java.lang.IllegalArgumentException GetIllegalArgumentException(object obj) - { - // LAME like JDK 6 we return the wrong exception message (talking about setting the field, instead of getting) - return SetIllegalArgumentException(obj); - } - - public global::java.lang.IllegalArgumentException SetIllegalArgumentException(object obj) - { - // LAME like JDK 6 we return the wrong exception message (when obj is the object, instead of the value) - return SetIllegalArgumentException(obj != null ? global::ikvm.runtime.Util.getClassFromObject(obj).getName() : "", ""); - } - - private global::java.lang.IllegalArgumentException SetIllegalArgumentException(string attemptedType, string attemptedValue) - { - return new global::java.lang.IllegalArgumentException(GetSetMessage(attemptedType, attemptedValue)); - } - - protected global::java.lang.IllegalAccessException FinalFieldIllegalAccessException(object obj) - { - return FinalFieldIllegalAccessException(obj != null ? global::ikvm.runtime.Util.getClassFromObject(obj).getName() : "", ""); - } - - private global::java.lang.IllegalAccessException FinalFieldIllegalAccessException(string attemptedType, string attemptedValue) - { - return new global::java.lang.IllegalAccessException(GetSetMessage(attemptedType, attemptedValue)); - } - - private global::java.lang.IllegalArgumentException GetIllegalArgumentException(string type) - { - return new global::java.lang.IllegalArgumentException("Attempt to get " + GetFieldTypeName() + " field \"" + GetQualifiedFieldName() + "\" with illegal data type conversion to " + type); - } - - // this message comes from global::sun.reflect.UnsafeFieldAccessorImpl - private string GetSetMessage(String attemptedType, String attemptedValue) - { - String err = "Can not set"; - if (fw.IsStatic) - err += " static"; - if (isFinal) - err += " final"; - err += " " + GetFieldTypeName() + " field " + GetQualifiedFieldName() + " to "; - if (attemptedValue.Length > 0) - { - err += "(" + attemptedType + ")" + attemptedValue; - } - else - { - if (attemptedType.Length > 0) - err += attemptedType; - else - err += "null value"; - } - return err; - } - - public virtual bool getBoolean(object obj) - { - throw GetIllegalArgumentException("boolean"); - } - - public virtual byte getByte(object obj) - { - throw GetIllegalArgumentException("byte"); - } - - public virtual char getChar(object obj) - { - throw GetIllegalArgumentException("char"); - } - - public virtual short getShort(object obj) - { - throw GetIllegalArgumentException("short"); - } - - public virtual int getInt(object obj) - { - throw GetIllegalArgumentException("int"); - } - - public virtual long getLong(object obj) - { - throw GetIllegalArgumentException("long"); - } - - public virtual float getFloat(object obj) - { - throw GetIllegalArgumentException("float"); - } - - public virtual double getDouble(object obj) - { - throw GetIllegalArgumentException("double"); - } - - public virtual void setBoolean(object obj, bool z) - { - throw SetIllegalArgumentException("boolean", global::java.lang.Boolean.toString(z)); - } - - public virtual void setByte(object obj, byte b) - { - throw SetIllegalArgumentException("byte", global::java.lang.Byte.toString(b)); - } - - public virtual void setChar(object obj, char c) - { - throw SetIllegalArgumentException("char", global::java.lang.Character.toString(c)); - } - - public virtual void setShort(object obj, short s) - { - throw SetIllegalArgumentException("short", global::java.lang.Short.toString(s)); - } - - public virtual void setInt(object obj, int i) - { - throw SetIllegalArgumentException("int", global::java.lang.Integer.toString(i)); - } - - public virtual void setLong(object obj, long l) - { - throw SetIllegalArgumentException("long", global::java.lang.Long.toString(l)); - } - - public virtual void setFloat(object obj, float f) - { - throw SetIllegalArgumentException("float", global::java.lang.Float.toString(f)); - } - - public virtual void setDouble(object obj, double d) - { - throw SetIllegalArgumentException("double", global::java.lang.Double.toString(d)); - } - - public abstract object get(object obj); - public abstract void set(object obj, object value); - - private abstract class FieldAccessor : FieldAccessorImplBase - { - protected delegate void Setter(object obj, T value, FieldAccessor acc); - protected delegate T Getter(object obj, FieldAccessor acc); - private static readonly Setter initialSetter = lazySet; - private static readonly Getter initialGetter = lazyGet; - protected Setter setter = initialSetter; - protected Getter getter = initialGetter; - - internal FieldAccessor(FieldWrapper fw, bool isFinal) - : base(fw, isFinal) - { - if (!IsSlowPathCompatible(fw)) - { - // prevent slow path - numInvocations = inflationThreshold; - } - } - - private bool IsSlowPathCompatible(FieldWrapper fw) - { + sealed class ActivatorConstructorAccessor : global::sun.reflect.ConstructorAccessor + { + private readonly Type type; + + internal ActivatorConstructorAccessor(MethodWrapper mw) + { + this.type = mw.DeclaringType.TypeAsBaseType; + } + + public object newInstance(object[] objarr) + { + if (objarr != null && objarr.Length != 0) + { + throw new global::java.lang.IllegalArgumentException(); + } + try + { + return Activator.CreateInstance(type); + } + catch (TargetInvocationException x) + { + throw new global::java.lang.reflect.InvocationTargetException(global::ikvm.runtime.Util.mapException(x.InnerException)); + } + } + + internal static bool IsSuitable(MethodWrapper mw) + { + MethodBase mb = mw.GetMethod(); + return mb != null + && mb.IsConstructor + && mb.IsPublic + && mb.DeclaringType.IsPublic + && mb.DeclaringType == mw.DeclaringType.TypeAsBaseType + && mb.GetParameters().Length == 0; + } + } + + private abstract class FieldAccessorImplBase : global::sun.reflect.FieldAccessor, IReflectionException + { + + protected static readonly ushort inflationThreshold = 15; + protected readonly FieldWrapper fw; + protected readonly bool isFinal; + protected ushort numInvocations; + + /// + /// Initializes the static instance. + /// + static FieldAccessorImplBase() + { + if (global::java.security.AccessController.doPrivileged(new FuncPrivilegedAction(() => SystemAccessor.InvokeGetProperty("ikvm.reflect.field.inflationThreshold"))) is string s && ushort.TryParse(s, out var value)) + inflationThreshold = value; + } + + /// + /// Initializes a new instance. + /// + /// + /// + FieldAccessorImplBase(FieldWrapper fw, bool isFinal) + { + this.fw = fw; + this.isFinal = isFinal; + } + + private string GetQualifiedFieldName() + { + return fw.DeclaringType.Name + "." + fw.Name; + } + + private string GetFieldTypeName() + { + return fw.FieldTypeWrapper.IsPrimitive + ? fw.FieldTypeWrapper.ClassObject.getName() + : fw.FieldTypeWrapper.Name; + } + + public global::java.lang.IllegalArgumentException GetIllegalArgumentException(object obj) + { + // LAME like JDK 6 we return the wrong exception message (talking about setting the field, instead of getting) + return SetIllegalArgumentException(obj); + } + + public global::java.lang.IllegalArgumentException SetIllegalArgumentException(object obj) + { + // LAME like JDK 6 we return the wrong exception message (when obj is the object, instead of the value) + return SetIllegalArgumentException(obj != null ? global::ikvm.runtime.Util.getClassFromObject(obj).getName() : "", ""); + } + + private global::java.lang.IllegalArgumentException SetIllegalArgumentException(string attemptedType, string attemptedValue) + { + return new global::java.lang.IllegalArgumentException(GetSetMessage(attemptedType, attemptedValue)); + } + + protected global::java.lang.IllegalAccessException FinalFieldIllegalAccessException(object obj) + { + return FinalFieldIllegalAccessException(obj != null ? global::ikvm.runtime.Util.getClassFromObject(obj).getName() : "", ""); + } + + private global::java.lang.IllegalAccessException FinalFieldIllegalAccessException(string attemptedType, string attemptedValue) + { + return new global::java.lang.IllegalAccessException(GetSetMessage(attemptedType, attemptedValue)); + } + + private global::java.lang.IllegalArgumentException GetIllegalArgumentException(string type) + { + return new global::java.lang.IllegalArgumentException("Attempt to get " + GetFieldTypeName() + " field \"" + GetQualifiedFieldName() + "\" with illegal data type conversion to " + type); + } + + // this message comes from global::sun.reflect.UnsafeFieldAccessorImpl + private string GetSetMessage(String attemptedType, String attemptedValue) + { + String err = "Can not set"; + if (fw.IsStatic) + err += " static"; + if (isFinal) + err += " final"; + err += " " + GetFieldTypeName() + " field " + GetQualifiedFieldName() + " to "; + if (attemptedValue.Length > 0) + { + err += "(" + attemptedType + ")" + attemptedValue; + } + else + { + if (attemptedType.Length > 0) + err += attemptedType; + else + err += "null value"; + } + return err; + } + + public virtual bool getBoolean(object obj) + { + throw GetIllegalArgumentException("boolean"); + } + + public virtual byte getByte(object obj) + { + throw GetIllegalArgumentException("byte"); + } + + public virtual char getChar(object obj) + { + throw GetIllegalArgumentException("char"); + } + + public virtual short getShort(object obj) + { + throw GetIllegalArgumentException("short"); + } + + public virtual int getInt(object obj) + { + throw GetIllegalArgumentException("int"); + } + + public virtual long getLong(object obj) + { + throw GetIllegalArgumentException("long"); + } + + public virtual float getFloat(object obj) + { + throw GetIllegalArgumentException("float"); + } + + public virtual double getDouble(object obj) + { + throw GetIllegalArgumentException("double"); + } + + public virtual void setBoolean(object obj, bool z) + { + throw SetIllegalArgumentException("boolean", global::java.lang.Boolean.toString(z)); + } + + public virtual void setByte(object obj, byte b) + { + throw SetIllegalArgumentException("byte", global::java.lang.Byte.toString(b)); + } + + public virtual void setChar(object obj, char c) + { + throw SetIllegalArgumentException("char", global::java.lang.Character.toString(c)); + } + + public virtual void setShort(object obj, short s) + { + throw SetIllegalArgumentException("short", global::java.lang.Short.toString(s)); + } + + public virtual void setInt(object obj, int i) + { + throw SetIllegalArgumentException("int", global::java.lang.Integer.toString(i)); + } + + public virtual void setLong(object obj, long l) + { + throw SetIllegalArgumentException("long", global::java.lang.Long.toString(l)); + } + + public virtual void setFloat(object obj, float f) + { + throw SetIllegalArgumentException("float", global::java.lang.Float.toString(f)); + } + + public virtual void setDouble(object obj, double d) + { + throw SetIllegalArgumentException("double", global::java.lang.Double.toString(d)); + } + + public abstract object get(object obj); + public abstract void set(object obj, object value); + + private abstract class FieldAccessor : FieldAccessorImplBase + { + protected delegate void Setter(object obj, T value, FieldAccessor acc); + protected delegate T Getter(object obj, FieldAccessor acc); + private static readonly Setter initialSetter = lazySet; + private static readonly Getter initialGetter = lazyGet; + protected Setter setter = initialSetter; + protected Getter getter = initialGetter; + + internal FieldAccessor(FieldWrapper fw, bool isFinal) + : base(fw, isFinal) + { + if (!IsSlowPathCompatible(fw)) + { + // prevent slow path + numInvocations = inflationThreshold; + } + } + + private bool IsSlowPathCompatible(FieldWrapper fw) + { #if !NO_REF_EMIT - if (fw.IsVolatile && (fw.FieldTypeWrapper == PrimitiveTypeWrapper.LONG || fw.FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)) - { - return false; - } + if (fw.IsVolatile && (fw.FieldTypeWrapper == PrimitiveTypeWrapper.LONG || fw.FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)) + { + return false; + } #endif - fw.Link(); - return true; - } - - private static T lazyGet(object obj, FieldAccessor acc) - { - return acc.lazyGet(obj); - } - - private static void lazySet(object obj, T value, FieldAccessor acc) - { - acc.lazySet(obj, value); - } - - private T lazyGet(object obj) - { + fw.Link(); + return true; + } + + private static T lazyGet(object obj, FieldAccessor acc) + { + return acc.lazyGet(obj); + } + + private static void lazySet(object obj, T value, FieldAccessor acc) + { + acc.lazySet(obj, value); + } + + private T lazyGet(object obj) + { #if !NO_REF_EMIT - if (numInvocations >= inflationThreshold) - { - // FXBUG it appears that a ldsfld/stsfld in a DynamicMethod doesn't trigger the class constructor - // and if we didn't use the slow path, we haven't yet initialized the class - fw.DeclaringType.RunClassInit(); - getter = (Getter)GenerateFastGetter(typeof(Getter), typeof(T), fw); - return getter(obj, this); - } + if (numInvocations >= inflationThreshold) + { + // FXBUG it appears that a ldsfld/stsfld in a DynamicMethod doesn't trigger the class constructor + // and if we didn't use the slow path, we haven't yet initialized the class + fw.DeclaringType.RunClassInit(); + getter = (Getter)GenerateFastGetter(typeof(Getter), typeof(T), fw); + return getter(obj, this); + } #endif // !NO_REF_EMIT - if (fw.IsStatic) - { - obj = null; - } - else if (obj == null) - { - throw new global::java.lang.NullPointerException(); - } - else if (!fw.DeclaringType.IsInstance(obj)) - { - throw GetIllegalArgumentException(obj); - } - else if (fw.DeclaringType.IsRemapped && !fw.DeclaringType.TypeAsBaseType.IsInstanceOfType(obj)) - { - throw GetUnsupportedRemappedFieldException(obj); - } - if (numInvocations == 0) - { - fw.DeclaringType.RunClassInit(); - fw.DeclaringType.Finish(); - fw.ResolveField(); - } - numInvocations++; - return (T)fw.FieldTypeWrapper.GhostUnwrap(fw.GetValue(obj)); - } - - private void lazySet(object obj, T value) - { - if (isFinal) - { - // for some reason Java runs class initialization before checking if the field is final - fw.DeclaringType.RunClassInit(); - throw FinalFieldIllegalAccessException(JavaBox(value)); - } + if (fw.IsStatic) + { + obj = null; + } + else if (obj == null) + { + throw new global::java.lang.NullPointerException(); + } + else if (!fw.DeclaringType.IsInstance(obj)) + { + throw GetIllegalArgumentException(obj); + } + else if (fw.DeclaringType.IsRemapped && !fw.DeclaringType.TypeAsBaseType.IsInstanceOfType(obj)) + { + throw GetUnsupportedRemappedFieldException(obj); + } + if (numInvocations == 0) + { + fw.DeclaringType.RunClassInit(); + fw.DeclaringType.Finish(); + fw.ResolveField(); + } + numInvocations++; + return (T)fw.FieldTypeWrapper.GhostUnwrap(fw.GetValue(obj)); + } + + private void lazySet(object obj, T value) + { + if (isFinal) + { + // for some reason Java runs class initialization before checking if the field is final + fw.DeclaringType.RunClassInit(); + throw FinalFieldIllegalAccessException(JavaBox(value)); + } #if !NO_REF_EMIT - if (numInvocations >= inflationThreshold) - { - // FXBUG it appears that a ldsfld/stsfld in a DynamicMethod doesn't trigger the class constructor - // and if we didn't use the slow path, we haven't yet initialized the class - fw.DeclaringType.RunClassInit(); - setter = (Setter)GenerateFastSetter(typeof(Setter), typeof(T), fw); - setter(obj, value, this); - return; - } + if (numInvocations >= inflationThreshold) + { + // FXBUG it appears that a ldsfld/stsfld in a DynamicMethod doesn't trigger the class constructor + // and if we didn't use the slow path, we haven't yet initialized the class + fw.DeclaringType.RunClassInit(); + setter = (Setter)GenerateFastSetter(typeof(Setter), typeof(T), fw); + setter(obj, value, this); + return; + } #endif // !NO_REF_EMIT - if (fw.IsStatic) - { - obj = null; - } - else if (obj == null) - { - throw new global::java.lang.NullPointerException(); - } - else if (!fw.DeclaringType.IsInstance(obj)) - { - throw SetIllegalArgumentException(obj); - } - else if (fw.DeclaringType.IsRemapped && !fw.DeclaringType.TypeAsBaseType.IsInstanceOfType(obj)) - { - throw GetUnsupportedRemappedFieldException(obj); - } - CheckValue(value); - if (numInvocations == 0) - { - fw.DeclaringType.RunClassInit(); - fw.DeclaringType.Finish(); - fw.ResolveField(); - } - numInvocations++; - fw.SetValue(obj, fw.FieldTypeWrapper.GhostWrap(value)); - } - - private Exception GetUnsupportedRemappedFieldException(object obj) - { - return new global::java.lang.IllegalAccessException("Accessing field " + fw.DeclaringType.Name + "." + fw.Name + " in an object of type " + global::ikvm.runtime.Util.getClassFromObject(obj).getName() + " is not supported"); - } - - protected virtual void CheckValue(T value) - { - } - - protected abstract object JavaBox(T value); - } - - private sealed class ByteField : FieldAccessor - { - internal ByteField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override short getShort(object obj) - { - return (sbyte)getByte(obj); - } - - public sealed override int getInt(object obj) - { - return (sbyte)getByte(obj); - } - - public sealed override long getLong(object obj) - { - return (sbyte)getByte(obj); - } - - public sealed override float getFloat(object obj) - { - return (sbyte)getByte(obj); - } - - public sealed override double getDouble(object obj) - { - return (sbyte)getByte(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Byte.valueOf(getByte(obj)); - } - - public sealed override void set(object obj, object val) - { - if (!(val is global::java.lang.Byte)) - { - throw SetIllegalArgumentException(val); - } - setByte(obj, ((global::java.lang.Byte)val).byteValue()); - } - - public sealed override byte getByte(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setByte(object obj, byte value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(byte value) - { - return global::java.lang.Byte.valueOf(value); - } - } - - private sealed class BooleanField : FieldAccessor - { - internal BooleanField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override object get(object obj) - { - return global::java.lang.Boolean.valueOf(getBoolean(obj)); - } - - public sealed override void set(object obj, object val) - { - if (!(val is global::java.lang.Boolean)) - { - throw SetIllegalArgumentException(val); - } - setBoolean(obj, ((global::java.lang.Boolean)val).booleanValue()); - } - - public sealed override bool getBoolean(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setBoolean(object obj, bool value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(bool value) - { - return global::java.lang.Boolean.valueOf(value); - } - } - - private sealed class CharField : FieldAccessor - { - internal CharField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override int getInt(object obj) - { - return getChar(obj); - } - - public sealed override long getLong(object obj) - { - return getChar(obj); - } - - public sealed override float getFloat(object obj) - { - return getChar(obj); - } - - public sealed override double getDouble(object obj) - { - return getChar(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Character.valueOf(getChar(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Character) - setChar(obj, ((global::java.lang.Character)val).charValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override char getChar(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setChar(object obj, char value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(char value) - { - return global::java.lang.Character.valueOf(value); - } - } - - private sealed class ShortField : FieldAccessor - { - internal ShortField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override int getInt(object obj) - { - return getShort(obj); - } - - public sealed override long getLong(object obj) - { - return getShort(obj); - } - - public sealed override float getFloat(object obj) - { - return getShort(obj); - } - - public sealed override double getDouble(object obj) - { - return getShort(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Short.valueOf(getShort(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Byte - || val is global::java.lang.Short) - setShort(obj, ((global::java.lang.Number)val).shortValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override void setByte(object obj, byte b) - { - setShort(obj, (sbyte)b); - } - - public sealed override short getShort(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setShort(object obj, short value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(short value) - { - return global::java.lang.Short.valueOf(value); - } - } - - private sealed class IntField : FieldAccessor - { - internal IntField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override long getLong(object obj) - { - return getInt(obj); - } - - public sealed override float getFloat(object obj) - { - return getInt(obj); - } - - public sealed override double getDouble(object obj) - { - return getInt(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Integer.valueOf(getInt(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Byte - || val is global::java.lang.Short - || val is global::java.lang.Integer) - setInt(obj, ((global::java.lang.Number)val).intValue()); - else if (val is global::java.lang.Character) - setInt(obj, ((global::java.lang.Character)val).charValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override void setByte(object obj, byte b) - { - setInt(obj, (sbyte)b); - } - - public sealed override void setChar(object obj, char c) - { - setInt(obj, c); - } - - public sealed override void setShort(object obj, short s) - { - setInt(obj, s); - } - - public sealed override int getInt(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setInt(object obj, int value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(int value) - { - return global::java.lang.Integer.valueOf(value); - } - } - - private sealed class FloatField : FieldAccessor - { - internal FloatField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override double getDouble(object obj) - { - return getFloat(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Float.valueOf(getFloat(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Float - || val is global::java.lang.Byte - || val is global::java.lang.Short - || val is global::java.lang.Integer - || val is global::java.lang.Long) - setFloat(obj, ((global::java.lang.Number)val).floatValue()); - else if (val is global::java.lang.Character) - setFloat(obj, ((global::java.lang.Character)val).charValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override void setByte(object obj, byte b) - { - setFloat(obj, (sbyte)b); - } - - public sealed override void setChar(object obj, char c) - { - setFloat(obj, c); - } - - public sealed override void setShort(object obj, short s) - { - setFloat(obj, s); - } - - public sealed override void setInt(object obj, int i) - { - setFloat(obj, i); - } - - public sealed override void setLong(object obj, long l) - { - setFloat(obj, l); - } - - public sealed override float getFloat(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setFloat(object obj, float value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(float value) - { - return global::java.lang.Float.valueOf(value); - } - } - - private sealed class LongField : FieldAccessor - { - internal LongField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override float getFloat(object obj) - { - return getLong(obj); - } - - public sealed override double getDouble(object obj) - { - return getLong(obj); - } - - public sealed override object get(object obj) - { - return global::java.lang.Long.valueOf(getLong(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Long - || val is global::java.lang.Byte - || val is global::java.lang.Short - || val is global::java.lang.Integer) - setLong(obj, ((global::java.lang.Number)val).longValue()); - else if (val is global::java.lang.Character) - setLong(obj, ((global::java.lang.Character)val).charValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override void setByte(object obj, byte b) - { - setLong(obj, (sbyte)b); - } - - public sealed override void setChar(object obj, char c) - { - setLong(obj, c); - } - - public sealed override void setShort(object obj, short s) - { - setLong(obj, s); - } - - public sealed override void setInt(object obj, int i) - { - setLong(obj, i); - } - - public sealed override long getLong(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setLong(object obj, long value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(long value) - { - return global::java.lang.Long.valueOf(value); - } - } - - private sealed class DoubleField : FieldAccessor - { - internal DoubleField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - public sealed override object get(object obj) - { - return global::java.lang.Double.valueOf(getDouble(obj)); - } - - public sealed override void set(object obj, object val) - { - if (val is global::java.lang.Double - || val is global::java.lang.Float - || val is global::java.lang.Byte - || val is global::java.lang.Short - || val is global::java.lang.Integer - || val is global::java.lang.Long) - setDouble(obj, ((global::java.lang.Number)val).doubleValue()); - else if (val is global::java.lang.Character) - setDouble(obj, ((global::java.lang.Character)val).charValue()); - else - throw SetIllegalArgumentException(val); - } - - public sealed override void setByte(object obj, byte b) - { - setDouble(obj, (sbyte)b); - } - - public sealed override void setChar(object obj, char c) - { - setDouble(obj, c); - } - - public sealed override void setShort(object obj, short s) - { - setDouble(obj, s); - } - - public sealed override void setInt(object obj, int i) - { - setDouble(obj, i); - } - - public sealed override void setLong(object obj, long l) - { - setDouble(obj, l); - } - - public sealed override void setFloat(object obj, float f) - { - setDouble(obj, f); - } - - public sealed override double getDouble(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void setDouble(object obj, double value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(double value) - { - return global::java.lang.Double.valueOf(value); - } - } - - private sealed class ObjectField : FieldAccessor - { - internal ObjectField(FieldWrapper field, bool isFinal) - : base(field, isFinal) - { - } - - protected sealed override void CheckValue(object value) - { - if (value != null && !fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()).IsInstance(value)) - { - throw SetIllegalArgumentException(value); - } - } - - public sealed override object get(object obj) - { - try - { - return getter(obj, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - public sealed override void set(object obj, object value) - { - try - { - setter(obj, value, this); - } - catch (FieldAccessException x) - { - throw new global::java.lang.IllegalAccessException().initCause(x); - } - } - - protected sealed override object JavaBox(object value) - { - return value; - } - } + if (fw.IsStatic) + { + obj = null; + } + else if (obj == null) + { + throw new global::java.lang.NullPointerException(); + } + else if (!fw.DeclaringType.IsInstance(obj)) + { + throw SetIllegalArgumentException(obj); + } + else if (fw.DeclaringType.IsRemapped && !fw.DeclaringType.TypeAsBaseType.IsInstanceOfType(obj)) + { + throw GetUnsupportedRemappedFieldException(obj); + } + CheckValue(value); + if (numInvocations == 0) + { + fw.DeclaringType.RunClassInit(); + fw.DeclaringType.Finish(); + fw.ResolveField(); + } + numInvocations++; + fw.SetValue(obj, fw.FieldTypeWrapper.GhostWrap(value)); + } + + private Exception GetUnsupportedRemappedFieldException(object obj) + { + return new global::java.lang.IllegalAccessException("Accessing field " + fw.DeclaringType.Name + "." + fw.Name + " in an object of type " + global::ikvm.runtime.Util.getClassFromObject(obj).getName() + " is not supported"); + } + + protected virtual void CheckValue(T value) + { + } + + protected abstract object JavaBox(T value); + } + + private sealed class ByteField : FieldAccessor + { + internal ByteField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override short getShort(object obj) + { + return (sbyte)getByte(obj); + } + + public sealed override int getInt(object obj) + { + return (sbyte)getByte(obj); + } + + public sealed override long getLong(object obj) + { + return (sbyte)getByte(obj); + } + + public sealed override float getFloat(object obj) + { + return (sbyte)getByte(obj); + } + + public sealed override double getDouble(object obj) + { + return (sbyte)getByte(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Byte.valueOf(getByte(obj)); + } + + public sealed override void set(object obj, object val) + { + if (!(val is global::java.lang.Byte)) + { + throw SetIllegalArgumentException(val); + } + setByte(obj, ((global::java.lang.Byte)val).byteValue()); + } + + public sealed override byte getByte(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setByte(object obj, byte value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(byte value) + { + return global::java.lang.Byte.valueOf(value); + } + } + + private sealed class BooleanField : FieldAccessor + { + internal BooleanField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override object get(object obj) + { + return global::java.lang.Boolean.valueOf(getBoolean(obj)); + } + + public sealed override void set(object obj, object val) + { + if (!(val is global::java.lang.Boolean)) + { + throw SetIllegalArgumentException(val); + } + setBoolean(obj, ((global::java.lang.Boolean)val).booleanValue()); + } + + public sealed override bool getBoolean(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setBoolean(object obj, bool value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(bool value) + { + return global::java.lang.Boolean.valueOf(value); + } + } + + private sealed class CharField : FieldAccessor + { + internal CharField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override int getInt(object obj) + { + return getChar(obj); + } + + public sealed override long getLong(object obj) + { + return getChar(obj); + } + + public sealed override float getFloat(object obj) + { + return getChar(obj); + } + + public sealed override double getDouble(object obj) + { + return getChar(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Character.valueOf(getChar(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Character) + setChar(obj, ((global::java.lang.Character)val).charValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override char getChar(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setChar(object obj, char value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(char value) + { + return global::java.lang.Character.valueOf(value); + } + } + + private sealed class ShortField : FieldAccessor + { + internal ShortField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override int getInt(object obj) + { + return getShort(obj); + } + + public sealed override long getLong(object obj) + { + return getShort(obj); + } + + public sealed override float getFloat(object obj) + { + return getShort(obj); + } + + public sealed override double getDouble(object obj) + { + return getShort(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Short.valueOf(getShort(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Byte + || val is global::java.lang.Short) + setShort(obj, ((global::java.lang.Number)val).shortValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override void setByte(object obj, byte b) + { + setShort(obj, (sbyte)b); + } + + public sealed override short getShort(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setShort(object obj, short value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(short value) + { + return global::java.lang.Short.valueOf(value); + } + } + + private sealed class IntField : FieldAccessor + { + internal IntField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override long getLong(object obj) + { + return getInt(obj); + } + + public sealed override float getFloat(object obj) + { + return getInt(obj); + } + + public sealed override double getDouble(object obj) + { + return getInt(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Integer.valueOf(getInt(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Byte + || val is global::java.lang.Short + || val is global::java.lang.Integer) + setInt(obj, ((global::java.lang.Number)val).intValue()); + else if (val is global::java.lang.Character) + setInt(obj, ((global::java.lang.Character)val).charValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override void setByte(object obj, byte b) + { + setInt(obj, (sbyte)b); + } + + public sealed override void setChar(object obj, char c) + { + setInt(obj, c); + } + + public sealed override void setShort(object obj, short s) + { + setInt(obj, s); + } + + public sealed override int getInt(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setInt(object obj, int value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(int value) + { + return global::java.lang.Integer.valueOf(value); + } + } + + private sealed class FloatField : FieldAccessor + { + internal FloatField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override double getDouble(object obj) + { + return getFloat(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Float.valueOf(getFloat(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Float + || val is global::java.lang.Byte + || val is global::java.lang.Short + || val is global::java.lang.Integer + || val is global::java.lang.Long) + setFloat(obj, ((global::java.lang.Number)val).floatValue()); + else if (val is global::java.lang.Character) + setFloat(obj, ((global::java.lang.Character)val).charValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override void setByte(object obj, byte b) + { + setFloat(obj, (sbyte)b); + } + + public sealed override void setChar(object obj, char c) + { + setFloat(obj, c); + } + + public sealed override void setShort(object obj, short s) + { + setFloat(obj, s); + } + + public sealed override void setInt(object obj, int i) + { + setFloat(obj, i); + } + + public sealed override void setLong(object obj, long l) + { + setFloat(obj, l); + } + + public sealed override float getFloat(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setFloat(object obj, float value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(float value) + { + return global::java.lang.Float.valueOf(value); + } + } + + private sealed class LongField : FieldAccessor + { + internal LongField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override float getFloat(object obj) + { + return getLong(obj); + } + + public sealed override double getDouble(object obj) + { + return getLong(obj); + } + + public sealed override object get(object obj) + { + return global::java.lang.Long.valueOf(getLong(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Long + || val is global::java.lang.Byte + || val is global::java.lang.Short + || val is global::java.lang.Integer) + setLong(obj, ((global::java.lang.Number)val).longValue()); + else if (val is global::java.lang.Character) + setLong(obj, ((global::java.lang.Character)val).charValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override void setByte(object obj, byte b) + { + setLong(obj, (sbyte)b); + } + + public sealed override void setChar(object obj, char c) + { + setLong(obj, c); + } + + public sealed override void setShort(object obj, short s) + { + setLong(obj, s); + } + + public sealed override void setInt(object obj, int i) + { + setLong(obj, i); + } + + public sealed override long getLong(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setLong(object obj, long value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(long value) + { + return global::java.lang.Long.valueOf(value); + } + } + + private sealed class DoubleField : FieldAccessor + { + internal DoubleField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + public sealed override object get(object obj) + { + return global::java.lang.Double.valueOf(getDouble(obj)); + } + + public sealed override void set(object obj, object val) + { + if (val is global::java.lang.Double + || val is global::java.lang.Float + || val is global::java.lang.Byte + || val is global::java.lang.Short + || val is global::java.lang.Integer + || val is global::java.lang.Long) + setDouble(obj, ((global::java.lang.Number)val).doubleValue()); + else if (val is global::java.lang.Character) + setDouble(obj, ((global::java.lang.Character)val).charValue()); + else + throw SetIllegalArgumentException(val); + } + + public sealed override void setByte(object obj, byte b) + { + setDouble(obj, (sbyte)b); + } + + public sealed override void setChar(object obj, char c) + { + setDouble(obj, c); + } + + public sealed override void setShort(object obj, short s) + { + setDouble(obj, s); + } + + public sealed override void setInt(object obj, int i) + { + setDouble(obj, i); + } + + public sealed override void setLong(object obj, long l) + { + setDouble(obj, l); + } + + public sealed override void setFloat(object obj, float f) + { + setDouble(obj, f); + } + + public sealed override double getDouble(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void setDouble(object obj, double value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(double value) + { + return global::java.lang.Double.valueOf(value); + } + } + + private sealed class ObjectField : FieldAccessor + { + internal ObjectField(FieldWrapper field, bool isFinal) + : base(field, isFinal) + { + } + + protected sealed override void CheckValue(object value) + { + if (value != null && !fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()).IsInstance(value)) + { + throw SetIllegalArgumentException(value); + } + } + + public sealed override object get(object obj) + { + try + { + return getter(obj, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + public sealed override void set(object obj, object value) + { + try + { + setter(obj, value, this); + } + catch (FieldAccessException x) + { + throw new global::java.lang.IllegalAccessException().initCause(x); + } + } + + protected sealed override object JavaBox(object value) + { + return value; + } + } #if !NO_REF_EMIT - private Delegate GenerateFastGetter(Type delegateType, Type fieldType, FieldWrapper fw) - { - TypeWrapper fieldTypeWrapper; - try - { - fieldTypeWrapper = fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()); - fieldTypeWrapper.Finish(); - fw.DeclaringType.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - fw.ResolveField(); - DynamicMethod dm = DynamicMethodUtils.Create("__", fw.DeclaringType.TypeAsBaseType, !fw.IsPublic || !fw.DeclaringType.IsPublic, fieldType, new Type[] { typeof(IReflectionException), typeof(object), typeof(object) }); - CodeEmitter ilgen = CodeEmitter.Create(dm); - if (fw.IsStatic) - { - fw.EmitGet(ilgen); - fieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen); - } - else - { - ilgen.BeginExceptionBlock(); - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Castclass, fw.DeclaringType.TypeAsBaseType); - fw.EmitGet(ilgen); - fieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen); - CodeEmitterLocal local = ilgen.DeclareLocal(fieldType); - ilgen.Emit(OpCodes.Stloc, local); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitLeave(label); - ilgen.BeginCatchBlock(typeof(InvalidCastException)); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("GetIllegalArgumentException")); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Ldloc, local); - } - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return dm.CreateDelegate(delegateType, this); - } - - private Delegate GenerateFastSetter(Type delegateType, Type fieldType, FieldWrapper fw) - { - TypeWrapper fieldTypeWrapper; - try - { - fieldTypeWrapper = fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()); - fieldTypeWrapper.Finish(); - fw.DeclaringType.Finish(); - } - catch (RetargetableJavaException x) - { - throw x.ToJava(); - } - fw.ResolveField(); - DynamicMethod dm = DynamicMethodUtils.Create("__", fw.DeclaringType.TypeAsBaseType, !fw.IsPublic || !fw.DeclaringType.IsPublic, null, new Type[] { typeof(IReflectionException), typeof(object), fieldType, typeof(object) }); - CodeEmitter ilgen = CodeEmitter.Create(dm); - if (fw.IsStatic) - { - if (fieldType == typeof(object)) - { - ilgen.BeginExceptionBlock(); - ilgen.Emit(OpCodes.Ldarg_2); - fieldTypeWrapper.EmitCheckcast(ilgen); - fieldTypeWrapper.EmitConvStackTypeToSignatureType(ilgen, null); - fw.EmitSet(ilgen); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitLeave(label); - ilgen.BeginCatchBlock(typeof(InvalidCastException)); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("SetIllegalArgumentException")); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - ilgen.MarkLabel(label); - } - else - { - ilgen.Emit(OpCodes.Ldarg_2); - fw.EmitSet(ilgen); - } - } - else - { - ilgen.BeginExceptionBlock(); - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Castclass, fw.DeclaringType.TypeAsBaseType); - ilgen.Emit(OpCodes.Ldarg_2); - if (fieldType == typeof(object)) - { - fieldTypeWrapper.EmitCheckcast(ilgen); - } - fieldTypeWrapper.EmitConvStackTypeToSignatureType(ilgen, null); - fw.EmitSet(ilgen); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitLeave(label); - ilgen.BeginCatchBlock(typeof(InvalidCastException)); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("SetIllegalArgumentException")); - ilgen.Emit(OpCodes.Throw); - ilgen.EndExceptionBlock(); - ilgen.MarkLabel(label); - } - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return dm.CreateDelegate(delegateType, this); - } + private Delegate GenerateFastGetter(Type delegateType, Type fieldType, FieldWrapper fw) + { + TypeWrapper fieldTypeWrapper; + try + { + fieldTypeWrapper = fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()); + fieldTypeWrapper.Finish(); + fw.DeclaringType.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + fw.ResolveField(); + DynamicMethod dm = DynamicMethodUtil.Create("__", fw.DeclaringType.TypeAsBaseType, !fw.IsPublic || !fw.DeclaringType.IsPublic, fieldType, new Type[] { typeof(IReflectionException), typeof(object), typeof(object) }); + CodeEmitter ilgen = CodeEmitter.Create(dm); + if (fw.IsStatic) + { + fw.EmitGet(ilgen); + fieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen); + } + else + { + ilgen.BeginExceptionBlock(); + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Castclass, fw.DeclaringType.TypeAsBaseType); + fw.EmitGet(ilgen); + fieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen); + CodeEmitterLocal local = ilgen.DeclareLocal(fieldType); + ilgen.Emit(OpCodes.Stloc, local); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitLeave(label); + ilgen.BeginCatchBlock(typeof(InvalidCastException)); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("GetIllegalArgumentException")); + ilgen.Emit(OpCodes.Throw); + ilgen.EndExceptionBlock(); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Ldloc, local); + } + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return dm.CreateDelegate(delegateType, this); + } + + private Delegate GenerateFastSetter(Type delegateType, Type fieldType, FieldWrapper fw) + { + TypeWrapper fieldTypeWrapper; + try + { + fieldTypeWrapper = fw.FieldTypeWrapper.EnsureLoadable(fw.DeclaringType.GetClassLoader()); + fieldTypeWrapper.Finish(); + fw.DeclaringType.Finish(); + } + catch (RetargetableJavaException x) + { + throw x.ToJava(); + } + fw.ResolveField(); + DynamicMethod dm = DynamicMethodUtil.Create("__", fw.DeclaringType.TypeAsBaseType, !fw.IsPublic || !fw.DeclaringType.IsPublic, null, new Type[] { typeof(IReflectionException), typeof(object), fieldType, typeof(object) }); + CodeEmitter ilgen = CodeEmitter.Create(dm); + if (fw.IsStatic) + { + if (fieldType == typeof(object)) + { + ilgen.BeginExceptionBlock(); + ilgen.Emit(OpCodes.Ldarg_2); + fieldTypeWrapper.EmitCheckcast(ilgen); + fieldTypeWrapper.EmitConvStackTypeToSignatureType(ilgen, null); + fw.EmitSet(ilgen); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitLeave(label); + ilgen.BeginCatchBlock(typeof(InvalidCastException)); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("SetIllegalArgumentException")); + ilgen.Emit(OpCodes.Throw); + ilgen.EndExceptionBlock(); + ilgen.MarkLabel(label); + } + else + { + ilgen.Emit(OpCodes.Ldarg_2); + fw.EmitSet(ilgen); + } + } + else + { + ilgen.BeginExceptionBlock(); + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Castclass, fw.DeclaringType.TypeAsBaseType); + ilgen.Emit(OpCodes.Ldarg_2); + if (fieldType == typeof(object)) + { + fieldTypeWrapper.EmitCheckcast(ilgen); + } + fieldTypeWrapper.EmitConvStackTypeToSignatureType(ilgen, null); + fw.EmitSet(ilgen); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitLeave(label); + ilgen.BeginCatchBlock(typeof(InvalidCastException)); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Callvirt, typeof(IReflectionException).GetMethod("SetIllegalArgumentException")); + ilgen.Emit(OpCodes.Throw); + ilgen.EndExceptionBlock(); + ilgen.MarkLabel(label); + } + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return dm.CreateDelegate(delegateType, this); + } #endif // !NO_REF_EMIT - internal static FieldAccessorImplBase Create(FieldWrapper field, bool isFinal) - { - TypeWrapper type = field.FieldTypeWrapper; - if (type.IsPrimitive) - { - if (type == PrimitiveTypeWrapper.BYTE) - { - return new ByteField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.BOOLEAN) - { - return new BooleanField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.CHAR) - { - return new CharField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.SHORT) - { - return new ShortField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.INT) - { - return new IntField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.FLOAT) - { - return new FloatField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.LONG) - { - return new LongField(field, isFinal); - } - if (type == PrimitiveTypeWrapper.DOUBLE) - { - return new DoubleField(field, isFinal); - } - throw new InvalidOperationException("field type: " + type); - } - else - { - return new ObjectField(field, isFinal); - } - } - } + internal static FieldAccessorImplBase Create(FieldWrapper field, bool isFinal) + { + TypeWrapper type = field.FieldTypeWrapper; + if (type.IsPrimitive) + { + if (type == PrimitiveTypeWrapper.BYTE) + { + return new ByteField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.BOOLEAN) + { + return new BooleanField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.CHAR) + { + return new CharField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.SHORT) + { + return new ShortField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.INT) + { + return new IntField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.FLOAT) + { + return new FloatField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.LONG) + { + return new LongField(field, isFinal); + } + if (type == PrimitiveTypeWrapper.DOUBLE) + { + return new DoubleField(field, isFinal); + } + throw new InvalidOperationException("field type: " + type); + } + else + { + return new ObjectField(field, isFinal); + } + } + } #endif - public static global::sun.reflect.FieldAccessor newFieldAccessor(object thisFactory, global::java.lang.reflect.Field field, bool overrideAccessCheck) - { + public static global::sun.reflect.FieldAccessor newFieldAccessor(object thisFactory, global::java.lang.reflect.Field field, bool overrideAccessCheck) + { #if FIRST_PASS return null; #else - // we look at the modifiers of the Field object to allow Unsafe to give us a fake Field take doesn't have the final flag set - int modifiers = field.getModifiers(); - bool isStatic = global::java.lang.reflect.Modifier.isStatic(modifiers); - bool isFinal = global::java.lang.reflect.Modifier.isFinal(modifiers); - return FieldAccessorImplBase.Create(FieldWrapper.FromField(field), isFinal && (!overrideAccessCheck || isStatic)); + // we look at the modifiers of the Field object to allow Unsafe to give us a fake Field take doesn't have the final flag set + int modifiers = field.getModifiers(); + bool isStatic = global::java.lang.reflect.Modifier.isStatic(modifiers); + bool isFinal = global::java.lang.reflect.Modifier.isFinal(modifiers); + return FieldAccessorImplBase.Create(FieldWrapper.FromField(field), isFinal && (!overrideAccessCheck || isStatic)); #endif - } + } #if !FIRST_PASS - internal static global::sun.reflect.FieldAccessor NewFieldAccessorJNI(FieldWrapper field) - { - return FieldAccessorImplBase.Create(field, false); - } + internal static global::sun.reflect.FieldAccessor NewFieldAccessorJNI(FieldWrapper field) + { + return FieldAccessorImplBase.Create(field, false); + } #endif - public static global::sun.reflect.MethodAccessor newMethodAccessor(object thisFactory, global::java.lang.reflect.Method method) - { + public static global::sun.reflect.MethodAccessor newMethodAccessor(object thisFactory, global::java.lang.reflect.Method method) + { #if FIRST_PASS return null; #else - MethodWrapper mw = MethodWrapper.FromExecutable(method); + MethodWrapper mw = MethodWrapper.FromExecutable(method); #if !NO_REF_EMIT - if (!mw.IsDynamicOnly) - { - return new FastMethodAccessorImpl(mw); - } + if (!mw.IsDynamicOnly) + { + return new FastMethodAccessorImpl(mw); + } #endif - return new MethodAccessorImpl(mw); + return new MethodAccessorImpl(mw); #endif - } + } - public static global::sun.reflect.ConstructorAccessor newConstructorAccessor0(object thisFactory, global::java.lang.reflect.Constructor constructor) - { + public static global::sun.reflect.ConstructorAccessor newConstructorAccessor0(object thisFactory, global::java.lang.reflect.Constructor constructor) + { #if FIRST_PASS return null; #else - MethodWrapper mw = MethodWrapper.FromExecutable(constructor); - if (ActivatorConstructorAccessor.IsSuitable(mw)) - { - // we special case public default constructors, because in that case using Activator.CreateInstance() - // is almost as fast as FastConstructorAccessorImpl, but it saves us significantly in working set and - // startup time (because often during startup a sun.nio.cs.* encoder is instantiated using reflection) - return new ActivatorConstructorAccessor(mw); - } - else - { + MethodWrapper mw = MethodWrapper.FromExecutable(constructor); + if (ActivatorConstructorAccessor.IsSuitable(mw)) + { + // we special case public default constructors, because in that case using Activator.CreateInstance() + // is almost as fast as FastConstructorAccessorImpl, but it saves us significantly in working set and + // startup time (because often during startup a sun.nio.cs.* encoder is instantiated using reflection) + return new ActivatorConstructorAccessor(mw); + } + else + { #if NO_REF_EMIT return new ConstructorAccessorImpl(mw); #else - return new FastConstructorAccessorImpl(constructor); + return new FastConstructorAccessorImpl(constructor); #endif - } + } #endif - } + } - public static global::sun.reflect.ConstructorAccessor newConstructorAccessorForSerialization(global::java.lang.Class classToInstantiate, global::java.lang.reflect.Constructor constructorToCall) - { + public static global::sun.reflect.ConstructorAccessor newConstructorAccessorForSerialization(global::java.lang.Class classToInstantiate, global::java.lang.reflect.Constructor constructorToCall) + { #if FIRST_PASS return null; #else - try - { + try + { #if NO_REF_EMIT return new SerializationConstructorAccessorImpl(constructorToCall, classToInstantiate); #else - return new FastSerializationConstructorAccessorImpl(constructorToCall, classToInstantiate); + return new FastSerializationConstructorAccessorImpl(constructorToCall, classToInstantiate); #endif - } - catch (SecurityException x) - { - throw new global::java.lang.SecurityException(x.Message, global::ikvm.runtime.Util.mapException(x)); - } + } + catch (SecurityException x) + { + throw new global::java.lang.SecurityException(x.Message, global::ikvm.runtime.Util.mapException(x)); + } #endif - } + } - } + } } diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECCurve.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECCurve.cs new file mode 100644 index 0000000000..b4b491667d --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECCurve.cs @@ -0,0 +1,40 @@ +using System.Security.Cryptography; + +namespace IKVM.Java.Externs.sun.security.ec +{ + +#if NET47_OR_GREATER || NETCOREAPP +#else + + struct ECCurve + { + + public enum ECCurveType : int + { + Implicit = 0, + PrimeShortWeierstrass = 1, + PrimeTwistedEdwards = 2, + PrimeMontgomery = 3, + Characteristic2 = 4, + Named = 5, + } + + public static ECCurve CreateFromOid(Oid curveOid) + { + ECCurve curve = default; + curve.CurveType = ECCurveType.Named; + curve.Oid = curveOid; + return curve; + } + + public ECCurveType CurveType; + + public Oid Oid { get; set; } + + public bool IsNamed => CurveType == ECCurve.ECCurveType.Named; + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDHKeyAgreement.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDHKeyAgreement.cs new file mode 100644 index 0000000000..984d2c3183 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDHKeyAgreement.cs @@ -0,0 +1,85 @@ +using System; +using System.Buffers; +using System.Security.Cryptography; + +#if FIRST_PASS == false + +using java.lang; +using java.security; + +#endif + +namespace IKVM.Java.Externs.sun.security.ec +{ + + /// + /// Implements the backing native methods for 'ECDHKeyAgreement'. + /// + static class ECDHKeyAgreement + { + + readonly static RandomNumberGenerator rng = RandomNumberGenerator.Create(); + + /// + /// Implements the native method for 'deriveKey'. + /// + /// + /// + /// + /// + public static byte[] deriveKey(byte[] s, byte[] w, byte[] encodedParams) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + if (encodedParams == null) + throw new InvalidAlgorithmParameterException(); + + var curve = ECUtil.DecodeParameters(encodedParams); + if (curve.IsNamed == false) + throw new InvalidAlgorithmParameterException(); + + try + { + // read in keys + using var prv = ECUtil.ImportECDiffieHellmanPrivateKey(curve, s); + using var pub = ECUtil.ImportECDiffieHellmanPublicKey(curve, ECUtil.ImportECPoint(w)); + + // generate new seed + var seed = (byte[])null; + try + { + seed = ArrayPool.Shared.Rent(64); + rng.GetBytes(seed); + +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + var secret = prv.DeriveKeyTls(pub, Array.Empty(), seed); +#else + ((ECDiffieHellmanCng)prv).Label = Array.Empty(); + ((ECDiffieHellmanCng)prv).Seed = seed; + ((ECDiffieHellmanCng)prv).HashAlgorithm = ECUtil.GetCurveCngAlgorithm(curve, ECUtil.ECMode.ECDH); + ((ECDiffieHellmanCng)prv).KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Tls; + var secret = prv.DeriveKeyMaterial(pub); +#endif + return secret; + } + finally + { + if (seed != null) + ArrayPool.Shared.Return(seed); + } + } + catch (CryptographicException e) + { + throw new KeyException(e); + } + catch (PlatformNotSupportedException e) + { + throw new IllegalStateException(e); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDSASignature.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDSASignature.cs new file mode 100644 index 0000000000..3be068c250 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECDSASignature.cs @@ -0,0 +1,102 @@ +using System; +using System.Security.Cryptography; + +#if FIRST_PASS == false + +using java.lang; +using java.security; + +#endif + +namespace IKVM.Java.Externs.sun.security.ec +{ + + /// + /// Implements the backing native methods for 'ECDSASignature'. + /// + static class ECDSASignature + { + + /// + /// Implements the native method for 'signDigest'. + /// + /// + /// + /// + /// + /// + public static byte[] signDigest(byte[] digest, byte[] s, byte[] encodedParams, byte[] seed) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var curve = ECUtil.DecodeParameters(encodedParams); + if (curve.IsNamed == false) + throw new InvalidAlgorithmParameterException(); + + try + { + using var dsa = ECUtil.ImportECDsaPrivateKey(curve, s); + var r = dsa.SignHash(digest); + return r; + } + catch (CryptographicException e) + { + throw new IllegalStateException(e); + } +#endif + } + + /// + /// Implements the native method for 'verifySignedDigest'. + /// + /// + /// + /// + /// + /// + public static bool verifySignedDigest(byte[] signature, byte[] digest, byte[] w, byte[] encodedParams) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var curve = ECUtil.DecodeParameters(encodedParams); + if (curve.IsNamed == false) + throw new InvalidAlgorithmParameterException(); + + // lookup key size as we may need to pad the signature + var keySize = ECUtil.GetCurveKeySize(curve); + if (keySize == 0) + throw new InvalidAlgorithmParameterException(); + + // signature is r and s concatinated + var keyLength = (keySize + 7) >> 3; + var sigLength = signature.Length / 2; + if (sigLength != keyLength) + { + var r = ECUtil.NormalizeLength((ReadOnlySpan)signature.AsSpan().Slice(0, sigLength), keyLength); + var s = ECUtil.NormalizeLength((ReadOnlySpan)signature.AsSpan().Slice(sigLength, sigLength), keyLength); + + // copy to appropriately sized new signature + var tmp = new byte[r.Length + s.Length]; + r.CopyTo(tmp); + s.CopyTo(tmp.AsSpan().Slice(keyLength)); + signature = tmp; + } + + try + { + using var dsa = ECUtil.ImportECDsaPublicKey(curve, w); + var result = dsa.VerifyHash(digest, signature); + return result; + } + catch (CryptographicException e) + { + throw new IllegalStateException(e); + } +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECKeyPairGenerator.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECKeyPairGenerator.cs new file mode 100644 index 0000000000..d8185bf160 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECKeyPairGenerator.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +#if FIRST_PASS == false + +using java.lang; +using java.security; + +#endif + +namespace IKVM.Java.Externs.sun.security.ec +{ + + /// + /// Implements the backing native methods for 'ECKeyPairGenerator'. + /// + static class ECKeyPairGenerator + { + + /// + /// Implements the native method for 'generateECKeyPair'. + /// + /// + /// + /// + /// + public static object[] generateECKeyPair(int keySize, byte[] encodedParams, byte[] seed) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + var curve = ECUtil.DecodeParameters(encodedParams); + if (curve.IsNamed == false) + throw new InvalidAlgorithmParameterException(); + +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + try + { + // create a new ECDsa instance, set parameters, and generate key; then export results + using var dsa = ECDsa.Create(); + dsa.KeySize = keySize; + dsa.GenerateKey(curve); + var p = dsa.ExportParameters(true); + + // result is a two item array, with the private key followed by exported ECPoint + return new object[] { p.D, ECUtil.ExportECPoint(p.Q) }; + } + catch (CryptographicException e) + { + throw new KeyException(e); + } + catch (PlatformNotSupportedException e) + { + throw new IllegalStateException(e); + } +#else + + try + { + var algorithm = ECUtil.GetCurveCngAlgorithm(curve, ECUtil.ECMode.ECDsa); + if (algorithm == null) + throw new InvalidAlgorithmParameterException(); + + // generate a new CNG key + var key = CngKey.Create(algorithm, null, new CngKeyCreationParameters() { ExportPolicy = CngExportPolicies.AllowPlaintextExport }); + var src = key.Export(CngKeyBlobFormat.EccPrivateBlob).AsSpan(); + + // read the key magic and validate + if (MemoryMarshal.Read(src.Slice(0, sizeof(uint))) != ECUtil.GetCurveBCryptPrivateMagic(curve, ECUtil.ECMode.ECDsa)) + throw new KeyException(); + + // read the key length, which is in bytes + var keyLength = (int)MemoryMarshal.Read(src.Slice(sizeof(uint), sizeof(uint))); + + // ECPoint structure is uncompressed, followed by X and Y, which start at the beginning of the BLOB + var q = new byte[1 + keyLength + keyLength]; + var w = q.AsSpan(); + w[0] = ECUtil.EC_POINT_FORM_UNCOMPRESSED; + src.Slice(sizeof(uint) + sizeof(uint), keyLength).CopyTo(w.Slice(1, keyLength)); + src.Slice(sizeof(uint) + sizeof(uint) + keyLength, keyLength).CopyTo(w.Slice(1 + keyLength, keyLength)); + + // private key starts after that + var d = new byte[keyLength]; + src.Slice(sizeof(uint) + sizeof(uint) + keyLength + keyLength, keyLength).CopyTo(d); + + // result is a two item array + return new object[] { d, q }; + } + catch (CryptographicException e) + { + throw new KeyException(e); + } + catch (PlatformNotSupportedException e) + { + throw new IllegalStateException(e); + } +#endif +#endif + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECPoint.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECPoint.cs new file mode 100644 index 0000000000..93f6df4142 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECPoint.cs @@ -0,0 +1,17 @@ +namespace IKVM.Java.Externs.sun.security.ec +{ + +#if NET47_OR_GREATER || NETCOREAPP +#else + + struct ECPoint + { + + public byte[] X; + public byte[] Y; + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECUtil.cs b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECUtil.cs new file mode 100644 index 0000000000..330a6ccffd --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/ec/ECUtil.cs @@ -0,0 +1,430 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +#if FIRST_PASS == false + +using java.lang; +using java.security; + +#endif + +namespace IKVM.Java.Externs.sun.security.ec +{ + + /// + /// Utilities for working with eliptical curve cryptography. + /// + internal static class ECUtil + { + + public const byte SEC_ASN1_OBJECT_ID = 0x06; + public const int EC_POINT_FORM_UNCOMPRESSED = 0x04; + public const int ANSI_X962_CURVE_OID_TOTAL_LEN = 10; + public const int SECG_CURVE_OID_TOTAL_LEN = 7; + + public const uint BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345; + public const uint BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345; + public const uint BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345; + public const uint BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345; + public const uint BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345; + public const uint BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345; + + public const uint BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345; + public const uint BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345; + public const uint BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345; + public const uint BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345; + public const uint BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345; + public const uint BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345; + +#if FIRST_PASS == false + + + /// + /// Creates a instance for the given curve and private key material. + /// + /// + /// + /// + /// + public static ECDsa ImportECDsaPrivateKey(ECCurve curve, byte[] s) + { + // lookup key size as we may need to prefix s + var keySize = GetCurveKeySize(curve); + if (keySize == 0) + throw new InvalidAlgorithmParameterException(); + + // incoming key needs to be proper length + var keyLength = (keySize + 7) >> 3; + s = NormalizeLength(s, keyLength); + +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + + // NET Core 3.1 does not support an empty Q + // However, on Windows, a fake Q the length of D works + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var X = new byte[s.Length]; + var Y = X; + return ECDsa.Create(new ECParameters() { Curve = curve, D = s, Q = new ECPoint() { X = X, Y = Y } }); + } + else + { + // fail on linux and mac os for now + throw new PlatformNotSupportedException(); + } +#else + var algorithm = GetCurveCngAlgorithm(curve, ECMode.ECDsa); + if (algorithm == null) + throw new InvalidAlgorithmParameterException(); + + var magic = GetCurveBCryptPrivateMagic(curve, ECMode.ECDsa); + + // CNG blob: magic + key size + X + Y + D + var blob = new byte[sizeof(uint) + sizeof(uint) + keyLength + keyLength + keyLength]; + var span = blob.AsSpan(); + MemoryMarshal.Write(span.Slice(0, sizeof(uint)), ref magic); + MemoryMarshal.Write(span.Slice(sizeof(uint), sizeof(uint)), ref keyLength); + span.Slice(sizeof(uint) + sizeof(uint), keyLength).Fill(0); + span.Slice(sizeof(uint) + sizeof(uint) + keyLength, keyLength).Fill(0); + s.CopyTo(span.Slice(sizeof(uint) + sizeof(uint) + keyLength + keyLength, keyLength)); + + // import blob and wrap with CNG + var key = CngKey.Import(blob, CngKeyBlobFormat.EccPrivateBlob); + var ech = new ECDsaCng(key); + + return ech; +#endif + } + + /// + /// Creates a instance for the given curve and private key material. + /// + /// + /// + /// + /// + public static ECDsa ImportECDsaPublicKey(ECCurve curve, byte[] w) + { + // lookup key size + var keySize = GetCurveKeySize(curve); + if (keySize == 0) + throw new InvalidAlgorithmParameterException(); + +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + return ECDsa.Create(new ECParameters { Curve = curve, Q = ImportECPoint(w) }); +#else + // properties of the key + var keyLength = (keySize + 7) >> 3; + var magic = GetCurveBCryptPublicMagic(curve, ECMode.ECDsa); + var q = ImportECPoint(w); + + // CNG blob: magic + key size + X + Y + var blob = new byte[sizeof(uint) + sizeof(uint) + keyLength + keyLength]; + var span = blob.AsSpan(); + MemoryMarshal.Write(span.Slice(0, sizeof(uint)), ref magic); + MemoryMarshal.Write(span.Slice(sizeof(uint), sizeof(uint)), ref keyLength); + q.X.CopyTo(span.Slice(sizeof(uint) + sizeof(uint), keyLength)); + q.Y.CopyTo(span.Slice(sizeof(uint) + sizeof(uint) + keyLength, keyLength)); + + // import blob and wrap with CNG + using var key = CngKey.Import(blob, CngKeyBlobFormat.EccPublicBlob); + var ech = new ECDsaCng(key); + return ech; +#endif + } + + /// + /// Creates a instance for the given curve and private key material. + /// + /// + /// + /// + /// + public static ECDiffieHellman ImportECDiffieHellmanPrivateKey(ECCurve curve, byte[] s) + { + // lookup key size as we may need to prefix s + var keySize = GetCurveKeySize(curve); + if (keySize == 0) + throw new InvalidAlgorithmParameterException(); + + // incoming key needs to be proper length + var keyLength = (keySize + 7) >> 3; + s = NormalizeLength(s, keyLength); + +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + // NET Core 3.1 does not support an empty Q + // However, on Windows, a fake Q the length of D works + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var X = new byte[s.Length]; + var Y = X; + return ECDiffieHellman.Create(new ECParameters() { Curve = curve, D = s, Q = new ECPoint() { X = X, Y = Y } }); + } + else + { + // fail on linux and mac os for now + throw new PlatformNotSupportedException(); + } +#else + var magic = GetCurveBCryptPrivateMagic(curve, ECMode.ECDH); + + // CNG blob: magic + key size + X + Y + D + var blob = new byte[sizeof(uint) + sizeof(uint) + keyLength + keyLength + keyLength]; + var span = blob.AsSpan(); + MemoryMarshal.Write(span.Slice(0, sizeof(uint)), ref magic); + MemoryMarshal.Write(span.Slice(sizeof(uint), sizeof(uint)), ref keyLength); + span.Slice(sizeof(uint) + sizeof(uint), keyLength).Fill(0); + span.Slice(sizeof(uint) + sizeof(uint) + keyLength, keyLength).Fill(0); + s.CopyTo(span.Slice(sizeof(uint) + sizeof(uint) + keyLength + keyLength, keyLength)); + + // import blob and wrap with CNG + var key = CngKey.Import(blob, CngKeyBlobFormat.EccPrivateBlob); + var ech = new ECDiffieHellmanCng(key); + + return ech; +#endif + } + + /// + /// Creates a new for the given curve and public key material. + /// + /// + /// + /// + public static ECDiffieHellmanPublicKey ImportECDiffieHellmanPublicKey(ECCurve curve, ECPoint q) + { +#if NET47_OR_GREATER || NETCOREAPP3_1_OR_GREATER + return ECDiffieHellman.Create(new ECParameters() { Curve = curve, Q = q }).PublicKey; +#else + // properties of the key + var keyLength = q.X.Length; + var magic = GetCurveBCryptPublicMagic(curve, ECMode.ECDH); + + // CNG blob: magic + key size + X + Y + var blob = new byte[sizeof(uint) + sizeof(uint) + keyLength + keyLength]; + var span = blob.AsSpan(); + MemoryMarshal.Write(span.Slice(0, sizeof(uint)), ref magic); + MemoryMarshal.Write(span.Slice(sizeof(uint), sizeof(uint)), ref keyLength); + q.X.CopyTo(span.Slice(sizeof(uint) + sizeof(uint), keyLength)); + q.Y.CopyTo(span.Slice(sizeof(uint) + sizeof(uint) + keyLength, keyLength)); + + return ECDiffieHellmanCngPublicKey.FromByteArray(blob, CngKeyBlobFormat.EccPublicBlob); +#endif + } + + /// + /// Exports an in uncompressed form. + /// + /// + /// + public static byte[] ExportECPoint(ECPoint p) + { + // public key result needs to be prefixed with point form + var b = new byte[1 + p.X.Length + p.Y.Length]; + b[0] = EC_POINT_FORM_UNCOMPRESSED; + p.X.CopyTo(((Span)b).Slice(1, p.X.Length)); + p.Y.CopyTo(((Span)b).Slice(1 + p.X.Length, p.Y.Length)); + return b; + } + + /// + /// Imports an . + /// + /// + /// + /// + public static ECPoint ImportECPoint(ReadOnlySpan w) + { + // format of point + var format = (int)w[0]; + if (format != EC_POINT_FORM_UNCOMPRESSED) + throw new InvalidAlgorithmParameterException(); + + // skip format, calculate x and y length + var W = w.Slice(1); + var l = W.Length / 2; + + // read X and Y + var x = W.Slice(0, l).ToArray(); + var y = W.Slice(l, l).ToArray(); + return new ECPoint() { X = x, Y = y }; + } + + /// + /// Decodes the specified encoded parameters. + /// + /// + /// + /// + public static ECCurve DecodeParameters(byte[] encodedParams) + { + if (encodedParams == null) + throw new InvalidAlgorithmParameterException(); + if (encodedParams.Length < 2) + throw new InvalidAlgorithmParameterException(); + + if (encodedParams.Length != ANSI_X962_CURVE_OID_TOTAL_LEN && + encodedParams.Length != SECG_CURVE_OID_TOTAL_LEN) + throw new InvalidAlgorithmParameterException(); + + if (encodedParams[0] != SEC_ASN1_OBJECT_ID) + throw new InvalidAlgorithmParameterException(); + + var oid = ParseOid(((ReadOnlySpan)encodedParams).Slice(2)); + if (oid == null || oid.FriendlyName == null) + throw new InvalidAlgorithmParameterException(); + +#if NET47_OR_GREATER + return ECCurve.CreateFromValue(oid.Value); +#else + return ECCurve.CreateFromOid(oid); +#endif + } + + /// + /// Parses the OID value in ASN1 format. This method can be replaced on later versions of .NET. + /// + /// + /// + /// + public static Oid ParseOid(ReadOnlySpan raw) + { + // parse the byte array into a set of individual items + var u = new List(8) { (uint)(raw[0] / 40), (uint)(raw[0] % 40) }; + var b = (uint)0; + for (var i = 1; i < raw.Length; i++) + { + if ((raw[i] & 0x80) == 0) + { + u.Add(raw[i] + (b << 7)); + b = 0; + } + else + { + b <<= 7; + b += (uint)(raw[i] & 0x7F); + } + } + + // convert to string format + var r = new System.Text.StringBuilder(u[0].ToString(CultureInfo.InvariantCulture)); + for (var k = 1; k < u.Count; k++) + r.Append(".").Append(u[k].ToString(CultureInfo.InvariantCulture)); + + // parse string format with built-in OID class, which has no raw constructor + return new Oid(r.ToString()); + } + +#endif + + /// + /// CNG has different values for ECDSA and ECDH modes. + /// + public enum ECMode + { + + ECDsa, + ECDH, + + } + + /// + /// Returns a value for the specified curve. + /// + /// + /// + /// + /// + public static CngAlgorithm GetCurveCngAlgorithm(ECCurve curve, ECMode mode) => curve.Oid.FriendlyName.ToLowerInvariant() switch + { + "nistp256" or "ecdsa_p256" => mode == ECMode.ECDH ? CngAlgorithm.ECDiffieHellmanP256 : CngAlgorithm.ECDsaP256, + "nistp384" or "ecdsa_p384" => mode == ECMode.ECDH ? CngAlgorithm.ECDiffieHellmanP384 : CngAlgorithm.ECDsaP384, + "nistp521" or "ecdsa_p521" => mode == ECMode.ECDH ? CngAlgorithm.ECDiffieHellmanP521 : CngAlgorithm.ECDsaP521, + _ => null, + }; + + /// + /// Returns the BCRYPT magic value for a private key of the specified curve. + /// + /// + /// + /// + /// + public static uint GetCurveBCryptPrivateMagic(ECCurve curve, ECMode mode) => curve.Oid.FriendlyName.ToLowerInvariant() switch + { + "nistp256" or "ecdsa_p256" => mode == ECMode.ECDH ? BCRYPT_ECDH_PRIVATE_P256_MAGIC : BCRYPT_ECDSA_PRIVATE_P256_MAGIC, + "nistp384" or "ecdsa_p384" => mode == ECMode.ECDH ? BCRYPT_ECDH_PRIVATE_P384_MAGIC : BCRYPT_ECDSA_PRIVATE_P384_MAGIC, + "nistp521" or "ecdsa_p521" => mode == ECMode.ECDH ? BCRYPT_ECDH_PRIVATE_P521_MAGIC : BCRYPT_ECDSA_PRIVATE_P521_MAGIC, + _ => 0, + }; + + /// + /// Returns the BCRYPT magic value for a public key of the specified curve. + /// + /// + /// + /// + /// + public static uint GetCurveBCryptPublicMagic(ECCurve curve, ECMode mode) => curve.Oid.FriendlyName.ToLowerInvariant() switch + { + "nistp256" or "ecdsa_p256" => mode == ECMode.ECDH ? BCRYPT_ECDH_PUBLIC_P256_MAGIC : BCRYPT_ECDSA_PUBLIC_P256_MAGIC, + "nistp384" or "ecdsa_p384" => mode == ECMode.ECDH ? BCRYPT_ECDH_PUBLIC_P384_MAGIC : BCRYPT_ECDSA_PUBLIC_P384_MAGIC, + "nistp521" or "ecdsa_p521" => mode == ECMode.ECDH ? BCRYPT_ECDH_PUBLIC_P521_MAGIC : BCRYPT_ECDSA_PUBLIC_P521_MAGIC, + _ => 0, + }; + + /// + /// Returns a value for the specified curve name. + /// + /// + /// + /// + public static int GetCurveKeySize(ECCurve curve) => curve.Oid.FriendlyName.ToLowerInvariant() switch + { + "nistp256" or "ecdsa_p256" => 256, + "nistp384" or "ecdsa_p384" => 384, + "nistp521" or "ecdsa_p521" => 521, + _ => 0, + }; + + /// + /// Trims or expands an array to match the specified length. + /// + /// + /// + /// + internal static byte[] NormalizeLength(byte[] s, int length) + { + return NormalizeLength(s.AsSpan(), length).ToArray(); + } + + /// + /// Trims or expands an array to match the specified length. + /// + /// + /// + /// + internal static ReadOnlySpan NormalizeLength(ReadOnlySpan s, int length) + { + if (s.Length > length) + { + // trim start + s = s.Slice(s.Length - length); + } + else if (s.Length < length) + { + // rebuild longer + var t = new byte[length]; + s.CopyTo(t.AsSpan().Slice(length - s.Length)); + s = t; + } + + return s; + } + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativePRNG.cs b/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativePRNG.cs new file mode 100644 index 0000000000..3d7159b782 --- /dev/null +++ b/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativePRNG.cs @@ -0,0 +1,55 @@ +using System; +using System.Security.Cryptography; + +namespace IKVM.Java.Externs.sun.security.provider +{ + + /// + /// Implements the native backend for 'NativePRNG'. This implementation provides seeds using , and then progressive random values using . + /// + static class NativePRNG + { + + static RandomNumberGenerator rng = RandomNumberGenerator.Create(); + static Random rnd = new Random(); + + /// + /// Implements the native method 'engineSetSeed'. This method initializes the pseudo random generator with the given seed value. + /// + /// + /// + public static void engineSetSeed(object self, byte[] seed) + { + int s = 0; + for (int i = 0; i < seed.Length; i++) + s ^= seed[i]; + + rnd = new Random(s); + } + + /// + /// Implements the native method 'engineNextBytes'. This method gets a number of pseudo random bytes. + /// + /// + /// + public static void engineNextBytes(object self, byte[] bytes) + { + rnd.NextBytes(bytes); + } + + /// + /// Implements the native method 'engineGenerateSeed'. This method generates a number of true random bytes. + /// + /// + /// + /// + public static byte[] engineGenerateSeed(object self, int numBytes) + { + var b = new byte[numBytes]; + rng.GetBytes(b); + return b; + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativeSeedGenerator.cs b/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativeSeedGenerator.cs index b1d02d417c..43c4e31600 100644 --- a/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativeSeedGenerator.cs +++ b/src/IKVM.Runtime/Java/Externs/sun/security/provider/NativeSeedGenerator.cs @@ -1,52 +1,27 @@ -/* - Copyright (C) 2007-2015 Jeroen Frijters - Copyright (C) 2009 Volker Berlin (i-net software) - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System.Security.Cryptography; +using System.Security.Cryptography; namespace IKVM.Java.Externs.sun.security.provider { + /// + /// Implements the native backend for 'NativeSeedGenerator'. This implementation uses the .NET RandomNumberGenerator. + /// static class NativeSeedGenerator { - public static bool nativeGenerateSeed(byte[] result) + static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + + /// + /// Implements the native method 'getSeedBytes'. + /// + /// + /// + /// + public static void getSeedBytes(object self, byte[] result) { - try - { - RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider(); - csp.GetBytes(result); -#if NET_4_0 - csp.Dispose(); -#endif - return true; - } - catch (CryptographicException) - { - return false; - } + rng.GetBytes(result); } } -} \ No newline at end of file +} diff --git a/src/IKVM.Runtime/JavaException.cs b/src/IKVM.Runtime/JavaException.cs index 8380535f3b..cac9d2f213 100644 --- a/src/IKVM.Runtime/JavaException.cs +++ b/src/IKVM.Runtime/JavaException.cs @@ -55,7 +55,7 @@ internal static string Format(string s, object[] args) return args == null || args.Length == 0 ? s : string.Format(s, args); } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal abstract Exception ToJava(); @@ -90,7 +90,7 @@ internal ClassLoadingException(Exception innerException, string className) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -127,7 +127,7 @@ internal LinkageError(string message, Exception innerException) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -170,7 +170,7 @@ internal VerifyError(string message, Exception innerException) : base(message, i } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -194,7 +194,7 @@ internal ClassNotFoundException(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -218,7 +218,7 @@ internal ClassCircularityError(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -242,7 +242,7 @@ internal NoClassDefFoundError(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -266,7 +266,7 @@ internal IncompatibleClassChangeError(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -290,7 +290,7 @@ internal IllegalAccessError(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { return new java.lang.IllegalAccessError(Message); @@ -314,7 +314,7 @@ internal ClassFormatError(string message, params object[] args) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -338,7 +338,7 @@ internal UnsupportedClassVersionError(string message) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { @@ -362,7 +362,7 @@ internal JavaSecurityException(string msg) : } -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR +#if !IMPORTER && !FIRST_PASS && !EXPORTER internal override Exception ToJava() { diff --git a/src/IKVM.Runtime/LambdaMetafactory.cs b/src/IKVM.Runtime/LambdaMetafactory.cs index 04e3fe145d..7be514de22 100644 --- a/src/IKVM.Runtime/LambdaMetafactory.cs +++ b/src/IKVM.Runtime/LambdaMetafactory.cs @@ -24,1012 +24,1024 @@ Jeroen Frijters using System; using System.Collections.Generic; using System.Diagnostics; -#if STATIC_COMPILER + +using IKVM.Attributes; +using IKVM.ByteCode; +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; #else using System.Reflection; using System.Reflection.Emit; + #endif namespace IKVM.Internal { - sealed class LambdaMetafactory - { - private MethodBuilder ctor; - internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile classFile, int constantPoolIndex, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen) - { - ClassFile.BootstrapMethod bsm = classFile.GetBootstrapMethod(cpi.BootstrapMethod); - if (!IsLambdaMetafactory(classFile, bsm) && !IsLambdaAltMetafactory(classFile, bsm)) - { - return false; - } - LambdaMetafactory lmf = context.GetValue(constantPoolIndex); - if (lmf.ctor == null && !lmf.EmitImpl(context, classFile, cpi, bsm, ilgen)) - { -#if STATIC_COMPILER + sealed class LambdaMetafactory + { + + private MethodBuilder ctor; + + internal static bool Emit(DynamicTypeWrapper.FinishContext context, ClassFile classFile, int constantPoolIndex, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen) + { + ClassFile.BootstrapMethod bsm = classFile.GetBootstrapMethod(cpi.BootstrapMethod); + if (!IsLambdaMetafactory(classFile, bsm) && !IsLambdaAltMetafactory(classFile, bsm)) + { + return false; + } + LambdaMetafactory lmf = context.GetValue(constantPoolIndex); + if (lmf.ctor == null && !lmf.EmitImpl(context, classFile, cpi, bsm, ilgen)) + { +#if IMPORTER if (context.TypeWrapper.GetClassLoader().DisableDynamicBinding) { StaticCompiler.IssueMessage(Message.UnableToCreateLambdaFactory); } #endif - return false; - } - ilgen.Emit(OpCodes.Newobj, lmf.ctor); - // the CLR verification rules about type merging mean we have to explicitly cast to the interface type here - ilgen.Emit(OpCodes.Castclass, cpi.GetRetType().TypeAsBaseType); - return true; - } + return false; + } + ilgen.Emit(OpCodes.Newobj, lmf.ctor); + // the CLR verification rules about type merging mean we have to explicitly cast to the interface type here + ilgen.Emit(OpCodes.Castclass, cpi.GetRetType().TypeAsBaseType); + return true; + } - private bool EmitImpl(DynamicTypeWrapper.FinishContext context, ClassFile classFile, ClassFile.ConstantPoolItemInvokeDynamic cpi, ClassFile.BootstrapMethod bsm, CodeEmitter ilgen) - { - if (HasUnloadable(cpi)) - { - Fail("cpi has unloadable"); - return false; - } - bool serializable = false; - TypeWrapper[] markers = TypeWrapper.EmptyArray; - ClassFile.ConstantPoolItemMethodType[] bridges = null; - if (bsm.ArgumentCount > 3) - { - AltFlags flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3)); - serializable = (flags & AltFlags.Serializable) != 0; - int argpos = 4; - if ((flags & AltFlags.Markers) != 0) - { - markers = new TypeWrapper[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))]; - for (int i = 0; i < markers.Length; i++) - { - if ((markers[i] = classFile.GetConstantPoolClassType(bsm.GetArgument(argpos++))).IsUnloadable) - { - Fail("unloadable marker"); - return false; - } - } - } - if ((flags & AltFlags.Bridges) != 0) - { - bridges = new ClassFile.ConstantPoolItemMethodType[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))]; - for (int i = 0; i < bridges.Length; i++) - { - bridges[i] = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(argpos++)); - if (HasUnloadable(bridges[i])) - { - Fail("unloadable bridge"); - return false; - } - } - } - } - ClassFile.ConstantPoolItemMethodType samMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(0)); - ClassFile.ConstantPoolItemMethodHandle implMethod = classFile.GetConstantPoolConstantMethodHandle(bsm.GetArgument(1)); - ClassFile.ConstantPoolItemMethodType instantiatedMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(2)); - if (HasUnloadable(samMethodType) - || HasUnloadable((ClassFile.ConstantPoolItemMI)implMethod.MemberConstantPoolItem) - || HasUnloadable(instantiatedMethodType)) - { - Fail("bsm args has unloadable"); - return false; - } - TypeWrapper interfaceType = cpi.GetRetType(); - MethodWrapper[] methodList; - if (!CheckSupportedInterfaces(context.TypeWrapper, interfaceType, markers, bridges, out methodList)) - { - Fail("unsupported interface"); - return false; - } - if (serializable && Array.Exists(methodList, delegate(MethodWrapper mw) { return mw.Name == "writeReplace" && mw.Signature == "()Ljava.lang.Object;"; })) - { - Fail("writeReplace"); - return false; - } - if (!IsSupportedImplMethod(implMethod, context.TypeWrapper, cpi.GetArgTypes(), instantiatedMethodType)) - { - Fail("implMethod " + implMethod.MemberConstantPoolItem.Class + "::" + implMethod.MemberConstantPoolItem.Name + implMethod.MemberConstantPoolItem.Signature); - return false; - } - TypeWrapper[] implParameters = GetImplParameters(implMethod); - CheckConstraints(instantiatedMethodType, samMethodType, cpi.GetArgTypes(), implParameters); - if (bridges != null) - { - foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges) - { - if (bridge.Signature == samMethodType.Signature) - { - Fail("bridge signature matches sam"); - return false; - } - if (!CheckConstraints(instantiatedMethodType, bridge, cpi.GetArgTypes(), implParameters)) - { - Fail("bridge constraints"); - return false; - } - } - } - if (instantiatedMethodType.GetRetType() != PrimitiveTypeWrapper.VOID) - { - TypeWrapper Rt = instantiatedMethodType.GetRetType(); - TypeWrapper Ra = GetImplReturnType(implMethod); - if (Ra == PrimitiveTypeWrapper.VOID || !IsAdaptable(Ra, Rt, true)) - { - Fail("The return type Rt is void, or the return type Ra is not void and is adaptable to Rt"); - return false; - } - } - MethodWrapper interfaceMethod = null; - List methods = new List(); - foreach (MethodWrapper mw in methodList) - { - if (mw.Name == cpi.Name && mw.Signature == samMethodType.Signature) - { - interfaceMethod = mw; - methods.Add(mw); - } - else if (mw.IsAbstract && !IsObjectMethod(mw)) - { - methods.Add(mw); - } - } - if (interfaceMethod == null || !interfaceMethod.IsAbstract || IsObjectMethod(interfaceMethod) || !MatchSignatures(interfaceMethod, samMethodType)) - { - Fail("interfaceMethod"); - return false; - } + private bool EmitImpl(DynamicTypeWrapper.FinishContext context, ClassFile classFile, ClassFile.ConstantPoolItemInvokeDynamic cpi, ClassFile.BootstrapMethod bsm, CodeEmitter ilgen) + { + if (HasUnloadable(cpi)) + { + Fail("cpi has unloadable"); + return false; + } + bool serializable = false; + TypeWrapper[] markers = TypeWrapper.EmptyArray; + ClassFile.ConstantPoolItemMethodType[] bridges = null; + if (bsm.ArgumentCount > 3) + { + AltFlags flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3)); + serializable = (flags & AltFlags.Serializable) != 0; + int argpos = 4; + if ((flags & AltFlags.Markers) != 0) + { + markers = new TypeWrapper[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))]; + for (int i = 0; i < markers.Length; i++) + { + if ((markers[i] = classFile.GetConstantPoolClassType(bsm.GetArgument(argpos++))).IsUnloadable) + { + Fail("unloadable marker"); + return false; + } + } + } + if ((flags & AltFlags.Bridges) != 0) + { + bridges = new ClassFile.ConstantPoolItemMethodType[classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++))]; + for (int i = 0; i < bridges.Length; i++) + { + bridges[i] = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(argpos++)); + if (HasUnloadable(bridges[i])) + { + Fail("unloadable bridge"); + return false; + } + } + } + } + ClassFile.ConstantPoolItemMethodType samMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(0)); + ClassFile.ConstantPoolItemMethodHandle implMethod = classFile.GetConstantPoolConstantMethodHandle(bsm.GetArgument(1)); + ClassFile.ConstantPoolItemMethodType instantiatedMethodType = classFile.GetConstantPoolConstantMethodType(bsm.GetArgument(2)); + if (HasUnloadable(samMethodType) + || HasUnloadable((ClassFile.ConstantPoolItemMI)implMethod.MemberConstantPoolItem) + || HasUnloadable(instantiatedMethodType)) + { + Fail("bsm args has unloadable"); + return false; + } + TypeWrapper interfaceType = cpi.GetRetType(); + MethodWrapper[] methodList; + if (!CheckSupportedInterfaces(context.TypeWrapper, interfaceType, markers, bridges, out methodList)) + { + Fail("unsupported interface"); + return false; + } + if (serializable && Array.Exists(methodList, delegate (MethodWrapper mw) { return mw.Name == "writeReplace" && mw.Signature == "()Ljava.lang.Object;"; })) + { + Fail("writeReplace"); + return false; + } + if (!IsSupportedImplMethod(implMethod, context.TypeWrapper, cpi.GetArgTypes(), instantiatedMethodType)) + { + Fail("implMethod " + implMethod.MemberConstantPoolItem.Class + "::" + implMethod.MemberConstantPoolItem.Name + implMethod.MemberConstantPoolItem.Signature); + return false; + } + TypeWrapper[] implParameters = GetImplParameters(implMethod); + CheckConstraints(instantiatedMethodType, samMethodType, cpi.GetArgTypes(), implParameters); + if (bridges != null) + { + foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges) + { + if (bridge.Signature == samMethodType.Signature) + { + Fail("bridge signature matches sam"); + return false; + } + if (!CheckConstraints(instantiatedMethodType, bridge, cpi.GetArgTypes(), implParameters)) + { + Fail("bridge constraints"); + return false; + } + } + } + if (instantiatedMethodType.GetRetType() != PrimitiveTypeWrapper.VOID) + { + TypeWrapper Rt = instantiatedMethodType.GetRetType(); + TypeWrapper Ra = GetImplReturnType(implMethod); + if (Ra == PrimitiveTypeWrapper.VOID || !IsAdaptable(Ra, Rt, true)) + { + Fail("The return type Rt is void, or the return type Ra is not void and is adaptable to Rt"); + return false; + } + } + MethodWrapper interfaceMethod = null; + List methods = new List(); + foreach (MethodWrapper mw in methodList) + { + if (mw.Name == cpi.Name && mw.Signature == samMethodType.Signature) + { + interfaceMethod = mw; + methods.Add(mw); + } + else if (mw.IsAbstract && !IsObjectMethod(mw)) + { + methods.Add(mw); + } + } + if (interfaceMethod == null || !interfaceMethod.IsAbstract || IsObjectMethod(interfaceMethod) || !MatchSignatures(interfaceMethod, samMethodType)) + { + Fail("interfaceMethod"); + return false; + } - TypeBuilder tb = context.DefineAnonymousClass(); - // we're not implementing the interfaces recursively (because we don't care about .NET Compact anymore), - // but should we decide to do that, we'd need to somehow communicate to AnonymousTypeWrapper what the 'real' interface is - tb.AddInterfaceImplementation(interfaceType.TypeAsBaseType); - if (serializable && Array.IndexOf(markers, CoreClasses.java.io.Serializable.Wrapper) == -1) - { - tb.AddInterfaceImplementation(CoreClasses.java.io.Serializable.Wrapper.TypeAsBaseType); - } - foreach (TypeWrapper marker in markers) - { - tb.AddInterfaceImplementation(marker.TypeAsBaseType); - } - ctor = CreateConstructorAndDispatch(context, cpi, tb, methods, implParameters, samMethodType, implMethod, instantiatedMethodType, serializable); - AddDefaultInterfaceMethods(context, methodList, tb); - return true; - } + TypeBuilder tb = context.DefineAnonymousClass(); + // we're not implementing the interfaces recursively (because we don't care about .NET Compact anymore), + // but should we decide to do that, we'd need to somehow communicate to AnonymousTypeWrapper what the 'real' interface is + tb.AddInterfaceImplementation(interfaceType.TypeAsBaseType); + if (serializable && Array.IndexOf(markers, CoreClasses.java.io.Serializable.Wrapper) == -1) + { + tb.AddInterfaceImplementation(CoreClasses.java.io.Serializable.Wrapper.TypeAsBaseType); + } + foreach (TypeWrapper marker in markers) + { + tb.AddInterfaceImplementation(marker.TypeAsBaseType); + } + ctor = CreateConstructorAndDispatch(context, cpi, tb, methods, implParameters, samMethodType, implMethod, instantiatedMethodType, serializable); + AddDefaultInterfaceMethods(context, methodList, tb); + return true; + } - [Conditional("TRACE_LAMBDA_METAFACTORY")] - private static void Fail(string msg) - { - Console.WriteLine("Fail: " + msg); - } + [Conditional("TRACE_LAMBDA_METAFACTORY")] + private static void Fail(string msg) + { + Console.WriteLine("Fail: " + msg); + } - private static bool CheckConstraints(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType methodType, TypeWrapper[] args, TypeWrapper[] implParameters) - { - if (!IsSubTypeOf(instantiatedMethodType, methodType)) - { - Fail("instantiatedMethodType <= methodType"); - return false; - } - if (args.Length + methodType.GetArgTypes().Length != implParameters.Length) - { - Fail("K + N = M"); - return false; - } - for (int i = 0, K = args.Length; i < K; i++) - { - if (args[i] == implParameters[i]) - { - // ok - } - else if (args[i].IsPrimitive || implParameters[i].IsPrimitive || !args[i].IsSubTypeOf(implParameters[i])) - { - Fail("For i=1..K, Di = Ai"); - return false; - } - } - for (int i = 0, N = methodType.GetArgTypes().Length, k = args.Length; i < N; i++) - { - if (!IsAdaptable(instantiatedMethodType.GetArgTypes()[i], implParameters[i + k], false)) - { - Fail("For i=1..N, Ti is adaptable to Aj, where j=i+k"); - return false; - } - } - return true; - } + private static bool CheckConstraints(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType methodType, TypeWrapper[] args, TypeWrapper[] implParameters) + { + if (!IsSubTypeOf(instantiatedMethodType, methodType)) + { + Fail("instantiatedMethodType <= methodType"); + return false; + } + if (args.Length + methodType.GetArgTypes().Length != implParameters.Length) + { + Fail("K + N = M"); + return false; + } + for (int i = 0, K = args.Length; i < K; i++) + { + if (args[i] == implParameters[i]) + { + // ok + } + else if (args[i].IsPrimitive || implParameters[i].IsPrimitive || !args[i].IsSubTypeOf(implParameters[i])) + { + Fail("For i=1..K, Di = Ai"); + return false; + } + } + for (int i = 0, N = methodType.GetArgTypes().Length, k = args.Length; i < N; i++) + { + if (!IsAdaptable(instantiatedMethodType.GetArgTypes()[i], implParameters[i + k], false)) + { + Fail("For i=1..N, Ti is adaptable to Aj, where j=i+k"); + return false; + } + } + return true; + } - private static TypeWrapper[] GetImplParameters(ClassFile.ConstantPoolItemMethodHandle implMethod) - { - MethodWrapper mw = (MethodWrapper)implMethod.Member; - TypeWrapper[] parameters = mw.GetParameters(); - if (mw.IsStatic || mw.IsConstructor) - { - return parameters; - } - return ArrayUtil.Concat(mw.DeclaringType, parameters); - } + private static TypeWrapper[] GetImplParameters(ClassFile.ConstantPoolItemMethodHandle implMethod) + { + MethodWrapper mw = (MethodWrapper)implMethod.Member; + TypeWrapper[] parameters = mw.GetParameters(); + if (mw.IsStatic || mw.IsConstructor) + { + return parameters; + } + return ArrayUtil.Concat(mw.DeclaringType, parameters); + } - private static TypeWrapper GetImplReturnType(ClassFile.ConstantPoolItemMethodHandle implMethod) - { - return implMethod.Kind == ClassFile.RefKind.newInvokeSpecial - ? implMethod.Member.DeclaringType - : ((MethodWrapper)implMethod.Member).ReturnType; - } + private static TypeWrapper GetImplReturnType(ClassFile.ConstantPoolItemMethodHandle implMethod) + { + return implMethod.Kind == ReferenceKind.NewInvokeSpecial + ? implMethod.Member.DeclaringType + : ((MethodWrapper)implMethod.Member).ReturnType; + } - private static bool IsAdaptable(TypeWrapper Q, TypeWrapper S, bool isReturn) - { - if (Q == S) - { - return true; - } + private static bool IsAdaptable(TypeWrapper Q, TypeWrapper S, bool isReturn) + { + if (Q == S) + { + return true; + } - if (Q.IsPrimitive) - { - if (S.IsPrimitive) - { - // Q can be converted to S via a primitive widening conversion - switch (Q.SigName[0] | S.SigName[0] << 8) - { - case 'B' | 'S' << 8: - case 'B' | 'I' << 8: - case 'B' | 'J' << 8: - case 'B' | 'F' << 8: - case 'B' | 'D' << 8: - case 'S' | 'I' << 8: - case 'S' | 'J' << 8: - case 'S' | 'F' << 8: - case 'S' | 'D' << 8: - case 'C' | 'I' << 8: - case 'C' | 'J' << 8: - case 'C' | 'F' << 8: - case 'C' | 'D' << 8: - case 'I' | 'J' << 8: - case 'I' | 'F' << 8: - case 'I' | 'D' << 8: - case 'J' | 'F' << 8: - case 'J' | 'D' << 8: - case 'F' | 'D' << 8: - return true; - default: - return false; - } - } - else - { - // S is a supertype of the Wrapper(Q) - return GetWrapper(Q).IsAssignableTo(S); - } - } - else if (isReturn) - { - return true; - } - else - { - if (S.IsPrimitive) - { - // If Q is a primitive wrapper, check that Primitive(Q) can be widened to S - TypeWrapper primitive = GetPrimitiveFromWrapper(Q); - return primitive != null && IsAdaptable(primitive, S, isReturn); - } - else - { - // for parameter types: S is a supertype of Q - return Q.IsAssignableTo(S); - } - } - } + if (Q.IsPrimitive) + { + if (S.IsPrimitive) + { + // Q can be converted to S via a primitive widening conversion + switch (Q.SigName[0] | S.SigName[0] << 8) + { + case 'B' | 'S' << 8: + case 'B' | 'I' << 8: + case 'B' | 'J' << 8: + case 'B' | 'F' << 8: + case 'B' | 'D' << 8: + case 'S' | 'I' << 8: + case 'S' | 'J' << 8: + case 'S' | 'F' << 8: + case 'S' | 'D' << 8: + case 'C' | 'I' << 8: + case 'C' | 'J' << 8: + case 'C' | 'F' << 8: + case 'C' | 'D' << 8: + case 'I' | 'J' << 8: + case 'I' | 'F' << 8: + case 'I' | 'D' << 8: + case 'J' | 'F' << 8: + case 'J' | 'D' << 8: + case 'F' | 'D' << 8: + return true; + default: + return false; + } + } + else + { + // S is a supertype of the Wrapper(Q) + return GetWrapper(Q).IsAssignableTo(S); + } + } + else if (isReturn) + { + return true; + } + else + { + if (S.IsPrimitive) + { + // If Q is a primitive wrapper, check that Primitive(Q) can be widened to S + TypeWrapper primitive = GetPrimitiveFromWrapper(Q); + return primitive != null && IsAdaptable(primitive, S, isReturn); + } + else + { + // for parameter types: S is a supertype of Q + return Q.IsAssignableTo(S); + } + } + } - private static TypeWrapper GetWrapper(TypeWrapper primitive) - { - Debug.Assert(primitive.IsPrimitive); - switch (primitive.SigName[0]) - { - case 'Z': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Boolean"); - case 'B': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Byte"); - case 'S': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Short"); - case 'C': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Character"); - case 'I': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Integer"); - case 'J': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Long"); - case 'F': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Float"); - case 'D': - return ClassLoaderWrapper.LoadClassCritical("java.lang.Double"); - default: - throw new InvalidOperationException(); - } - } + private static TypeWrapper GetWrapper(TypeWrapper primitive) + { + Debug.Assert(primitive.IsPrimitive); + switch (primitive.SigName[0]) + { + case 'Z': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Boolean"); + case 'B': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Byte"); + case 'S': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Short"); + case 'C': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Character"); + case 'I': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Integer"); + case 'J': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Long"); + case 'F': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Float"); + case 'D': + return ClassLoaderWrapper.LoadClassCritical("java.lang.Double"); + default: + throw new InvalidOperationException(); + } + } - private static TypeWrapper GetPrimitiveFromWrapper(TypeWrapper wrapper) - { - switch (wrapper.Name) - { - case "java.lang.Boolean": - return PrimitiveTypeWrapper.BOOLEAN; - case "java.lang.Byte": - return PrimitiveTypeWrapper.BYTE; - case "java.lang.Short": - return PrimitiveTypeWrapper.SHORT; - case "java.lang.Character": - return PrimitiveTypeWrapper.CHAR; - case "java.lang.Integer": - return PrimitiveTypeWrapper.INT; - case "java.lang.Long": - return PrimitiveTypeWrapper.LONG; - case "java.lang.Float": - return PrimitiveTypeWrapper.FLOAT; - case "java.lang.Double": - return PrimitiveTypeWrapper.DOUBLE; - default: - return null; - } - } + private static TypeWrapper GetPrimitiveFromWrapper(TypeWrapper wrapper) + { + switch (wrapper.Name) + { + case "java.lang.Boolean": + return PrimitiveTypeWrapper.BOOLEAN; + case "java.lang.Byte": + return PrimitiveTypeWrapper.BYTE; + case "java.lang.Short": + return PrimitiveTypeWrapper.SHORT; + case "java.lang.Character": + return PrimitiveTypeWrapper.CHAR; + case "java.lang.Integer": + return PrimitiveTypeWrapper.INT; + case "java.lang.Long": + return PrimitiveTypeWrapper.LONG; + case "java.lang.Float": + return PrimitiveTypeWrapper.FLOAT; + case "java.lang.Double": + return PrimitiveTypeWrapper.DOUBLE; + default: + return null; + } + } - private static bool IsSubTypeOf(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType samMethodType) - { - TypeWrapper[] T = instantiatedMethodType.GetArgTypes(); - TypeWrapper[] U = samMethodType.GetArgTypes(); - if (T.Length != U.Length) - { - return false; - } - for (int i = 0; i < T.Length; i++) - { - if (!T[i].IsAssignableTo(U[i])) - { - return false; - } - } - TypeWrapper Rt = instantiatedMethodType.GetRetType(); - TypeWrapper Ru = samMethodType.GetRetType(); - return Rt.IsAssignableTo(Ru); - } + private static bool IsSubTypeOf(ClassFile.ConstantPoolItemMethodType instantiatedMethodType, ClassFile.ConstantPoolItemMethodType samMethodType) + { + TypeWrapper[] T = instantiatedMethodType.GetArgTypes(); + TypeWrapper[] U = samMethodType.GetArgTypes(); + if (T.Length != U.Length) + { + return false; + } + for (int i = 0; i < T.Length; i++) + { + if (!T[i].IsAssignableTo(U[i])) + { + return false; + } + } + TypeWrapper Rt = instantiatedMethodType.GetRetType(); + TypeWrapper Ru = samMethodType.GetRetType(); + return Rt.IsAssignableTo(Ru); + } - private static MethodBuilder CreateConstructorAndDispatch(DynamicTypeWrapper.FinishContext context, ClassFile.ConstantPoolItemInvokeDynamic cpi, TypeBuilder tb, - List methods, TypeWrapper[] implParameters, ClassFile.ConstantPoolItemMethodType samMethodType, ClassFile.ConstantPoolItemMethodHandle implMethod, - ClassFile.ConstantPoolItemMethodType instantiatedMethodType, bool serializable) - { - TypeWrapper[] args = cpi.GetArgTypes(); + private static MethodBuilder CreateConstructorAndDispatch(DynamicTypeWrapper.FinishContext context, ClassFile.ConstantPoolItemInvokeDynamic cpi, TypeBuilder tb, + List methods, TypeWrapper[] implParameters, ClassFile.ConstantPoolItemMethodType samMethodType, ClassFile.ConstantPoolItemMethodHandle implMethod, + ClassFile.ConstantPoolItemMethodType instantiatedMethodType, bool serializable) + { + TypeWrapper[] args = cpi.GetArgTypes(); - // captured values - Type[] capturedTypes = new Type[args.Length]; - FieldBuilder[] capturedFields = new FieldBuilder[capturedTypes.Length]; - for (int i = 0; i < capturedTypes.Length; i++) - { - capturedTypes[i] = args[i].TypeAsSignatureType; - FieldAttributes attr = FieldAttributes.Private; - if (i > 0 || !args[0].IsGhost) - { - attr |= FieldAttributes.InitOnly; - } - capturedFields[i] = tb.DefineField("arg$" + (i + 1), capturedTypes[i], attr); - } + // captured values + Type[] capturedTypes = new Type[args.Length]; + FieldBuilder[] capturedFields = new FieldBuilder[capturedTypes.Length]; + for (int i = 0; i < capturedTypes.Length; i++) + { + capturedTypes[i] = args[i].TypeAsSignatureType; + FieldAttributes attr = FieldAttributes.Private; + if (i > 0 || !args[0].IsGhost) + { + attr |= FieldAttributes.InitOnly; + } + capturedFields[i] = tb.DefineField("arg$" + (i + 1), capturedTypes[i], attr); + } - // constructor - MethodBuilder ctor = ReflectUtil.DefineConstructor(tb, MethodAttributes.Assembly, capturedTypes); - CodeEmitter ilgen = CodeEmitter.Create(ctor); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Call, Types.Object.GetConstructor(Type.EmptyTypes)); - for (int i = 0; i < capturedTypes.Length; i++) - { - ilgen.EmitLdarg(0); - ilgen.EmitLdarg(i + 1); - ilgen.Emit(OpCodes.Stfld, capturedFields[i]); - } - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); + // constructor + MethodBuilder ctor = ReflectUtil.DefineConstructor(tb, MethodAttributes.Assembly, capturedTypes); + CodeEmitter ilgen = CodeEmitter.Create(ctor); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Call, Types.Object.GetConstructor(Type.EmptyTypes)); + for (int i = 0; i < capturedTypes.Length; i++) + { + ilgen.EmitLdarg(0); + ilgen.EmitLdarg(i + 1); + ilgen.Emit(OpCodes.Stfld, capturedFields[i]); + } + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); - // dispatch methods - foreach (MethodWrapper mw in methods) - { - EmitDispatch(context, args, tb, mw, implParameters, implMethod, instantiatedMethodType, capturedFields); - } + // dispatch methods + foreach (MethodWrapper mw in methods) + { + EmitDispatch(context, args, tb, mw, implParameters, implMethod, instantiatedMethodType, capturedFields); + } - // writeReplace method - if (serializable) - { - MethodBuilder writeReplace = tb.DefineMethod("writeReplace", MethodAttributes.Private, Types.Object, Type.EmptyTypes); - ilgen = CodeEmitter.Create(writeReplace); - context.TypeWrapper.EmitClassLiteral(ilgen); - ilgen.Emit(OpCodes.Ldstr, cpi.GetRetType().Name.Replace('.', '/')); - ilgen.Emit(OpCodes.Ldstr, cpi.Name); - ilgen.Emit(OpCodes.Ldstr, samMethodType.Signature.Replace('.', '/')); - ilgen.EmitLdc_I4((int)implMethod.Kind); - ilgen.Emit(OpCodes.Ldstr, implMethod.Class.Replace('.', '/')); - ilgen.Emit(OpCodes.Ldstr, implMethod.Name); - ilgen.Emit(OpCodes.Ldstr, implMethod.Signature.Replace('.', '/')); - ilgen.Emit(OpCodes.Ldstr, instantiatedMethodType.Signature.Replace('.', '/')); - ilgen.EmitLdc_I4(capturedFields.Length); - ilgen.Emit(OpCodes.Newarr, Types.Object); - for (int i = 0; i < capturedFields.Length; i++) - { - ilgen.Emit(OpCodes.Dup); - ilgen.EmitLdc_I4(i); - ilgen.EmitLdarg(0); - ilgen.Emit(OpCodes.Ldfld, capturedFields[i]); - if (args[i].IsPrimitive) - { - Boxer.EmitBox(ilgen, args[i]); - } - else if (args[i].IsGhost) - { - args[i].EmitConvSignatureTypeToStackType(ilgen); - } - ilgen.Emit(OpCodes.Stelem, Types.Object); - } - MethodWrapper ctorSerializedLambda = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.SerializedLambda").GetMethodWrapper(StringConstants.INIT, - "(Ljava.lang.Class;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;ILjava.lang.String;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;[Ljava.lang.Object;)V", false); - ctorSerializedLambda.Link(); - ctorSerializedLambda.EmitNewobj(ilgen); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); + // writeReplace method + if (serializable) + { + MethodBuilder writeReplace = tb.DefineMethod("writeReplace", MethodAttributes.Private, Types.Object, Type.EmptyTypes); + ilgen = CodeEmitter.Create(writeReplace); + context.TypeWrapper.EmitClassLiteral(ilgen); + ilgen.Emit(OpCodes.Ldstr, cpi.GetRetType().Name.Replace('.', '/')); + ilgen.Emit(OpCodes.Ldstr, cpi.Name); + ilgen.Emit(OpCodes.Ldstr, samMethodType.Signature.Replace('.', '/')); + ilgen.EmitLdc_I4((int)implMethod.Kind); + ilgen.Emit(OpCodes.Ldstr, implMethod.Class.Replace('.', '/')); + ilgen.Emit(OpCodes.Ldstr, implMethod.Name); + ilgen.Emit(OpCodes.Ldstr, implMethod.Signature.Replace('.', '/')); + ilgen.Emit(OpCodes.Ldstr, instantiatedMethodType.Signature.Replace('.', '/')); + ilgen.EmitLdc_I4(capturedFields.Length); + ilgen.Emit(OpCodes.Newarr, Types.Object); + for (int i = 0; i < capturedFields.Length; i++) + { + ilgen.Emit(OpCodes.Dup); + ilgen.EmitLdc_I4(i); + ilgen.EmitLdarg(0); + ilgen.Emit(OpCodes.Ldfld, capturedFields[i]); + if (args[i].IsPrimitive) + { + Boxer.EmitBox(ilgen, args[i]); + } + else if (args[i].IsGhost) + { + args[i].EmitConvSignatureTypeToStackType(ilgen); + } + ilgen.Emit(OpCodes.Stelem, Types.Object); + } + MethodWrapper ctorSerializedLambda = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.SerializedLambda").GetMethodWrapper(StringConstants.INIT, + "(Ljava.lang.Class;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;ILjava.lang.String;Ljava.lang.String;Ljava.lang.String;Ljava.lang.String;[Ljava.lang.Object;)V", false); + ctorSerializedLambda.Link(); + ctorSerializedLambda.EmitNewobj(ilgen); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); - if (!context.TypeWrapper.GetClassLoader().NoAutomagicSerialization) - { - // add .NET serialization interop support - Serialization.MarkSerializable(tb); - Serialization.AddGetObjectData(tb); - } - } + if (!context.TypeWrapper.GetClassLoader().NoAutomagicSerialization) + { + // add .NET serialization interop support + Serialization.MarkSerializable(tb); + Serialization.AddGetObjectData(tb); + } + } - return ctor; - } + return ctor; + } - private static void EmitDispatch(DynamicTypeWrapper.FinishContext context, TypeWrapper[] args, TypeBuilder tb, MethodWrapper interfaceMethod, TypeWrapper[] implParameters, - ClassFile.ConstantPoolItemMethodHandle implMethod, ClassFile.ConstantPoolItemMethodType instantiatedMethodType, FieldBuilder[] capturedFields) - { - MethodBuilder mb = interfaceMethod.GetDefineMethodHelper().DefineMethod(context.TypeWrapper, tb, interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final); - if (interfaceMethod.Name != interfaceMethod.RealName) - { - tb.DefineMethodOverride(mb, (MethodInfo)interfaceMethod.GetMethod()); - } - CodeEmitter ilgen = CodeEmitter.Create(mb); - for (int i = 0; i < capturedFields.Length; i++) - { - ilgen.EmitLdarg(0); - OpCode opc = OpCodes.Ldfld; - if (i == 0 && args[0].IsGhost) - { - switch (implMethod.Kind) - { - case ClassFile.RefKind.invokeInterface: - case ClassFile.RefKind.invokeVirtual: - case ClassFile.RefKind.invokeSpecial: - opc = OpCodes.Ldflda; - break; - } - } - ilgen.Emit(opc, capturedFields[i]); - } - for (int i = 0, count = interfaceMethod.GetParameters().Length, k = capturedFields.Length; i < count; i++) - { - ilgen.EmitLdarg(i + 1); - TypeWrapper Ui = interfaceMethod.GetParameters()[i]; - TypeWrapper Ti = instantiatedMethodType.GetArgTypes()[i]; - TypeWrapper Aj = implParameters[i + k]; - if (Ui == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Conv_I1); - } - if (Ti != Ui) - { - if (Ti.IsGhost) - { - Ti.EmitConvStackTypeToSignatureType(ilgen, Ui); - } - else if (Ui.IsGhost) - { - Ui.EmitConvSignatureTypeToStackType(ilgen); - } - else - { - Ti.EmitCheckcast(ilgen); - } - } - if (Ti != Aj) - { - if (Ti.IsPrimitive && !Aj.IsPrimitive) - { - Boxer.EmitBox(ilgen, Ti); - } - else if (!Ti.IsPrimitive && Aj.IsPrimitive) - { - TypeWrapper primitive = GetPrimitiveFromWrapper(Ti); - Boxer.EmitUnbox(ilgen, primitive, false); - if (primitive == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Conv_I1); - } - } - else if (Aj == PrimitiveTypeWrapper.LONG) - { - ilgen.Emit(OpCodes.Conv_I8); - } - else if (Aj == PrimitiveTypeWrapper.FLOAT) - { - ilgen.Emit(OpCodes.Conv_R4); - } - else if (Aj == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Conv_R8); - } - } - } - switch (implMethod.Kind) - { - case ClassFile.RefKind.invokeVirtual: - case ClassFile.RefKind.invokeInterface: - ((MethodWrapper)implMethod.Member).EmitCallvirt(ilgen); - break; - case ClassFile.RefKind.newInvokeSpecial: - ((MethodWrapper)implMethod.Member).EmitNewobj(ilgen); - break; - case ClassFile.RefKind.invokeStatic: - case ClassFile.RefKind.invokeSpecial: - ((MethodWrapper)implMethod.Member).EmitCall(ilgen); - break; - default: - throw new InvalidOperationException(); - } - TypeWrapper Ru = interfaceMethod.ReturnType; - TypeWrapper Ra = GetImplReturnType(implMethod); - TypeWrapper Rt = instantiatedMethodType.GetRetType(); - if (Ra == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Conv_I1); - } - if (Ra != Ru) - { - if (Ru == PrimitiveTypeWrapper.VOID) - { - ilgen.Emit(OpCodes.Pop); - } - else if (Ra.IsGhost) - { - Ra.EmitConvSignatureTypeToStackType(ilgen); - } - else if (Ru.IsGhost) - { - Ru.EmitConvStackTypeToSignatureType(ilgen, Ra); - } - } - if (Ra != Rt) - { - if (Rt.IsPrimitive) - { - if (Rt == PrimitiveTypeWrapper.VOID) - { - // already popped - } - else if (!Ra.IsPrimitive) - { - TypeWrapper primitive = GetPrimitiveFromWrapper(Ra); - if (primitive != null) - { - Boxer.EmitUnbox(ilgen, primitive, false); - } - else - { - // If Q is not a primitive wrapper, cast Q to the base Wrapper(S); for example Number for numeric types - EmitConvertingUnbox(ilgen, Rt); - } - } - else if (Rt == PrimitiveTypeWrapper.LONG) - { - ilgen.Emit(OpCodes.Conv_I8); - } - else if (Rt == PrimitiveTypeWrapper.FLOAT) - { - ilgen.Emit(OpCodes.Conv_R4); - } - else if (Rt == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Conv_R8); - } - } - else if (Ra.IsPrimitive) - { - TypeWrapper tw = GetPrimitiveFromWrapper(Rt); - if (tw == null) - { - tw = Ra; - } - Boxer.EmitBox(ilgen, tw); - } - else - { - Rt.EmitCheckcast(ilgen); - } - } - ilgen.EmitTailCallPrevention(); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - } + private static void EmitDispatch(DynamicTypeWrapper.FinishContext context, TypeWrapper[] args, TypeBuilder tb, MethodWrapper interfaceMethod, TypeWrapper[] implParameters, + ClassFile.ConstantPoolItemMethodHandle implMethod, ClassFile.ConstantPoolItemMethodType instantiatedMethodType, FieldBuilder[] capturedFields) + { + MethodBuilder mb = interfaceMethod.GetDefineMethodHelper().DefineMethod(context.TypeWrapper, tb, interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final); + if (interfaceMethod.Name != interfaceMethod.RealName) + { + tb.DefineMethodOverride(mb, (MethodInfo)interfaceMethod.GetMethod()); + } - private static void EmitConvertingUnbox(CodeEmitter ilgen, TypeWrapper tw) - { - switch (tw.SigName[0]) - { - case 'Z': - case 'C': - Boxer.EmitUnbox(ilgen, tw, true); - break; - case 'B': - EmitUnboxNumber(ilgen, "byteValue", "()B"); - break; - case 'S': - EmitUnboxNumber(ilgen, "shortValue", "()S"); - break; - case 'I': - EmitUnboxNumber(ilgen, "intValue", "()I"); - break; - case 'J': - EmitUnboxNumber(ilgen, "longValue", "()J"); - break; - case 'F': - EmitUnboxNumber(ilgen, "floatValue", "()F"); - break; - case 'D': - EmitUnboxNumber(ilgen, "doubleValue", "()D"); - break; - default: - throw new InvalidOperationException(); - } - } + AttributeHelper.HideFromJava(mb); - private static void EmitUnboxNumber(CodeEmitter ilgen, string methodName, string methodSig) - { - TypeWrapper tw = ClassLoaderWrapper.LoadClassCritical("java.lang.Number"); - tw.EmitCheckcast(ilgen); - MethodWrapper mw = tw.GetMethodWrapper(methodName, methodSig, false); - mw.Link(); - mw.EmitCallvirt(ilgen); - } + CodeEmitter ilgen = CodeEmitter.Create(mb); + for (int i = 0; i < capturedFields.Length; i++) + { + ilgen.EmitLdarg(0); + OpCode opc = OpCodes.Ldfld; + if (i == 0 && args[0].IsGhost) + { + switch (implMethod.Kind) + { + case ReferenceKind.InvokeInterface: + case ReferenceKind.InvokeVirtual: + case ReferenceKind.InvokeSpecial: + opc = OpCodes.Ldflda; + break; + } + } + ilgen.Emit(opc, capturedFields[i]); + } + for (int i = 0, count = interfaceMethod.GetParameters().Length, k = capturedFields.Length; i < count; i++) + { + ilgen.EmitLdarg(i + 1); + TypeWrapper Ui = interfaceMethod.GetParameters()[i]; + TypeWrapper Ti = instantiatedMethodType.GetArgTypes()[i]; + TypeWrapper Aj = implParameters[i + k]; + if (Ui == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Conv_I1); + } + if (Ti != Ui) + { + if (Ti.IsGhost) + { + Ti.EmitConvStackTypeToSignatureType(ilgen, Ui); + } + else if (Ui.IsGhost) + { + Ui.EmitConvSignatureTypeToStackType(ilgen); + } + else + { + Ti.EmitCheckcast(ilgen); + } + } + if (Ti != Aj) + { + if (Ti.IsPrimitive && !Aj.IsPrimitive) + { + Boxer.EmitBox(ilgen, Ti); + } + else if (!Ti.IsPrimitive && Aj.IsPrimitive) + { + TypeWrapper primitive = GetPrimitiveFromWrapper(Ti); + Boxer.EmitUnbox(ilgen, primitive, false); + if (primitive == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Conv_I1); + } + } + else if (Aj == PrimitiveTypeWrapper.LONG) + { + ilgen.Emit(OpCodes.Conv_I8); + } + else if (Aj == PrimitiveTypeWrapper.FLOAT) + { + ilgen.Emit(OpCodes.Conv_R4); + } + else if (Aj == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Conv_R8); + } + } + } + switch (implMethod.Kind) + { + case ReferenceKind.InvokeVirtual: + case ReferenceKind.InvokeInterface: + ((MethodWrapper)implMethod.Member).EmitCallvirt(ilgen); + break; + case ReferenceKind.NewInvokeSpecial: + ((MethodWrapper)implMethod.Member).EmitNewobj(ilgen); + break; + case ReferenceKind.InvokeStatic: + case ReferenceKind.InvokeSpecial: + ((MethodWrapper)implMethod.Member).EmitCall(ilgen); + break; + default: + throw new InvalidOperationException(); + } + TypeWrapper Ru = interfaceMethod.ReturnType; + TypeWrapper Ra = GetImplReturnType(implMethod); + TypeWrapper Rt = instantiatedMethodType.GetRetType(); + if (Ra == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Conv_I1); + } + if (Ra != Ru) + { + if (Ru == PrimitiveTypeWrapper.VOID) + { + ilgen.Emit(OpCodes.Pop); + } + else if (Ra.IsGhost) + { + Ra.EmitConvSignatureTypeToStackType(ilgen); + } + else if (Ru.IsGhost) + { + Ru.EmitConvStackTypeToSignatureType(ilgen, Ra); + } + } + if (Ra != Rt) + { + if (Rt.IsPrimitive) + { + if (Rt == PrimitiveTypeWrapper.VOID) + { + // already popped + } + else if (!Ra.IsPrimitive) + { + TypeWrapper primitive = GetPrimitiveFromWrapper(Ra); + if (primitive != null) + { + Boxer.EmitUnbox(ilgen, primitive, false); + } + else + { + // If Q is not a primitive wrapper, cast Q to the base Wrapper(S); for example Number for numeric types + EmitConvertingUnbox(ilgen, Rt); + } + } + else if (Rt == PrimitiveTypeWrapper.LONG) + { + ilgen.Emit(OpCodes.Conv_I8); + } + else if (Rt == PrimitiveTypeWrapper.FLOAT) + { + ilgen.Emit(OpCodes.Conv_R4); + } + else if (Rt == PrimitiveTypeWrapper.DOUBLE) + { + ilgen.Emit(OpCodes.Conv_R8); + } + } + else if (Ra.IsPrimitive) + { + TypeWrapper tw = GetPrimitiveFromWrapper(Rt); + if (tw == null) + { + tw = Ra; + } + Boxer.EmitBox(ilgen, tw); + } + else + { + Rt.EmitCheckcast(ilgen); + } + } + ilgen.EmitTailCallPrevention(); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + } - private static void AddDefaultInterfaceMethods(DynamicTypeWrapper.FinishContext context, MethodWrapper[] methodList, TypeBuilder tb) - { - // we use special name to hide these from Java reflection - const MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName; - TypeWrapperFactory factory = context.TypeWrapper.GetClassLoader().GetTypeWrapperFactory(); - foreach (MethodWrapper mw in methodList) - { - if (!mw.IsAbstract) - { - MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr); - if (mw.Name != mw.RealName) - { - tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); - } - DynamicTypeWrapper.FinishContext.EmitCallDefaultInterfaceMethod(mb, mw); - } - else if (IsObjectMethod(mw)) - { - MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr); - if (mw.Name != mw.RealName) - { - tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); - } - CodeEmitter ilgen = CodeEmitter.Create(mb); - for (int i = 0, count = mw.GetParameters().Length; i <= count; i++) - { - ilgen.EmitLdarg(i); - } - CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false).EmitCallvirt(ilgen); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - } - } - } + private static void EmitConvertingUnbox(CodeEmitter ilgen, TypeWrapper tw) + { + switch (tw.SigName[0]) + { + case 'Z': + case 'C': + Boxer.EmitUnbox(ilgen, tw, true); + break; + case 'B': + EmitUnboxNumber(ilgen, "byteValue", "()B"); + break; + case 'S': + EmitUnboxNumber(ilgen, "shortValue", "()S"); + break; + case 'I': + EmitUnboxNumber(ilgen, "intValue", "()I"); + break; + case 'J': + EmitUnboxNumber(ilgen, "longValue", "()J"); + break; + case 'F': + EmitUnboxNumber(ilgen, "floatValue", "()F"); + break; + case 'D': + EmitUnboxNumber(ilgen, "doubleValue", "()D"); + break; + default: + throw new InvalidOperationException(); + } + } - private static bool IsSupportedImplMethod(ClassFile.ConstantPoolItemMethodHandle implMethod, TypeWrapper caller, TypeWrapper[] captured, ClassFile.ConstantPoolItemMethodType instantiatedMethodType) - { - switch (implMethod.Kind) - { - case ClassFile.RefKind.invokeVirtual: - case ClassFile.RefKind.invokeInterface: - case ClassFile.RefKind.newInvokeSpecial: - case ClassFile.RefKind.invokeStatic: - case ClassFile.RefKind.invokeSpecial: - break; - default: - return false; - } - MethodWrapper mw = (MethodWrapper)implMethod.Member; - if (mw == null || mw.HasCallerID || DynamicTypeWrapper.RequiresDynamicReflectionCallerClass(mw.DeclaringType.Name, mw.Name, mw.Signature)) - { - return false; - } - TypeWrapper instance; - if (mw.IsConstructor) - { - instance = mw.DeclaringType; - } - else if (mw.IsStatic) - { - instance = null; - } - else - { - // if implMethod is an instance method, the type of the first captured value must be subtype of implMethod.DeclaringType - instance = captured.Length == 0 ? instantiatedMethodType.GetArgTypes()[0] : captured[0]; - if (!instance.IsAssignableTo(mw.DeclaringType)) - { - return false; - } - } - if (!mw.IsAccessibleFrom(mw.DeclaringType, caller, instance)) - { - return false; - } - mw.Link(); - return true; - } + private static void EmitUnboxNumber(CodeEmitter ilgen, string methodName, string methodSig) + { + TypeWrapper tw = ClassLoaderWrapper.LoadClassCritical("java.lang.Number"); + tw.EmitCheckcast(ilgen); + MethodWrapper mw = tw.GetMethodWrapper(methodName, methodSig, false); + mw.Link(); + mw.EmitCallvirt(ilgen); + } - private static bool IsSupportedInterface(TypeWrapper tw, TypeWrapper caller) - { - return tw.IsInterface - && !tw.IsGhost - && tw.IsAccessibleFrom(caller) - && !Serialization.IsISerializable(tw); - } + private static void AddDefaultInterfaceMethods(DynamicTypeWrapper.FinishContext context, MethodWrapper[] methodList, TypeBuilder tb) + { + // we use special name to hide these from Java reflection + const MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName; + TypeWrapperFactory factory = context.TypeWrapper.GetClassLoader().GetTypeWrapperFactory(); + foreach (MethodWrapper mw in methodList) + { + if (!mw.IsAbstract) + { + MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr); + if (mw.Name != mw.RealName) + { + tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); + } + DynamicTypeWrapper.FinishContext.EmitCallDefaultInterfaceMethod(mb, mw); + } + else if (IsObjectMethod(mw)) + { + MethodBuilder mb = mw.GetDefineMethodHelper().DefineMethod(factory, tb, mw.Name, attr); + if (mw.Name != mw.RealName) + { + tb.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); + } + CodeEmitter ilgen = CodeEmitter.Create(mb); + for (int i = 0, count = mw.GetParameters().Length; i <= count; i++) + { + ilgen.EmitLdarg(i); + } + CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false).EmitCallvirt(ilgen); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + } + } + } - private static bool CheckSupportedInterfaces(TypeWrapper caller, TypeWrapper tw, TypeWrapper[] markers, ClassFile.ConstantPoolItemMethodType[] bridges, out MethodWrapper[] methodList) - { - // we don't need to check for unloadable, because we already did that while validating the invoke signature - if (!IsSupportedInterface(tw, caller)) - { - methodList = null; - return false; - } - Dictionary methods = new Dictionary(); - int abstractMethodCount = 0; - int bridgeMethodCount = 0; - if (GatherAllInterfaceMethods(tw, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount) && abstractMethodCount == 1) - { - foreach (TypeWrapper marker in markers) - { - if (!IsSupportedInterface(marker, caller)) - { - methodList = null; - return false; - } - if (!GatherAllInterfaceMethods(marker, null, methods, ref abstractMethodCount, ref bridgeMethodCount) || abstractMethodCount != 1) - { - methodList = null; - return false; - } - } - if (bridges != null && bridgeMethodCount != bridges.Length) - { - methodList = null; - return false; - } - methodList = new MethodWrapper[methods.Count]; - methods.Values.CopyTo(methodList, 0); - return true; - } - methodList = null; - return false; - } + private static bool IsSupportedImplMethod(ClassFile.ConstantPoolItemMethodHandle implMethod, TypeWrapper caller, TypeWrapper[] captured, ClassFile.ConstantPoolItemMethodType instantiatedMethodType) + { + switch (implMethod.Kind) + { + case ReferenceKind.InvokeVirtual: + case ReferenceKind.InvokeInterface: + case ReferenceKind.NewInvokeSpecial: + case ReferenceKind.InvokeStatic: + case ReferenceKind.InvokeSpecial: + break; + default: + return false; + } + MethodWrapper mw = (MethodWrapper)implMethod.Member; + if (mw == null || mw.HasCallerID || DynamicTypeWrapper.RequiresDynamicReflectionCallerClass(mw.DeclaringType.Name, mw.Name, mw.Signature)) + { + return false; + } + TypeWrapper instance; + if (mw.IsConstructor) + { + instance = mw.DeclaringType; + } + else if (mw.IsStatic) + { + instance = null; + } + else + { + // if implMethod is an instance method, the type of the first captured value must be subtype of implMethod.DeclaringType + instance = captured.Length == 0 ? instantiatedMethodType.GetArgTypes()[0] : captured[0]; + if (!instance.IsAssignableTo(mw.DeclaringType)) + { + return false; + } + } + if (!mw.IsAccessibleFrom(mw.DeclaringType, caller, instance)) + { + return false; + } + mw.Link(); + return true; + } - private static bool GatherAllInterfaceMethods(TypeWrapper tw, ClassFile.ConstantPoolItemMethodType[] bridges, Dictionary methods, - ref int abstractMethodCount, ref int bridgeMethodCount) - { - foreach (MethodWrapper mw in tw.GetMethods()) - { - if (mw.IsVirtual) - { - MirandaMethodWrapper mmw = mw as MirandaMethodWrapper; - if (mmw != null) - { - if (mmw.Error != null) - { - return false; - } - continue; - } - MethodKey key = new MethodKey("", mw.Name, mw.Signature); - MethodWrapper current; - if (methods.TryGetValue(key, out current)) - { - if (!MatchSignatures(mw, current)) - { - // linkage error (or unloadable type) - return false; - } - } - else - { - methods.Add(key, mw); - if (mw.IsAbstract && !IsObjectMethod(mw)) - { - if (bridges != null && IsBridge(mw, bridges)) - { - bridgeMethodCount++; - } - else - { - abstractMethodCount++; - } - } - } - mw.Link(); - if (mw.GetMethod() == null) - { - return false; - } - if (current != null && mw.RealName != current.RealName) - { - return false; - } - } - } - foreach (TypeWrapper tw1 in tw.Interfaces) - { - if (!GatherAllInterfaceMethods(tw1, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount)) - { - return false; - } - } - return true; - } + private static bool IsSupportedInterface(TypeWrapper tw, TypeWrapper caller) + { + return tw.IsInterface + && !tw.IsGhost + && tw.IsAccessibleFrom(caller) + && !Serialization.IsISerializable(tw); + } - private static bool IsBridge(MethodWrapper mw, ClassFile.ConstantPoolItemMethodType[] bridges) - { - foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges) - { - if (bridge.Signature == mw.Signature) - { - return true; - } - } - return false; - } + private static bool CheckSupportedInterfaces(TypeWrapper caller, TypeWrapper tw, TypeWrapper[] markers, ClassFile.ConstantPoolItemMethodType[] bridges, out MethodWrapper[] methodList) + { + // we don't need to check for unloadable, because we already did that while validating the invoke signature + if (!IsSupportedInterface(tw, caller)) + { + methodList = null; + return false; + } + Dictionary methods = new Dictionary(); + int abstractMethodCount = 0; + int bridgeMethodCount = 0; + if (GatherAllInterfaceMethods(tw, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount) && abstractMethodCount == 1) + { + foreach (TypeWrapper marker in markers) + { + if (!IsSupportedInterface(marker, caller)) + { + methodList = null; + return false; + } + if (!GatherAllInterfaceMethods(marker, null, methods, ref abstractMethodCount, ref bridgeMethodCount) || abstractMethodCount != 1) + { + methodList = null; + return false; + } + } + if (bridges != null && bridgeMethodCount != bridges.Length) + { + methodList = null; + return false; + } + methodList = new MethodWrapper[methods.Count]; + methods.Values.CopyTo(methodList, 0); + return true; + } + methodList = null; + return false; + } - private static bool IsObjectMethod(MethodWrapper mw) - { - MethodWrapper objectMethod; - return (objectMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false)) != null - && objectMethod.IsPublic; - } + private static bool GatherAllInterfaceMethods(TypeWrapper tw, ClassFile.ConstantPoolItemMethodType[] bridges, Dictionary methods, ref int abstractMethodCount, ref int bridgeMethodCount) + { + foreach (MethodWrapper mw in tw.GetMethods()) + { + if (mw.IsVirtual) + { + MirandaMethodWrapper mmw = mw as MirandaMethodWrapper; + if (mmw != null) + { + if (mmw.Error != null) + { + return false; + } + continue; + } + MethodKey key = new MethodKey("", mw.Name, mw.Signature); + MethodWrapper current; + if (methods.TryGetValue(key, out current)) + { + if (!MatchSignatures(mw, current)) + { + // linkage error (or unloadable type) + return false; + } + } + else + { + methods.Add(key, mw); + if (mw.IsAbstract && !IsObjectMethod(mw)) + { + if (bridges != null && IsBridge(mw, bridges)) + { + bridgeMethodCount++; + } + else + { + abstractMethodCount++; + } + } + } + mw.Link(); + if (mw.GetMethod() == null) + { + return false; + } + if (current != null && mw.RealName != current.RealName) + { + return false; + } + } + } + foreach (TypeWrapper tw1 in tw.Interfaces) + { + if (!GatherAllInterfaceMethods(tw1, bridges, methods, ref abstractMethodCount, ref bridgeMethodCount)) + { + return false; + } + } + return true; + } - private static bool MatchSignatures(MethodWrapper interfaceMethod, ClassFile.ConstantPoolItemMethodType samMethodType) - { - return interfaceMethod.ReturnType == samMethodType.GetRetType() - && MatchTypes(interfaceMethod.GetParameters(), samMethodType.GetArgTypes()); - } + private static bool IsBridge(MethodWrapper mw, ClassFile.ConstantPoolItemMethodType[] bridges) + { + foreach (ClassFile.ConstantPoolItemMethodType bridge in bridges) + { + if (bridge.Signature == mw.Signature) + { + return true; + } + } + return false; + } - private static bool MatchSignatures(MethodWrapper mw1, MethodWrapper mw2) - { - return mw1.ReturnType == mw2.ReturnType - && MatchTypes(mw1.GetParameters(), mw2.GetParameters()); - } + private static bool IsObjectMethod(MethodWrapper mw) + { + MethodWrapper objectMethod; + return (objectMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(mw.Name, mw.Signature, false)) != null + && objectMethod.IsPublic; + } - private static bool MatchTypes(TypeWrapper[] ar1, TypeWrapper[] ar2) - { - if (ar1.Length != ar2.Length) - { - return false; - } - for (int i = 0; i < ar1.Length; i++) - { - if (ar1[i] != ar2[i]) - { - return false; - } - } - return true; - } + private static bool MatchSignatures(MethodWrapper interfaceMethod, ClassFile.ConstantPoolItemMethodType samMethodType) + { + return interfaceMethod.ReturnType == samMethodType.GetRetType() + && MatchTypes(interfaceMethod.GetParameters(), samMethodType.GetArgTypes()); + } - private static bool IsLambdaMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm) - { - ClassFile.ConstantPoolItemMethodHandle mh; - return bsm.ArgumentCount == 3 - && classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType - && classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle - && classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType - && (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ClassFile.RefKind.invokeStatic - && mh.Member != null - && IsLambdaMetafactory(mh.Member); - } + private static bool MatchSignatures(MethodWrapper mw1, MethodWrapper mw2) + { + return mw1.ReturnType == mw2.ReturnType + && MatchTypes(mw1.GetParameters(), mw2.GetParameters()); + } - private static bool IsLambdaMetafactory(MemberWrapper mw) - { - return mw.Name == "metafactory" - && mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodHandle;Ljava.lang.invoke.MethodType;)Ljava.lang.invoke.CallSite;" - && mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory"; - } + private static bool MatchTypes(TypeWrapper[] ar1, TypeWrapper[] ar2) + { + if (ar1.Length != ar2.Length) + { + return false; + } + for (int i = 0; i < ar1.Length; i++) + { + if (ar1[i] != ar2[i]) + { + return false; + } + } + return true; + } - [Flags] - enum AltFlags - { - Serializable = 1, - Markers = 2, - Bridges = 4, - Mask = Serializable | Markers | Bridges - } + private static bool IsLambdaMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm) + { + ClassFile.ConstantPoolItemMethodHandle mh; + return bsm.ArgumentCount == 3 + && classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType + && classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle + && classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType + && (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ReferenceKind.InvokeStatic + && mh.Member != null + && IsLambdaMetafactory(mh.Member); + } - private static bool IsLambdaAltMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm) - { - ClassFile.ConstantPoolItemMethodHandle mh; - AltFlags flags; - int argpos = 4; - return bsm.ArgumentCount >= 4 - && (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ClassFile.RefKind.invokeStatic - && mh.Member != null - && IsLambdaAltMetafactory(mh.Member) - && classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType - && classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle - && classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType - && classFile.GetConstantPoolConstantType(bsm.GetArgument(3)) == ClassFile.ConstantType.Integer - && ((flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3))) & ~AltFlags.Mask) == 0 - && ((flags & AltFlags.Markers) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.Class, ref argpos)) - && ((flags & AltFlags.Bridges) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.MethodType, ref argpos)) - && argpos == bsm.ArgumentCount; - } + private static bool IsLambdaMetafactory(MemberWrapper mw) + { + return mw.Name == "metafactory" + && mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodType;Ljava.lang.invoke.MethodHandle;Ljava.lang.invoke.MethodType;)Ljava.lang.invoke.CallSite;" + && mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory"; + } - private static bool IsLambdaAltMetafactory(MemberWrapper mw) - { - return mw.Name == "altMetafactory" - && mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;[Ljava.lang.Object;)Ljava.lang.invoke.CallSite;" - && mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory"; - } + [Flags] + enum AltFlags + { + Serializable = 1, + Markers = 2, + Bridges = 4, + Mask = Serializable | Markers | Bridges + } - private static bool CheckOptionalArgs(ClassFile classFile, ClassFile.BootstrapMethod bsm, ClassFile.ConstantType type, ref int argpos) - { - if (bsm.ArgumentCount - argpos < 1) - { - return false; - } - if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos)) != ClassFile.ConstantType.Integer) - { - return false; - } - int count = classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++)); - if (count < 0 || bsm.ArgumentCount - argpos < count) - { - return false; - } - for (int i = 0; i < count; i++) - { - if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos++)) != type) - { - return false; - } - } - return true; - } + private static bool IsLambdaAltMetafactory(ClassFile classFile, ClassFile.BootstrapMethod bsm) + { + ClassFile.ConstantPoolItemMethodHandle mh; + AltFlags flags; + int argpos = 4; + return bsm.ArgumentCount >= 4 + && (mh = classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex)).Kind == ReferenceKind.InvokeStatic + && mh.Member != null + && IsLambdaAltMetafactory(mh.Member) + && classFile.GetConstantPoolConstantType(bsm.GetArgument(0)) == ClassFile.ConstantType.MethodType + && classFile.GetConstantPoolConstantType(bsm.GetArgument(1)) == ClassFile.ConstantType.MethodHandle + && classFile.GetConstantPoolConstantType(bsm.GetArgument(2)) == ClassFile.ConstantType.MethodType + && classFile.GetConstantPoolConstantType(bsm.GetArgument(3)) == ClassFile.ConstantType.Integer + && ((flags = (AltFlags)classFile.GetConstantPoolConstantInteger(bsm.GetArgument(3))) & ~AltFlags.Mask) == 0 + && ((flags & AltFlags.Markers) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.Class, ref argpos)) + && ((flags & AltFlags.Bridges) == 0 || CheckOptionalArgs(classFile, bsm, ClassFile.ConstantType.MethodType, ref argpos)) + && argpos == bsm.ArgumentCount; + } - private static bool HasUnloadable(ClassFile.ConstantPoolItemInvokeDynamic cpi) - { - return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; - } + private static bool IsLambdaAltMetafactory(MemberWrapper mw) + { + return mw.Name == "altMetafactory" + && mw.Signature == "(Ljava.lang.invoke.MethodHandles$Lookup;Ljava.lang.String;Ljava.lang.invoke.MethodType;[Ljava.lang.Object;)Ljava.lang.invoke.CallSite;" + && mw.DeclaringType.Name == "java.lang.invoke.LambdaMetafactory"; + } - private static bool HasUnloadable(ClassFile.ConstantPoolItemMethodType cpi) - { - return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; - } + private static bool CheckOptionalArgs(ClassFile classFile, ClassFile.BootstrapMethod bsm, ClassFile.ConstantType type, ref int argpos) + { + if (bsm.ArgumentCount - argpos < 1) + { + return false; + } + if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos)) != ClassFile.ConstantType.Integer) + { + return false; + } + int count = classFile.GetConstantPoolConstantInteger(bsm.GetArgument(argpos++)); + if (count < 0 || bsm.ArgumentCount - argpos < count) + { + return false; + } + for (int i = 0; i < count; i++) + { + if (classFile.GetConstantPoolConstantType(bsm.GetArgument(argpos++)) != type) + { + return false; + } + } + return true; + } - private static bool HasUnloadable(ClassFile.ConstantPoolItemMI cpi) - { - return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; - } + private static bool HasUnloadable(ClassFile.ConstantPoolItemInvokeDynamic cpi) + { + return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; + } - private static bool HasUnloadable(TypeWrapper[] wrappers) - { - foreach (TypeWrapper tw in wrappers) - { - if (tw.IsUnloadable) - { - return true; - } - } - return false; - } - } + private static bool HasUnloadable(ClassFile.ConstantPoolItemMethodType cpi) + { + return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; + } + + private static bool HasUnloadable(ClassFile.ConstantPoolItemMI cpi) + { + return HasUnloadable(cpi.GetArgTypes()) || cpi.GetRetType().IsUnloadable; + } + + private static bool HasUnloadable(TypeWrapper[] wrappers) + { + foreach (TypeWrapper tw in wrappers) + { + if (tw.IsUnloadable) + { + return true; + } + } + return false; + } + } } diff --git a/src/IKVM.Runtime/Launcher.cs b/src/IKVM.Runtime/Launcher.cs index 9e2e5a051f..6ada07509e 100644 --- a/src/IKVM.Runtime/Launcher.cs +++ b/src/IKVM.Runtime/Launcher.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -12,7 +11,9 @@ using System.Text.Json.Serialization; using System.Threading; +using IKVM.Attributes; using IKVM.Internal; +using IKVM.Runtime.Accessors.Java.Lang; namespace IKVM.Runtime { @@ -24,6 +25,20 @@ namespace IKVM.Runtime static class Launcher { +#if FIRST_PASS == false && IMPORTER == false && EXPORTER == false + + static SystemAccessor systemAccessor; + static ThreadAccessor threadAccessor; + static ThreadGroupAccessor threadGroupAccessor; + + static SystemAccessor SystemAccessor => JVM.BaseAccessors.Get(ref systemAccessor); + + static ThreadAccessor ThreadAccessor => JVM.BaseAccessors.Get(ref threadAccessor); + + static ThreadGroupAccessor ThreadGroupAccessor => JVM.BaseAccessors.Get(ref threadGroupAccessor); + +#endif + /// /// Data sent as part of the debug ping protocol. /// @@ -100,15 +115,16 @@ static string[] Glob(string[] paths) /// Sets the startup properties. /// /// - static void SetProperties(IDictionary properties) + static void SetProperties(IDictionary properties) { -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else if (properties is null) throw new ArgumentNullException(nameof(properties)); - IKVM.Java.Externs.java.lang.VMSystemProperties.ImportProperties = properties; + foreach (var kvp in properties) + JVM.Properties.User[kvp.Key] = kvp.Value; #endif } @@ -117,7 +133,7 @@ static void SetProperties(IDictionary properties) /// static void EnterMainThread() { -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else if (Thread.CurrentThread.Name == null) @@ -135,11 +151,13 @@ static void EnterMainThread() } } - java.lang.Thread.currentThread(); + JVM.EnsureInitialized(); + ThreadAccessor.InvokeCurrentThread(); try { - sun.misc.Signal.handle(new sun.misc.Signal("BREAK"), sun.misc.SignalHandler.SIG_DFL); + if (RuntimeUtil.IsWindows) + sun.misc.Signal.handle(new sun.misc.Signal("BREAK"), sun.misc.SignalHandler.SIG_DFL); } catch (java.lang.IllegalArgumentException) { @@ -153,7 +171,7 @@ static void EnterMainThread() /// static void ExitMainThread() { -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else // FXBUG when the main thread ends, it doesn't actually die, it stays around to manage the lifetime @@ -161,7 +179,7 @@ static void ExitMainThread() // use the TLS as a hack to track when the thread dies (if the object stored in the TLS is finalized, // we know the thread is dead). So to make that work for the main thread, we use jniDetach which // explicitly cleans up our thread. - java.lang.Thread.currentThread().die(); + ThreadAccessor.InvokeDie(ThreadAccessor.InvokeCurrentThread()); #endif } @@ -189,12 +207,12 @@ static string GetVersionAndCopyrightInfo() /// static void PrintVersion() { -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else Console.WriteLine(GetVersionAndCopyrightInfo()); Console.WriteLine("CLR version: {0} ({1} bit)", Environment.Version, IntPtr.Size * 8); - var ver = java.lang.System.getProperty("openjdk.version"); + var ver = SystemAccessor.InvokeGetProperty("openjdk.version"); if (ver != null) Console.WriteLine("OpenJDK version: {0}", ver); #endif @@ -206,7 +224,7 @@ static void PrintVersion() /// static void AddBootClassPathAssembly(Assembly assembly) { -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else ClassLoaderWrapper.GetBootstrapClassLoader().AddDelegate(AssemblyClassLoader.FromAssembly(assembly)); @@ -233,12 +251,13 @@ static bool ArgEquals(ReadOnlySpan a, string b) /// /// /// + [HideFromJava(HideFromJavaFlags.StackTrace)] public static int Run(string main, bool jar, string[] args, string rarg, IDictionary properties) { if (args is null) throw new ArgumentNullException(nameof(args)); -#if FIRST_PASS || STATIC_COMPILER +#if FIRST_PASS || IMPORTER throw new NotImplementedException(); #else HandleDebugTrace(); @@ -256,7 +275,6 @@ public static int Run(string main, bool jar, string[] args, string rarg, IDictio initialize["java.class.path"] = string.Join(Path.PathSeparator.ToString(), Glob(cp.Split(Path.PathSeparator))); // ikvm.home from environment by default - initialize["ikvm.home"] = "ikvm"; if (Environment.GetEnvironmentVariable("IKVM_HOME") is string ih && !string.IsNullOrEmpty(ih)) initialize["ikvm.home"] = ih; @@ -483,7 +501,7 @@ public static int Run(string main, bool jar, string[] args, string rarg, IDictio // process the main argument, returning the true value, and resetting the command property to match var clazz = sun.launcher.LauncherHelper.checkAndLoadMain(true, jar ? 2 : 1, main); - java.lang.System.setProperty("sun.java.command", initialize["sun.java.command"]); + SystemAccessor.InvokeSetProperty("sun.java.command", initialize["sun.java.command"]); // find the main method and ensure it is accessible var method = clazz.getMethod("main", typeof(string[])); @@ -502,8 +520,8 @@ public static int Run(string main, bool jar, string[] args, string rarg, IDictio } catch (Exception e) { - var thread = java.lang.Thread.currentThread(); - thread.getThreadGroup().uncaughtException(thread, ikvm.runtime.Util.mapException(e)); + var thread = ThreadAccessor.InvokeCurrentThread(); + ThreadGroupAccessor.InvokeUncaughtException(ThreadAccessor.InvokeGetThreadGroup(thread), thread, ikvm.runtime.Util.mapException(e)); } finally { @@ -535,7 +553,7 @@ static void HandleDebugTrace() // send a start event to the host if (debugUri != null) { - if (debugUri.Scheme == "tcp" && IPAddress.TryParse(debugUri.Host, out var ip) && debugUri.Port > 0) + if (debugUri.Scheme == "tcp" && IPAddress.TryParse(debugUri.Host, out var ip) && debugUri.Port > 0) { var message = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new IkvmStartEvent() { ProcessId = Process.GetCurrentProcess().Id, })); using var c = new TcpClient(); diff --git a/src/IKVM.Runtime/LoadMode.cs b/src/IKVM.Runtime/LoadMode.cs index 2f18a01549..a9efa34006 100644 --- a/src/IKVM.Runtime/LoadMode.cs +++ b/src/IKVM.Runtime/LoadMode.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -34,9 +34,11 @@ Jeroen Frijters namespace IKVM.Internal { + [Flags] enum LoadMode { + // These are the modes that should be used Find = ReturnNull, LoadOrNull = Load | ReturnNull, @@ -58,5 +60,7 @@ enum LoadMode // warnings WarnClassNotFound = 0x0020, + } + } diff --git a/src/IKVM.Runtime/LocalVars.cs b/src/IKVM.Runtime/LocalVars.cs index c40ddc3aea..8294d5b04c 100644 --- a/src/IKVM.Runtime/LocalVars.cs +++ b/src/IKVM.Runtime/LocalVars.cs @@ -27,7 +27,7 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; #else diff --git a/src/ikvmc/IKVM/Internal/MapXml/ConditionalInstruction.cs b/src/IKVM.Runtime/MemberFlags.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/ConditionalInstruction.cs rename to src/IKVM.Runtime/MemberFlags.cs index f4a87d1392..d3d4269526 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/ConditionalInstruction.cs +++ b/src/IKVM.Runtime/MemberFlags.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2014 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,25 +21,29 @@ Jeroen Frijters jeroen@frijters.net */ - using System; -using System.Xml.Serialization; -namespace IKVM.Internal.MapXml +namespace IKVM.Internal { - [XmlType("conditional")] - public sealed class ConditionalInstruction : Instruction + + [Flags] + enum MemberFlags : short { - [XmlAttribute("framework")] - public string framework; - public InstructionList code; - - internal override void Generate(CodeGenContext context, CodeEmitter ilgen) - { - if (Environment.Version.ToString().StartsWith(framework)) - { - code.Generate(context, ilgen); - } - } + + None = 0, + HideFromReflection = 1, + ExplicitOverride = 2, + MirandaMethod = 8, + AccessStub = 16, + InternalAccess = 32, // member has "internal" access (@ikvm.lang.Internal) + PropertyAccessor = 64, + Intrinsic = 128, + CallerID = 256, + NonPublicTypeInSignature = 512, // this flag is only available after linking and is not set for access stubs + DelegateInvokeWithByRefParameter = 1024, + Type2FinalField = 2048, + NoOp = 4096, // empty static initializer + } + } diff --git a/src/IKVM.Runtime/MemberWrapper.cs b/src/IKVM.Runtime/MemberWrapper.cs index 44a383bdfd..157052b4f8 100644 --- a/src/IKVM.Runtime/MemberWrapper.cs +++ b/src/IKVM.Runtime/MemberWrapper.cs @@ -22,57 +22,48 @@ Jeroen Frijters */ using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using IKVM.Attributes; +using IKVM.Runtime; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; - #else using System.Reflection; using System.Reflection.Emit; #endif -using System.Diagnostics; - -using IKVM.Attributes; -using System.Threading; -using System.Runtime.InteropServices; +#if IMPORTER +using IKVM.Tools.Importer; +#endif namespace IKVM.Internal { - [Flags] - enum MemberFlags : short - { - None = 0, - HideFromReflection = 1, - ExplicitOverride = 2, - MirandaMethod = 8, - AccessStub = 16, - InternalAccess = 32, // member has "internal" access (@ikvm.lang.Internal) - PropertyAccessor = 64, - Intrinsic = 128, - CallerID = 256, - NonPublicTypeInSignature = 512, // this flag is only available after linking and is not set for access stubs - DelegateInvokeWithByRefParameter = 1024, - Type2FinalField = 2048, - NoOp = 4096, // empty static initializer - } abstract class MemberWrapper { - private HandleWrapper handle; - private readonly TypeWrapper declaringType; + + readonly TypeWrapper declaringType; + readonly string name; + readonly string sig; protected readonly Modifiers modifiers; - private MemberFlags flags; - private readonly string name; - private readonly string sig; + HandleWrapper handle; + MemberFlags flags; - private sealed class HandleWrapper + sealed class HandleWrapper { + internal readonly IntPtr Value; + /// + /// Initializes a new instance. + /// + /// [System.Security.SecurityCritical] internal HandleWrapper(MemberWrapper obj) { @@ -80,23 +71,25 @@ internal HandleWrapper(MemberWrapper obj) } #if CLASSGC - [System.Security.SecuritySafeCritical] - ~HandleWrapper() - { - if (!Environment.HasShutdownStarted) - { - GCHandle h = (GCHandle)Value; - if (h.Target == null) - { - h.Free(); - } - else - { - GC.ReRegisterForFinalize(this); - } - } - } + + /// + /// Finalizes the instance. + /// + [System.Security.SecuritySafeCritical] + ~HandleWrapper() + { + if (!Environment.HasShutdownStarted) + { + var h = (GCHandle)Value; + if (h.Target == null) + h.Free(); + else + GC.ReRegisterForFinalize(this); + } + } + #endif + } protected MemberWrapper(TypeWrapper declaringType, string name, string sig, Modifiers modifiers, MemberFlags flags) @@ -111,16 +104,11 @@ protected MemberWrapper(TypeWrapper declaringType, string name, string sig, Modi internal IntPtr Cookie { - [System.Security.SecurityCritical] get { lock (this) - { - if (handle == null) - { - handle = new HandleWrapper(this); - } - } + handle ??= new HandleWrapper(this); + return handle.Value; } } @@ -131,29 +119,11 @@ internal static MemberWrapper FromCookieImpl(IntPtr cookie) return (MemberWrapper)GCHandle.FromIntPtr(cookie).Target; } - internal TypeWrapper DeclaringType - { - get - { - return declaringType; - } - } + internal TypeWrapper DeclaringType => declaringType; - internal string Name - { - get - { - return name; - } - } + internal string Name => name; - internal string Signature - { - get - { - return sig; - } - } + internal string Signature => sig; internal bool IsAccessibleFrom(TypeWrapper referencedType, TypeWrapper caller, TypeWrapper instance) { @@ -183,22 +153,22 @@ private bool IsPublicOrProtectedMemberAccessible(TypeWrapper caller, TypeWrapper private bool InPracticeInternalsVisibleTo(TypeWrapper caller) { -#if !STATIC_COMPILER - if (DeclaringType.TypeAsTBD.Assembly.Equals(caller.TypeAsTBD.Assembly)) - { - // both the caller and the declaring type are in the same assembly - // so we know that the internals are visible - // (this handles the case where we're running in dynamic mode) - return true; - } +#if !IMPORTER + if (DeclaringType.TypeAsTBD.Assembly.Equals(caller.TypeAsTBD.Assembly)) + { + // both the caller and the declaring type are in the same assembly + // so we know that the internals are visible + // (this handles the case where we're running in dynamic mode) + return true; + } #endif #if CLASSGC - if (DeclaringType.IsDynamic) - { - // if we are dynamic, we can just become friends with the caller - DeclaringType.GetClassLoader().GetTypeWrapperFactory().AddInternalsVisibleTo(caller.TypeAsTBD.Assembly); - return true; - } + if (DeclaringType.IsDynamic) + { + // if we are dynamic, we can just become friends with the caller + DeclaringType.GetClassLoader().GetTypeWrapperFactory().AddInternalsVisibleTo(caller.TypeAsTBD.Assembly); + return true; + } #endif return DeclaringType.InternalsVisibleTo(caller); } @@ -363,1823 +333,39 @@ internal bool IsFinal } } - abstract class MethodWrapper : MemberWrapper + sealed class PrivateInterfaceMethodWrapper : SmartMethodWrapper { -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR - private volatile java.lang.reflect.Executable reflectionMethod; -#endif - internal static readonly MethodWrapper[] EmptyArray = new MethodWrapper[0]; - private MethodBase method; - private string[] declaredExceptions; - private TypeWrapper returnTypeWrapper; - private TypeWrapper[] parameterTypeWrappers; - -#if EMITTERS - internal virtual void EmitCall(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } - - internal virtual void EmitCallvirt(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } - - internal virtual void EmitCallvirtReflect(CodeEmitter ilgen) - { - EmitCallvirt(ilgen); - } - - internal virtual void EmitNewobj(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } - - internal virtual bool EmitIntrinsic(EmitIntrinsicContext context) - { - return Intrinsics.Emit(context); - } -#endif // EMITTERS - - internal virtual bool IsDynamicOnly - { - get - { - return false; - } - } - - internal MethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, modifiers, flags) - { - Profiler.Count("MethodWrapper"); - this.method = method; - Debug.Assert(((returnType == null) == (parameterTypes == null)) || (returnType == PrimitiveTypeWrapper.VOID)); - this.returnTypeWrapper = returnType; - this.parameterTypeWrappers = parameterTypes; - if (Intrinsics.IsIntrinsic(this)) - { - SetIntrinsicFlag(); - } - UpdateNonPublicTypeInSignatureFlag(); - } - - private void UpdateNonPublicTypeInSignatureFlag() - { - if ((IsPublic || IsProtected) && (returnTypeWrapper != null && parameterTypeWrappers != null) && !(this is AccessStubMethodWrapper) && !(this is AccessStubConstructorMethodWrapper)) - { - if (!returnTypeWrapper.IsPublic && !returnTypeWrapper.IsUnloadable) - { - SetNonPublicTypeInSignatureFlag(); - } - else - { - foreach (TypeWrapper tw in parameterTypeWrappers) - { - if (!tw.IsPublic && !tw.IsUnloadable) - { - SetNonPublicTypeInSignatureFlag(); - break; - } - } - } - } - } - - internal void SetDeclaredExceptions(string[] exceptions) - { - if (exceptions == null) - { - exceptions = new string[0]; - } - this.declaredExceptions = (string[])exceptions.Clone(); - } - - internal string[] GetDeclaredExceptions() - { - return declaredExceptions; - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal java.lang.reflect.Executable ToMethodOrConstructor(bool copy) - { -#if FIRST_PASS - return null; -#else - java.lang.reflect.Executable method = reflectionMethod; - if (method == null) - { - Link(); - ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader(); - TypeWrapper[] argTypes = GetParameters(); - java.lang.Class[] parameterTypes = new java.lang.Class[argTypes.Length]; - for (int i = 0; i < argTypes.Length; i++) - { - parameterTypes[i] = argTypes[i].EnsureLoadable(loader).ClassObject; - } - java.lang.Class[] checkedExceptions = GetExceptions(); - if (this.IsConstructor) - { - method = new java.lang.reflect.Constructor( - this.DeclaringType.ClassObject, - parameterTypes, - checkedExceptions, - (int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0), - Array.IndexOf(this.DeclaringType.GetMethods(), this), - this.DeclaringType.GetGenericMethodSignature(this), - null, - null - ); - } - else - { - method = new java.lang.reflect.Method( - this.DeclaringType.ClassObject, - this.Name, - parameterTypes, - this.ReturnType.EnsureLoadable(loader).ClassObject, - checkedExceptions, - (int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0), - Array.IndexOf(this.DeclaringType.GetMethods(), this), - this.DeclaringType.GetGenericMethodSignature(this), - null, - null, - null - ); - } - lock (this) - { - if (reflectionMethod == null) - { - reflectionMethod = method; - } - else - { - method = reflectionMethod; - } - } - } - if (copy) - { - java.lang.reflect.Constructor ctor = method as java.lang.reflect.Constructor; - if (ctor != null) - { - return ctor.copy(); - } - return ((java.lang.reflect.Method)method).copy(); - } - return method; -#endif - } - -#if !FIRST_PASS - private java.lang.Class[] GetExceptions() - { - string[] classes = declaredExceptions; - Type[] types = Type.EmptyTypes; - if (classes == null) - { - // NOTE if method is a MethodBuilder, GetCustomAttributes doesn't work (and if - // the method had any declared exceptions, the declaredExceptions field would have - // been set) - if (method != null && !(method is MethodBuilder)) - { - ThrowsAttribute attr = AttributeHelper.GetThrows(method); - if (attr != null) - { - classes = attr.classes; - types = attr.types; - } - } - } - if (classes != null) - { - java.lang.Class[] array = new java.lang.Class[classes.Length]; - for (int i = 0; i < classes.Length; i++) - { - array[i] = this.DeclaringType.GetClassLoader().LoadClassByDottedName(classes[i]).ClassObject; - } - return array; - } - else - { - java.lang.Class[] array = new java.lang.Class[types.Length]; - for (int i = 0; i < types.Length; i++) - { - array[i] = types[i]; - } - return array; - } - } -#endif // !FIRST_PASS - - internal static MethodWrapper FromExecutable(java.lang.reflect.Executable executable) - { -#if FIRST_PASS - return null; -#else - return TypeWrapper.FromClass(executable.getDeclaringClass()).GetMethods()[executable._slot()]; -#endif - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - - [System.Security.SecurityCritical] - internal static MethodWrapper FromCookie(IntPtr cookie) - { - return (MethodWrapper)FromCookieImpl(cookie); - } - - internal bool IsLinked - { - get - { - return parameterTypeWrappers != null; - } - } - - internal void Link() - { - Link(LoadMode.Link); - } - - internal void Link(LoadMode mode) - { - lock (this) - { - if (parameterTypeWrappers != null) - { - return; - } - } - ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader(); - TypeWrapper ret = loader.RetTypeWrapperFromSig(Signature, mode); - TypeWrapper[] parameters = loader.ArgTypeWrapperListFromSig(Signature, mode); - lock (this) - { - try - { - // critical code in the finally block to avoid Thread.Abort interrupting the thread - } - finally - { - if (parameterTypeWrappers == null) - { - Debug.Assert(returnTypeWrapper == null || returnTypeWrapper == PrimitiveTypeWrapper.VOID); - returnTypeWrapper = ret; - parameterTypeWrappers = parameters; - UpdateNonPublicTypeInSignatureFlag(); - if (method == null) - { - try - { - DoLinkMethod(); - } - catch - { - // HACK if linking fails, we unlink to make sure - // that the next link attempt will fail again - returnTypeWrapper = null; - parameterTypeWrappers = null; - throw; - } - } - } - } - } - } - - protected virtual void DoLinkMethod() - { - method = this.DeclaringType.LinkMethod(this); - } - - [Conditional("DEBUG")] - internal void AssertLinked() + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal PrivateInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) { - if (!(parameterTypeWrappers != null && returnTypeWrapper != null)) - { - Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + this.Signature); - } - Debug.Assert(parameterTypeWrappers != null && returnTypeWrapper != null, this.DeclaringType.Name + "::" + this.Name + this.Signature); - } - - internal TypeWrapper ReturnType - { - get - { - AssertLinked(); - return returnTypeWrapper; - } - } - - internal TypeWrapper[] GetParameters() - { - AssertLinked(); - return parameterTypeWrappers; - } - -#if !STUB_GENERATOR - internal DefineMethodHelper GetDefineMethodHelper() - { - return new DefineMethodHelper(this); - } -#endif - - internal Type ReturnTypeForDefineMethod - { - get - { - return ReturnType.TypeAsSignatureType; - } - } - - internal Type[] GetParametersForDefineMethod() - { - TypeWrapper[] wrappers = GetParameters(); - int len = wrappers.Length; - if (HasCallerID) - { - len++; - } - Type[] temp = new Type[len]; - for (int i = 0; i < wrappers.Length; i++) - { - temp[i] = wrappers[i].TypeAsSignatureType; - } - if (HasCallerID) - { - temp[len - 1] = CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType; - } - return temp; - } - - // we expose the underlying MethodBase object, - // for Java types, this is the method that contains the compiled Java bytecode - // for remapped types, this is the mbCore method (not the helper) - // Note that for some artificial methods (notably wrap() in enums), method is null - internal MethodBase GetMethod() - { - AssertLinked(); - return method; - } - - internal string RealName - { - get - { - AssertLinked(); - return method.Name; - } - } - - internal bool IsAbstract - { - get - { - return (Modifiers & Modifiers.Abstract) != 0; - } - } - - internal bool RequiresNonVirtualDispatcher - { - get - { - return !IsConstructor - && !IsStatic - && !IsPrivate - && !IsAbstract - && !IsFinal - && !DeclaringType.IsFinal; - } - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal Type GetDelegateType() - { - TypeWrapper[] paramTypes = GetParameters(); - if (paramTypes.Length > MethodHandleUtil.MaxArity) - { - Type type = DeclaringType.TypeAsBaseType.Assembly.GetType( - ReturnType == PrimitiveTypeWrapper.VOID ? "__<>NVIV`" + paramTypes.Length : "__<>NVI`" + (paramTypes.Length + 1)); - if (type == null) - { - type = DeclaringType.GetClassLoader().GetTypeWrapperFactory().DefineDelegate(paramTypes.Length, ReturnType == PrimitiveTypeWrapper.VOID); - } - Type[] types = new Type[paramTypes.Length + (ReturnType == PrimitiveTypeWrapper.VOID ? 0 : 1)]; - for (int i = 0; i < paramTypes.Length; i++) - { - types[i] = paramTypes[i].TypeAsSignatureType; - } - if (ReturnType != PrimitiveTypeWrapper.VOID) - { - types[types.Length - 1] = ReturnType.TypeAsSignatureType; - } - return type.MakeGenericType(types); - } - return MethodHandleUtil.CreateMemberWrapperDelegateType(paramTypes, ReturnType); - } - - internal void ResolveMethod() - { -#if !FIRST_PASS - // if we've still got the builder object, we need to replace it with the real thing before we can call it - MethodBuilder mb = method as MethodBuilder; - if (mb != null) - { -#if NETFRAMEWORK - method = mb.Module.ResolveMethod(mb.GetToken().Token); -#else - BindingFlags flags = BindingFlags.DeclaredOnly; - flags |= mb.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; - flags |= mb.IsStatic ? BindingFlags.Static : BindingFlags.Instance; - method = DeclaringType.TypeAsTBD.GetMethod(mb.Name, flags, null, GetParametersForDefineMethod(), null); - if (method == null) - { - method = DeclaringType.TypeAsTBD.GetConstructor(flags, null, GetParametersForDefineMethod(), null); - } -#endif - } -#endif - } - - [HideFromJava] - internal virtual object InvokeNonvirtualRemapped(object obj, object[] args) - { - throw new InvalidOperationException(); - } - - [HideFromJava] - protected static object InvokeAndUnwrapException(MethodBase mb, object obj, object[] args) - { -#if FIRST_PASS - return null; -#else - try - { - return mb.Invoke(obj, args); - } - catch (TargetInvocationException x) - { - throw ikvm.runtime.Util.mapException(x.InnerException); - } -#endif - } - - [HideFromJava] - internal virtual object Invoke(object obj, object[] args) - { - return InvokeAndUnwrapException(method, obj, args); - } - - [HideFromJava] - internal virtual object CreateInstance(object[] args) - { -#if FIRST_PASS - return null; -#else - try - { - return ((ConstructorInfo)method).Invoke(args); - } - catch (TargetInvocationException x) - { - throw ikvm.runtime.Util.mapException(x.InnerException); - } -#endif - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - - internal static OpCode SimpleOpCodeToOpCode(SimpleOpCode opc) - { - switch (opc) - { - case SimpleOpCode.Call: - return OpCodes.Call; - case SimpleOpCode.Callvirt: - return OpCodes.Callvirt; - case SimpleOpCode.Newobj: - return OpCodes.Newobj; - default: - throw new InvalidOperationException(); - } - } - - internal virtual bool IsOptionalAttributeAnnotationValue - { - get { return false; } - } - - internal bool IsConstructor - { - get { return (object)Name == (object)StringConstants.INIT; } - } - - internal bool IsClassInitializer - { - get { return (object)Name == (object)StringConstants.CLINIT; } - } - - internal bool IsVirtual - { - get - { - return (modifiers & (Modifiers.Static | Modifiers.Private)) == 0 - && !IsConstructor; - } - } - - internal bool IsFinalizeOrClone - { - get - { - return IsProtected - && (DeclaringType == CoreClasses.java.lang.Object.Wrapper || DeclaringType == CoreClasses.java.lang.Throwable.Wrapper) - && (Name == StringConstants.CLONE || Name == StringConstants.FINALIZE); - } - } - } - abstract class SmartMethodWrapper : MethodWrapper - { - internal SmartMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) - { } #if EMITTERS - internal sealed override void EmitCall(CodeEmitter ilgen) - { - AssertLinked(); - CallImpl(ilgen); - } - protected virtual void CallImpl(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } - - internal sealed override void EmitCallvirt(CodeEmitter ilgen) - { - AssertLinked(); - if (DeclaringType.IsNonPrimitiveValueType) - { - // callvirt isn't allowed on a value type - // (we don't need to check for a null reference, because we're always dealing with an unboxed value) - CallImpl(ilgen); - } - else - { - CallvirtImpl(ilgen); - } - } - - protected virtual void CallvirtImpl(CodeEmitter ilgen) + protected override void CallImpl(CodeEmitter ilgen) { - throw new InvalidOperationException(); + ilgen.Emit(OpCodes.Call, GetMethod()); } - internal sealed override void EmitNewobj(CodeEmitter ilgen) + protected override void CallvirtImpl(CodeEmitter ilgen) { - AssertLinked(); - NewobjImpl(ilgen); - if (DeclaringType.IsNonPrimitiveValueType) - { - DeclaringType.EmitBox(ilgen); - } + ilgen.Emit(OpCodes.Call, GetMethod()); } - protected virtual void NewobjImpl(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } -#endif // EMITTERS - } +#endif - enum SimpleOpCode : byte - { - Call, - Callvirt, - Newobj } - sealed class SimpleCallMethodWrapper : MethodWrapper - { - private readonly SimpleOpCode call; - private readonly SimpleOpCode callvirt; - - internal SimpleCallMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags, SimpleOpCode call, SimpleOpCode callvirt) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) - { - this.call = call; - this.callvirt = callvirt; - } - -#if EMITTERS - internal override void EmitCall(CodeEmitter ilgen) - { - ilgen.Emit(SimpleOpCodeToOpCode(call), GetMethod()); - } - - internal override void EmitCallvirt(CodeEmitter ilgen) - { - ilgen.Emit(SimpleOpCodeToOpCode(callvirt), GetMethod()); - } -#endif // EMITTERS - } - - sealed class TypicalMethodWrapper : SmartMethodWrapper - { - internal TypicalMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) - { - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, GetMethod()); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Callvirt, GetMethod()); - } - - protected override void NewobjImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Newobj, GetMethod()); - } -#endif // EMITTERS - } - - abstract class MirandaMethodWrapper : SmartMethodWrapper - { - private readonly MethodWrapper ifmethod; - - private MirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, Modifiers modifiers) - : base(declaringType, ifmethod.Name, ifmethod.Signature, null, null, null, modifiers, MemberFlags.HideFromReflection | MemberFlags.MirandaMethod) - { - this.ifmethod = ifmethod; - } - - private sealed class AbstractMirandaMethodWrapper : MirandaMethodWrapper - { - internal AbstractMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod) - : base(declaringType, ifmethod, Modifiers.Public | Modifiers.Abstract) - { - } - } - - private sealed class DefaultMirandaMethodWrapper : MirandaMethodWrapper - { - internal DefaultMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod) - : base(declaringType, ifmethod, Modifiers.Public) - { - } - } - - private sealed class ErrorMirandaMethodWrapper : MirandaMethodWrapper - { - private string error; - - internal ErrorMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, string error) - : base(declaringType, ifmethod, Modifiers.Public) - { - this.error = error; - } - - protected override MirandaMethodWrapper AddConflictError(MethodWrapper mw) - { - error += " " + mw.DeclaringType.Name + "." + mw.Name; - return this; - } - - internal override string Error - { - get { return error; } - } - } - - internal static MirandaMethodWrapper Create(TypeWrapper declaringType, MethodWrapper ifmethod) - { - DefaultMirandaMethodWrapper dmmw = ifmethod as DefaultMirandaMethodWrapper; - if (dmmw != null) - { - return new DefaultMirandaMethodWrapper(declaringType, dmmw.BaseMethod); - } - return ifmethod.IsAbstract - ? (MirandaMethodWrapper)new AbstractMirandaMethodWrapper(declaringType, ifmethod) - : (MirandaMethodWrapper)new DefaultMirandaMethodWrapper(declaringType, ifmethod); - } - - internal MirandaMethodWrapper Update(MethodWrapper mw) - { - if (ifmethod == mw) - { - // an interface can be implemented multiple times - return this; - } - else if (ifmethod.DeclaringType.ImplementsInterface(mw.DeclaringType)) - { - // we can override a base interface without problems - return this; - } - else if (mw.DeclaringType.ImplementsInterface(ifmethod.DeclaringType)) - { - return Create(DeclaringType, mw); - } - else if (!ifmethod.IsAbstract && !mw.IsAbstract) - { - return AddConflictError(mw); - } - else if (!ifmethod.IsAbstract && mw.IsAbstract) - { - return new ErrorMirandaMethodWrapper(DeclaringType, mw, DeclaringType.Name + "." + Name + Signature); - } - else - { - return this; - } - } - - protected virtual MirandaMethodWrapper AddConflictError(MethodWrapper mw) - { - return new ErrorMirandaMethodWrapper(DeclaringType, ifmethod, "Conflicting default methods:") - .AddConflictError(ifmethod) - .AddConflictError(mw); - } - - internal bool IsConflictError - { - get { return Error != null && Error.StartsWith("Conflicting default methods:"); } - } - - internal MethodWrapper BaseMethod - { - get { return ifmethod; } - } - - internal virtual string Error - { - get { return null; } - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, GetMethod()); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Callvirt, GetMethod()); - } -#endif // EMITTERS - } - - sealed class GhostMethodWrapper : SmartMethodWrapper - { - private MethodInfo ghostMethod; - private MethodInfo defaultImpl; - - internal GhostMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, MethodInfo ghostMethod, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) - { - // make sure we weren't handed the ghostMethod in the wrapper value type - Debug.Assert(method == null || method.DeclaringType.IsInterface); - this.ghostMethod = ghostMethod; - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, defaultImpl); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, ghostMethod); - } -#endif - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - [HideFromJava] - internal override object Invoke(object obj, object[] args) - { - return InvokeAndUnwrapException(ghostMethod, DeclaringType.GhostWrap(obj), args); - } -#endif - - internal void SetDefaultImpl(MethodInfo impl) - { - this.defaultImpl = impl; - } - - internal MethodInfo GetDefaultImpl() - { - return defaultImpl; - } - -#if STATIC_COMPILER - internal void SetGhostMethod(MethodBuilder mb) - { - this.ghostMethod = mb; - } - - internal MethodBuilder GetGhostMethod() - { - return (MethodBuilder)ghostMethod; - } -#endif - } - - sealed class AccessStubMethodWrapper : SmartMethodWrapper - { - private readonly MethodInfo stubVirtual; - private readonly MethodInfo stubNonVirtual; - - internal AccessStubMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo core, MethodInfo stubVirtual, MethodInfo stubNonVirtual, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, core, returnType, parameterTypes, modifiers, flags) - { - this.stubVirtual = stubVirtual; - this.stubNonVirtual = stubNonVirtual; - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, stubNonVirtual); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Callvirt, stubVirtual); - } -#endif // EMITTERS - } - - sealed class AccessStubConstructorMethodWrapper : SmartMethodWrapper - { - private readonly ConstructorInfo stub; - - internal AccessStubConstructorMethodWrapper(TypeWrapper declaringType, string sig, ConstructorInfo core, ConstructorInfo stub, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, StringConstants.INIT, sig, core, PrimitiveTypeWrapper.VOID, parameterTypes, modifiers, flags) - { - this.stub = stub; - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, stub); - } - - protected override void NewobjImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Newobj, stub); - } -#endif // EMITTERS - } - - sealed class DefaultInterfaceMethodWrapper : SmartMethodWrapper - { - private MethodInfo impl; - - internal DefaultInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo ifmethod, MethodInfo impl, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, ifmethod, returnType, parameterTypes, modifiers, flags) - { - this.impl = impl; - } - - internal static MethodInfo GetImpl(MethodWrapper mw) - { - DefaultInterfaceMethodWrapper dimw = mw as DefaultInterfaceMethodWrapper; - if (dimw != null) - { - return dimw.impl; - } - else - { - return ((GhostMethodWrapper)mw).GetDefaultImpl(); - } - } - - internal static void SetImpl(MethodWrapper mw, MethodInfo impl) - { - DefaultInterfaceMethodWrapper dimw = mw as DefaultInterfaceMethodWrapper; - if (dimw != null) - { - dimw.impl = impl; - } - else - { - ((GhostMethodWrapper)mw).SetDefaultImpl(impl); - } - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, impl); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Callvirt, GetMethod()); - } -#endif // EMITTERS - } - - sealed class PrivateInterfaceMethodWrapper : SmartMethodWrapper - { - internal PrivateInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) - { - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, GetMethod()); - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, GetMethod()); - } -#endif // EMITTERS - } - - abstract class FieldWrapper : MemberWrapper - { -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR - private volatile java.lang.reflect.Field reflectionField; - private sun.reflect.FieldAccessor jniAccessor; -#endif - internal static readonly FieldWrapper[] EmptyArray = new FieldWrapper[0]; - private FieldInfo field; - private TypeWrapper fieldType; - - internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, MemberFlags flags) - : base(declaringType, name, sig, modifiers, flags) - { - Debug.Assert(name != null); - Debug.Assert(sig != null); - this.fieldType = fieldType; - this.field = field; - UpdateNonPublicTypeInSignatureFlag(); -#if STATIC_COMPILER - if (IsFinal - && DeclaringType.IsPublic - && !DeclaringType.IsInterface - && (IsPublic || (IsProtected && !DeclaringType.IsFinal)) - && !DeclaringType.GetClassLoader().StrictFinalFieldSemantics - && DeclaringType.IsDynamic - && !(this is ConstantFieldWrapper) - && !(this is DynamicPropertyFieldWrapper)) - { - SetType2FinalField(); - } -#endif - } - - internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, ExModifiers modifiers, FieldInfo field) - : this(declaringType, fieldType, name, sig, modifiers.Modifiers, field, - (modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None)) - { - } - - private void UpdateNonPublicTypeInSignatureFlag() - { - if ((IsPublic || IsProtected) && fieldType != null && !IsAccessStub) - { - if (!fieldType.IsPublic && !fieldType.IsUnloadable) - { - SetNonPublicTypeInSignatureFlag(); - } - } - } - - internal FieldInfo GetField() - { - AssertLinked(); - return field; - } - - [Conditional("DEBUG")] - internal void AssertLinked() - { - if (fieldType == null) - { - Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature + ")"); - } - Debug.Assert(fieldType != null, this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature + ")"); - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal static FieldWrapper FromField(java.lang.reflect.Field field) - { -#if FIRST_PASS - return null; -#else - int slot = field._slot(); - if (slot == -1) - { - // it's a Field created by Unsafe.objectFieldOffset(Class,String) so we must resolve based on the name - foreach (FieldWrapper fw in TypeWrapper.FromClass(field.getDeclaringClass()).GetFields()) - { - if (fw.Name == field.getName()) - { - return fw; - } - } - } - return TypeWrapper.FromClass(field.getDeclaringClass()).GetFields()[slot]; -#endif - } - - internal object ToField(bool copy) - { - return ToField(copy, null); - } - - internal object ToField(bool copy, int? fieldIndex) - { -#if FIRST_PASS - return null; -#else - java.lang.reflect.Field field = reflectionField; - if (field == null) - { - const Modifiers ReflectionFieldModifiersMask = Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static - | Modifiers.Final | Modifiers.Volatile | Modifiers.Transient | Modifiers.Synthetic | Modifiers.Enum; - Link(); - field = new java.lang.reflect.Field( - this.DeclaringType.ClassObject, - this.Name, - this.FieldTypeWrapper.EnsureLoadable(this.DeclaringType.GetClassLoader()).ClassObject, - (int)(this.Modifiers & ReflectionFieldModifiersMask) | (this.IsInternal ? 0x40000000 : 0), - fieldIndex ?? Array.IndexOf(this.DeclaringType.GetFields(), this), - this.DeclaringType.GetGenericFieldSignature(this), - null - ); - } - lock (this) - { - if (reflectionField == null) - { - reflectionField = field; - } - else - { - field = reflectionField; - } - } - if (copy) - { - field = field.copy(); - } - return field; -#endif // FIRST_PASS - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - - [System.Security.SecurityCritical] - internal static FieldWrapper FromCookie(IntPtr cookie) - { - return (FieldWrapper)FromCookieImpl(cookie); - } - - internal TypeWrapper FieldTypeWrapper - { - get - { - AssertLinked(); - return fieldType; - } - } - -#if EMITTERS - internal void EmitGet(CodeEmitter ilgen) - { - AssertLinked(); - EmitGetImpl(ilgen); - } - - protected abstract void EmitGetImpl(CodeEmitter ilgen); - - internal void EmitSet(CodeEmitter ilgen) - { - AssertLinked(); - EmitSetImpl(ilgen); - } - - protected abstract void EmitSetImpl(CodeEmitter ilgen); -#endif // EMITTERS - - -#if STATIC_COMPILER - internal bool IsLinked - { - get { return fieldType != null; } - } -#endif - - internal void Link() - { - Link(LoadMode.Link); - } - - internal void Link(LoadMode mode) - { - lock (this) - { - if (fieldType != null) - { - return; - } - } - TypeWrapper fld = this.DeclaringType.GetClassLoader().FieldTypeWrapperFromSig(Signature, mode); - lock (this) - { - try - { - // critical code in the finally block to avoid Thread.Abort interrupting the thread - } - finally - { - if (fieldType == null) - { - fieldType = fld; - UpdateNonPublicTypeInSignatureFlag(); - try - { - field = this.DeclaringType.LinkField(this); - } - catch - { - // HACK if linking fails, we unlink to make sure - // that the next link attempt will fail again - fieldType = null; - throw; - } - } - } - } - } - - internal bool IsVolatile - { - get - { - return (Modifiers & Modifiers.Volatile) != 0; - } - } - - internal bool IsSerialVersionUID - { - get - { - // a serialVersionUID field must be static and final to be recognized (see ObjectStreamClass.getDeclaredSUID()) - return (Modifiers & (Modifiers.Static | Modifiers.Final)) == (Modifiers.Static | Modifiers.Final) - && Name == "serialVersionUID" - && (FieldTypeWrapper == PrimitiveTypeWrapper.LONG - || FieldTypeWrapper == PrimitiveTypeWrapper.INT - || FieldTypeWrapper == PrimitiveTypeWrapper.CHAR - || FieldTypeWrapper == PrimitiveTypeWrapper.SHORT - || FieldTypeWrapper == PrimitiveTypeWrapper.BYTE); - } - } - - internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) - { - // volatile long & double field accesses must be made atomic - if ((modifiers.Modifiers & Modifiers.Volatile) != 0 && (sig == "J" || sig == "D")) - { - return new VolatileLongDoubleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers); - } - return new SimpleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers); - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal virtual void ResolveField() - { - FieldBuilder fb = field as FieldBuilder; - if(fb != null) - { -#if NETFRAMEWORK - field = fb.Module.ResolveField(fb.GetToken().Token); -#else - BindingFlags flags = BindingFlags.DeclaredOnly; - flags |= fb.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; - flags |= fb.IsStatic ? BindingFlags.Static : BindingFlags.Instance; - field = DeclaringType.TypeAsTBD.GetField(fb.Name, flags); -#endif - } - } - - internal object GetFieldAccessorJNI() - { -#if FIRST_PASS - return null; -#else - if (jniAccessor == null) - { - Interlocked.CompareExchange(ref jniAccessor, IKVM.Java.Externs.sun.reflect.ReflectionFactory.NewFieldAccessorJNI(this), null); - } - return jniAccessor; -#endif - } - -#if !FIRST_PASS - internal abstract object GetValue(object obj); - internal abstract void SetValue(object obj, object value); -#endif -#endif // !STATIC_COMPILER && !STUB_GENERATOR - } - - sealed class SimpleFieldWrapper : FieldWrapper - { - internal SimpleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) - : base(declaringType, fieldType, name, sig, modifiers, fi) - { - Debug.Assert(!(fieldType == PrimitiveTypeWrapper.DOUBLE || fieldType == PrimitiveTypeWrapper.LONG) || !IsVolatile); - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - if (!IsStatic && DeclaringType.IsNonPrimitiveValueType) - { - ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); - } - if (IsVolatile) - { - ilgen.Emit(OpCodes.Volatile); - } - ilgen.Emit(IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, GetField()); - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - FieldInfo fi = GetField(); - if (!IsStatic && DeclaringType.IsNonPrimitiveValueType) - { - CodeEmitterLocal temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temp); - ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); - ilgen.Emit(OpCodes.Ldloc, temp); - } - if (IsVolatile) - { - ilgen.Emit(OpCodes.Volatile); - } - ilgen.Emit(IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi); - if (IsVolatile) - { - ilgen.EmitMemoryBarrier(); - } - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - internal override object GetValue(object obj) - { - return GetField().GetValue(obj); - } - - internal override void SetValue(object obj, object value) - { - GetField().SetValue(obj, value); - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - } - - sealed class VolatileLongDoubleFieldWrapper : FieldWrapper - { - internal VolatileLongDoubleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) - : base(declaringType, fieldType, name, sig, modifiers, fi) - { - Debug.Assert(IsVolatile); - Debug.Assert(sig == "J" || sig == "D"); - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - FieldInfo fi = GetField(); - if (fi.IsStatic) - { - ilgen.Emit(OpCodes.Ldsflda, fi); - } - else - { - if (DeclaringType.IsNonPrimitiveValueType) - { - ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); - } - ilgen.Emit(OpCodes.Ldflda, fi); - } - if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadDouble); - } - else - { - Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadLong); - } - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - FieldInfo fi = GetField(); - CodeEmitterLocal temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temp); - if (fi.IsStatic) - { - ilgen.Emit(OpCodes.Ldsflda, fi); - } - else - { - if (DeclaringType.IsNonPrimitiveValueType) - { - ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); - } - ilgen.Emit(OpCodes.Ldflda, fi); - } - ilgen.Emit(OpCodes.Ldloc, temp); - if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) - { - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteDouble); - } - else - { - Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteLong); - } - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS -#if NO_REF_EMIT - internal static readonly object lockObject = new object(); -#endif - - internal override object GetValue(object obj) - { -#if NO_REF_EMIT - lock (lockObject) - { - return GetField().GetValue(obj); - } -#else - throw new InvalidOperationException(); -#endif - } - - internal override void SetValue(object obj, object value) - { -#if NO_REF_EMIT - lock (lockObject) - { - GetField().SetValue(obj, value); - } -#else - throw new InvalidOperationException(); -#endif - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - } - -#if !STUB_GENERATOR - // this class represents a .NET property defined in Java with the ikvm.lang.Property annotation - sealed class DynamicPropertyFieldWrapper : FieldWrapper - { - private readonly MethodWrapper getter; - private readonly MethodWrapper setter; - private PropertyBuilder pb; - - private MethodWrapper GetMethod(string name, string sig, bool isstatic) - { - if (name != null) - { - MethodWrapper mw = this.DeclaringType.GetMethodWrapper(name, sig, false); - if (mw != null && mw.IsStatic == isstatic) - { - mw.IsPropertyAccessor = true; - return mw; - } - Tracer.Error(Tracer.Compiler, "Property '{0}' accessor '{1}' not found in class '{2}'", this.Name, name, this.DeclaringType.Name); - } - return null; - } - - internal DynamicPropertyFieldWrapper(TypeWrapper declaringType, ClassFile.Field fld) - : base(declaringType, null, fld.Name, fld.Signature, new ExModifiers(fld.Modifiers, fld.IsInternal), null) - { - getter = GetMethod(fld.PropertyGetter, "()" + fld.Signature, fld.IsStatic); - setter = GetMethod(fld.PropertySetter, "(" + fld.Signature + ")V", fld.IsStatic); - } - -#if !STATIC_COMPILER && !FIRST_PASS - internal override void ResolveField() - { - if (getter != null) - { - getter.ResolveMethod(); - } - if (setter != null) - { - setter.ResolveMethod(); - } - } -#endif - - internal PropertyBuilder GetPropertyBuilder() - { - AssertLinked(); - return pb; - } - - internal void DoLink(TypeBuilder tb) - { - if (getter != null) - { - getter.Link(); - } - if (setter != null) - { - setter.Link(); - } - pb = tb.DefineProperty(this.Name, PropertyAttributes.None, this.FieldTypeWrapper.TypeAsSignatureType, Type.EmptyTypes); - if (getter != null) - { - pb.SetGetMethod((MethodBuilder)getter.GetMethod()); - } - if (setter != null) - { - pb.SetSetMethod((MethodBuilder)setter.GetMethod()); - } -#if STATIC_COMPILER - AttributeHelper.SetModifiers(pb, this.Modifiers, this.IsInternal); -#endif - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - if (getter == null) - { - EmitThrowNoSuchMethodErrorForGetter(ilgen, this.FieldTypeWrapper, this); - } - else if (getter.IsStatic) - { - getter.EmitCall(ilgen); - } - else - { - getter.EmitCallvirt(ilgen); - } - } - - internal static void EmitThrowNoSuchMethodErrorForGetter(CodeEmitter ilgen, TypeWrapper type, MemberWrapper member) - { -#if STATIC_COMPILER - StaticCompiler.IssueMessage(Message.EmittedNoSuchMethodError, "", member.DeclaringType.Name + "." + member.Name + member.Signature); -#endif - // HACK the branch around the throw is to keep the verifier happy - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.Emit(OpCodes.Ldc_I4_0); - ilgen.EmitBrtrue(label); - ilgen.EmitThrow("java.lang.NoSuchMethodError"); - ilgen.MarkLabel(label); - if (!member.IsStatic) - { - ilgen.Emit(OpCodes.Pop); - } - ilgen.Emit(OpCodes.Ldloc, ilgen.DeclareLocal(type.TypeAsLocalOrStackType)); - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - if (setter == null) - { - if (this.IsFinal) - { - ilgen.Emit(OpCodes.Pop); - if (!this.IsStatic) - { - ilgen.Emit(OpCodes.Pop); - } - } - else - { - EmitThrowNoSuchMethodErrorForSetter(ilgen, this); - } - } - else if (setter.IsStatic) - { - setter.EmitCall(ilgen); - } - else - { - setter.EmitCallvirt(ilgen); - } - } - - internal static void EmitThrowNoSuchMethodErrorForSetter(CodeEmitter ilgen, MemberWrapper member) - { -#if STATIC_COMPILER - StaticCompiler.IssueMessage(Message.EmittedNoSuchMethodError, "", member.DeclaringType.Name + "." + member.Name + member.Signature); -#endif - // HACK the branch around the throw is to keep the verifier happy - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.Emit(OpCodes.Ldc_I4_0); - ilgen.EmitBrtrue(label); - ilgen.EmitThrow("java.lang.NoSuchMethodError"); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Pop); - if (!member.IsStatic) - { - ilgen.Emit(OpCodes.Pop); - } - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !FIRST_PASS - internal override object GetValue(object obj) - { - if (getter == null) - { - throw new java.lang.NoSuchMethodError(); - } - return getter.Invoke(obj, new object[0]); - } - - internal override void SetValue(object obj, object value) - { - if (setter == null) - { - throw new java.lang.NoSuchMethodError(); - } - setter.Invoke(obj, new object[] { value }); - } -#endif - } -#endif // !STUB_GENERATOR - - // this class represents a .NET property defined in Java with the ikvm.lang.Property annotation - sealed class CompiledPropertyFieldWrapper : FieldWrapper - { - private readonly PropertyInfo property; - - internal CompiledPropertyFieldWrapper(TypeWrapper declaringType, PropertyInfo property, ExModifiers modifiers) - : base(declaringType, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType), property.Name, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType).SigName, modifiers, null) - { - this.property = property; - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - MethodInfo getter = property.GetGetMethod(true); - if (getter == null) - { - DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForGetter(ilgen, this.FieldTypeWrapper, this); - } - else if (getter.IsStatic) - { - ilgen.Emit(OpCodes.Call, getter); - } - else - { - ilgen.Emit(OpCodes.Callvirt, getter); - } - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - MethodInfo setter = property.GetSetMethod(true); - if (setter == null) - { - if (this.IsFinal) - { - ilgen.Emit(OpCodes.Pop); - if (!this.IsStatic) - { - ilgen.Emit(OpCodes.Pop); - } - } - else - { - DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForSetter(ilgen, this); - } - } - else if (setter.IsStatic) - { - ilgen.Emit(OpCodes.Call, setter); - } - else - { - ilgen.Emit(OpCodes.Callvirt, setter); - } - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - internal override object GetValue(object obj) - { - MethodInfo getter = property.GetGetMethod(true); - if (getter == null) - { - throw new java.lang.NoSuchMethodError(); - } - return getter.Invoke(obj, new object[0]); - } - - internal override void SetValue(object obj, object value) - { - MethodInfo setter = property.GetSetMethod(true); - if (setter == null) - { - throw new java.lang.NoSuchMethodError(); - } - setter.Invoke(obj, new object[] { value }); - } -#endif - - internal PropertyInfo GetProperty() - { - return property; - } - } - - sealed class ConstantFieldWrapper : FieldWrapper - { - // NOTE this field wrapper can resprent a .NET enum, but in that case "constant" contains the raw constant value (i.e. the boxed underlying primitive value, not a boxed enum) - private object constant; - - internal ConstantFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, object constant, MemberFlags flags) - : base(declaringType, fieldType, name, sig, modifiers, field, flags) - { - Debug.Assert(IsStatic); - Debug.Assert(constant == null || constant.GetType().IsPrimitive || constant is string); - this.constant = constant; - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - // Reading a field should trigger the cctor, but since we're inlining the value - // we have to trigger it explicitly - DeclaringType.EmitRunClassConstructor(ilgen); - - // NOTE even though you're not supposed to access a constant static final (the compiler is supposed - // to inline them), we have to support it (because it does happen, e.g. if the field becomes final - // after the referencing class was compiled, or when we're accessing an unsigned primitive .NET field) - object v = GetConstantValue(); - if (v == null) - { - ilgen.Emit(OpCodes.Ldnull); - } - else if (constant is int || - constant is short || - constant is ushort || - constant is byte || - constant is sbyte || - constant is char || - constant is bool) - { - ilgen.EmitLdc_I4(((IConvertible)constant).ToInt32(null)); - } - else if (constant is string) - { - ilgen.Emit(OpCodes.Ldstr, (string)constant); - } - else if (constant is float) - { - ilgen.EmitLdc_R4((float)constant); - } - else if (constant is double) - { - ilgen.EmitLdc_R8((double)constant); - } - else if (constant is long) - { - ilgen.EmitLdc_I8((long)constant); - } - else if (constant is uint) - { - ilgen.EmitLdc_I4(unchecked((int)((IConvertible)constant).ToUInt32(null))); - } - else if (constant is ulong) - { - ilgen.EmitLdc_I8(unchecked((long)(ulong)constant)); - } - else - { - throw new InvalidOperationException(constant.GetType().FullName); - } - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - // when constant static final fields are updated, the JIT normally doesn't see that (because the - // constant value is inlined), so we emulate that behavior by emitting a Pop - ilgen.Emit(OpCodes.Pop); - } -#endif // EMITTERS - - internal object GetConstantValue() - { - if (constant == null) - { - FieldInfo field = GetField(); - constant = field.GetRawConstantValue(); - } - return constant; - } - -#if !STUB_GENERATOR && !STATIC_COMPILER && !FIRST_PASS - internal override object GetValue(object obj) - { - FieldInfo field = GetField(); - return FieldTypeWrapper.IsPrimitive || field == null - ? GetConstantValue() - : field.GetValue(null); - } - - internal override void SetValue(object obj, object value) - { - } -#endif - } - - sealed class CompiledAccessStubFieldWrapper : FieldWrapper - { - private readonly MethodInfo getter; - private readonly MethodInfo setter; - - private static Modifiers GetModifiers(PropertyInfo property) - { - // NOTE we only support the subset of modifiers that is expected for "access stub" properties - MethodInfo getter = property.GetGetMethod(true); - Modifiers modifiers = getter.IsPublic ? Modifiers.Public : Modifiers.Protected; - if (!property.CanWrite) - { - modifiers |= Modifiers.Final; - } - if (getter.IsStatic) - { - modifiers |= Modifiers.Static; - } - return modifiers; - } - - // constructor for type 1 access stubs - internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, TypeWrapper propertyType) - : this(wrapper, property, null, propertyType, GetModifiers(property), MemberFlags.HideFromReflection | MemberFlags.AccessStub) - { - } - - // constructor for type 2 access stubs - internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType) - : this(wrapper, property, field, propertyType, AttributeHelper.GetModifiersAttribute(property).Modifiers, MemberFlags.AccessStub) - { - } - - private CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType, Modifiers modifiers, MemberFlags flags) - : base(wrapper, propertyType, property.Name, propertyType.SigName, modifiers, field, flags) - { - this.getter = property.GetGetMethod(true); - this.setter = property.GetSetMethod(true); - } - -#if EMITTERS - protected override void EmitGetImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, getter); - } - - protected override void EmitSetImpl(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, setter); - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - internal override object GetValue(object obj) - { - // we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field - return GetField().GetValue(obj); - } - - internal override void SetValue(object obj, object value) - { - // we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field - GetField().SetValue(obj, value); - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - } } diff --git a/src/IKVM.Runtime/Memory/ExecutableMemory.cs b/src/IKVM.Runtime/Memory/ExecutableMemory.cs new file mode 100644 index 0000000000..9974b557eb --- /dev/null +++ b/src/IKVM.Runtime/Memory/ExecutableMemory.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; + +namespace IKVM.Runtime.JNI.Memory +{ + + /// + /// Abstract type for reference to platform specifiec executable memory allocation. + /// + abstract class ExecutableMemory : SafeHandle + { + + /// + /// Casts the memory to a Span. + /// + /// + + public static unsafe implicit operator Span(ExecutableMemory handle) + { + return new Span((void*)handle.DangerousGetHandle(), handle.size); + } + + /// + /// Allocates executable memory of the specified size. + /// + /// + /// + /// + public static ExecutableMemory Allocate(int size) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Win32ExecutableVirtualMemory.Allocate(size); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return UnixExecutableVirtualMemory.Allocate(size); + else + throw new PlatformNotSupportedException(); + } + + readonly int size; + + /// + /// Initializes a new instance. + /// + protected ExecutableMemory(IntPtr handle, int size) : + base(IntPtr.Zero, true) + { + if (size <= 0) + throw new ArgumentOutOfRangeException(nameof(size)); + + this.size = size; + SetHandle(handle); + } + + /// + /// Gets the size of the allocated region. + /// + public int Size => size; + + /// + /// Returns true if the handle is invalid. + /// + public override bool IsInvalid => handle == IntPtr.Zero; + + } + +} diff --git a/src/IKVM.Runtime/Memory/PosixExecutableVirtualMemory.cs b/src/IKVM.Runtime/Memory/PosixExecutableVirtualMemory.cs new file mode 100644 index 0000000000..25f0baced3 --- /dev/null +++ b/src/IKVM.Runtime/Memory/PosixExecutableVirtualMemory.cs @@ -0,0 +1,61 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +using Mono.Unix.Native; + +namespace IKVM.Runtime.JNI.Memory +{ + + /// + /// Maintains a reference to a region of memory allocated by VirtualAlloc. + /// + class UnixExecutableVirtualMemory : ExecutableMemory + { + + /// + /// Creates a new region of executable virtual memory. + /// + /// + /// + public new static UnixExecutableVirtualMemory Allocate(int size) + { + if (size <= 0) + throw new ArgumentOutOfRangeException(nameof(size)); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == false && + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) == false) + throw new PlatformNotSupportedException(); + + var handle = Syscall.mmap(IntPtr.Zero, (ulong)size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC, MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANON, -1, 0); + if (handle == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + return new UnixExecutableVirtualMemory(handle, size); + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public UnixExecutableVirtualMemory(IntPtr handle, int size) : + base(handle, size) + { + + } + + /// + /// Releases the allocated memory. + /// + /// + protected override bool ReleaseHandle() + { + Stdlib.free(DangerousGetHandle()); + return true; + } + + } + +} diff --git a/src/IKVM.Runtime/Memory/Win32ExecutableVirtualMemory.cs b/src/IKVM.Runtime/Memory/Win32ExecutableVirtualMemory.cs new file mode 100644 index 0000000000..82a5b5863f --- /dev/null +++ b/src/IKVM.Runtime/Memory/Win32ExecutableVirtualMemory.cs @@ -0,0 +1,102 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace IKVM.Runtime.JNI.Memory +{ + + /// + /// Maintains a reference to a region of memory allocated by VirtualAlloc. + /// + class Win32ExecutableVirtualMemory : ExecutableMemory + { + + [Flags] + public enum AllocationType + { + Commit = 0x1000, + Reserve = 0x2000, + Decommit = 0x4000, + Release = 0x8000, + Reset = 0x80000, + Physical = 0x400000, + TopDown = 0x100000, + WriteWatch = 0x200000, + LargePages = 0x20000000 + } + + [Flags] + public enum MemoryProtection + { + Execute = 0x10, + ExecuteRead = 0x20, + ExecuteReadWrite = 0x40, + ExecuteWriteCopy = 0x80, + NoAccess = 0x01, + ReadOnly = 0x02, + ReadWrite = 0x04, + WriteCopy = 0x08, + GuardModifierflag = 0x100, + NoCacheModifierflag = 0x200, + WriteCombineModifierflag = 0x400 + } + + [Flags] + public enum FreeType + { + MEM_DECOMMIT = 0x00004000, + MEM_RELEASE = 0x00008000, + MEM_COALESCE_PLACEHOLDERS = 0x00000001, + MEM_PRESERVE_PLACEHOLDER = 0x00000002 + } + + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] + static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); + + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] + static extern int VirtualFree(IntPtr lpAddress, IntPtr dwSize, FreeType freeType); + + /// + /// Creates a new region of executable virtual memory. + /// + /// + /// + public new static Win32ExecutableVirtualMemory Allocate(int size) + { + if (size <= 0) + throw new ArgumentOutOfRangeException(nameof(size)); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == false) + throw new PlatformNotSupportedException(); + + var handle = VirtualAlloc(IntPtr.Zero, (IntPtr)size, AllocationType.Commit | AllocationType.Reserve, MemoryProtection.ExecuteReadWrite); + if (handle == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + return new Win32ExecutableVirtualMemory(handle, size); + } + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public Win32ExecutableVirtualMemory(IntPtr handle, int size) : + base(handle, size) + { + + } + + /// + /// Releases the allocated memory. + /// + /// + protected override bool ReleaseHandle() + { + return VirtualFree(DangerousGetHandle(), (IntPtr)Size, FreeType.MEM_RELEASE) == 0; + } + + } + +} diff --git a/src/IKVM.Runtime/MethodHandleUtil.compiler.cs b/src/IKVM.Runtime/MethodHandleUtil.compiler.cs index 53416c3167..0545289ae7 100644 --- a/src/IKVM.Runtime/MethodHandleUtil.compiler.cs +++ b/src/IKVM.Runtime/MethodHandleUtil.compiler.cs @@ -1,6 +1,6 @@ using System; -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; diff --git a/src/IKVM.Runtime/MethodHandleUtil.jniexport.cs b/src/IKVM.Runtime/MethodHandleUtil.jniexport.cs index b9d7267633..ac172202e3 100644 --- a/src/IKVM.Runtime/MethodHandleUtil.jniexport.cs +++ b/src/IKVM.Runtime/MethodHandleUtil.jniexport.cs @@ -21,13 +21,14 @@ Jeroen Frijters jeroen@frijters.net */ -#if !STATIC_COMPILER +#if !IMPORTER using System; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using IKVM.Internal; +using IKVM.Runtime; static partial class MethodHandleUtil { @@ -623,7 +624,7 @@ private static Delegate ValidateDelegate(Delegate d) } catch (Exception x) { - JVM.CriticalFailure("Delegate failed to JIT", x); + throw new InternalException("Delegate failed to JIT", x); } #endif return d; diff --git a/src/IKVM.Runtime/MethodHandleUtil.root.cs b/src/IKVM.Runtime/MethodHandleUtil.root.cs index 38f628f814..5a66a1c6e7 100644 --- a/src/IKVM.Runtime/MethodHandleUtil.root.cs +++ b/src/IKVM.Runtime/MethodHandleUtil.root.cs @@ -25,9 +25,11 @@ Jeroen Frijters using IKVM.Internal; -#if STATIC_COMPILER +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; #else using System.Reflection; @@ -36,14 +38,16 @@ Jeroen Frijters static partial class MethodHandleUtil { + internal const int MaxArity = 8; - private static readonly Type typeofMHA; - private static readonly Type[] typeofMHV; - private static readonly Type[] typeofMH; + + static readonly Type typeofMHA; + static readonly Type[] typeofMHV; + static readonly Type[] typeofMH; static MethodHandleUtil() { -#if STATIC_COMPILER +#if IMPORTER typeofMHA = StaticCompiler.GetRuntimeType("IKVM.Runtime.MHA`8"); typeofMHV = new Type[] { StaticCompiler.GetRuntimeType("IKVM.Runtime.MHV"), @@ -121,7 +125,7 @@ internal static Type CreateMemberWrapperDelegateType(TypeWrapper[] args, TypeWra return CreateDelegateType(typeArgs, AsBasicType(ret)); } - private static Type CreateDelegateType(Type[] types, Type retType) + static Type CreateDelegateType(Type[] types, Type retType) { if (types.Length == 0 && retType == Types.Void) { @@ -137,16 +141,19 @@ private static Type CreateDelegateType(Type[] types, Type retType) remainder = 7; count--; } - Type last = typeofMHA.MakeGenericType(SubArray(types, types.Length - 8, 8)); + + var last = typeofMHA.MakeGenericType(SubArray(types, types.Length - 8, 8)); for (int i = 0; i < count; i++) { - Type[] temp = SubArray(types, types.Length - 8 - 7 * (i + 1), 8); + var temp = SubArray(types, types.Length - 8 - 7 * (i + 1), 8); temp[7] = last; last = typeofMHA.MakeGenericType(temp); } + types = SubArray(types, 0, remainder + 1); types[remainder] = last; } + if (retType == Types.Void) { return typeofMHV[types.Length].MakeGenericType(types); @@ -158,9 +165,9 @@ private static Type CreateDelegateType(Type[] types, Type retType) } } - private static Type[] SubArray(Type[] inArray, int start, int length) + static Type[] SubArray(Type[] inArray, int start, int length) { - Type[] outArray = new Type[length]; + var outArray = new Type[length]; Array.Copy(inArray, start, outArray, 0, length); return outArray; } @@ -168,32 +175,23 @@ private static Type[] SubArray(Type[] inArray, int start, int length) internal static Type AsBasicType(TypeWrapper tw) { if (tw == PrimitiveTypeWrapper.BOOLEAN || tw == PrimitiveTypeWrapper.BYTE || tw == PrimitiveTypeWrapper.CHAR || tw == PrimitiveTypeWrapper.SHORT || tw == PrimitiveTypeWrapper.INT) - { return Types.Int32; - } else if (tw == PrimitiveTypeWrapper.LONG || tw == PrimitiveTypeWrapper.FLOAT || tw == PrimitiveTypeWrapper.DOUBLE || tw == PrimitiveTypeWrapper.VOID) - { return tw.TypeAsSignatureType; - } else - { return Types.Object; - } } internal static bool HasOnlyBasicTypes(TypeWrapper[] args, TypeWrapper ret) { - foreach (TypeWrapper tw in args) - { + foreach (var tw in args) if (!IsBasicType(tw)) - { return false; - } - } + return IsBasicType(ret); } - private static bool IsBasicType(TypeWrapper tw) + static bool IsBasicType(TypeWrapper tw) { return tw == PrimitiveTypeWrapper.INT || tw == PrimitiveTypeWrapper.LONG diff --git a/src/IKVM.Runtime/ClassFile/ClassFile.RefKind.cs b/src/IKVM.Runtime/MethodParametersEntry.cs similarity index 78% rename from src/IKVM.Runtime/ClassFile/ClassFile.RefKind.cs rename to src/IKVM.Runtime/MethodParametersEntry.cs index d8d0d214ac..b365da095b 100644 --- a/src/IKVM.Runtime/ClassFile/ClassFile.RefKind.cs +++ b/src/IKVM.Runtime/MethodParametersEntry.cs @@ -22,23 +22,19 @@ Jeroen Frijters */ +using IKVM.ByteCode; + namespace IKVM.Internal { - sealed partial class ClassFile - { - internal enum RefKind - { - getField = 1, - getStatic = 2, - putField = 3, - putStatic = 4, - invokeVirtual = 5, - invokeStatic = 6, - invokeSpecial = 7, - newInvokeSpecial = 8, - invokeInterface = 9 - } - } + struct MethodParametersEntry + { + + internal static readonly MethodParametersEntry[] Malformed = new MethodParametersEntry[0]; + + internal string name; + internal AccessFlag accessFlags; + + } } diff --git a/src/IKVM.Runtime/MethodWrapper.cs b/src/IKVM.Runtime/MethodWrapper.cs new file mode 100644 index 0000000000..bd935d6744 --- /dev/null +++ b/src/IKVM.Runtime/MethodWrapper.cs @@ -0,0 +1,599 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Diagnostics; + +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + abstract class MethodWrapper : MemberWrapper + { + +#if !IMPORTER && !FIRST_PASS && !EXPORTER + volatile java.lang.reflect.Executable reflectionMethod; +#endif + internal static readonly MethodWrapper[] EmptyArray = new MethodWrapper[0]; + MethodBase method; + string[] declaredExceptions; + TypeWrapper returnTypeWrapper; + TypeWrapper[] parameterTypeWrappers; + +#if EMITTERS + + internal virtual void EmitCall(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + + internal virtual void EmitCallvirt(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + + internal virtual void EmitCallvirtReflect(CodeEmitter ilgen) + { + EmitCallvirt(ilgen); + } + + internal virtual void EmitNewobj(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + + internal virtual bool EmitIntrinsic(EmitIntrinsicContext context) + { + return Intrinsics.Emit(context); + } + +#endif // EMITTERS + + internal virtual bool IsDynamicOnly => false; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal MethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, modifiers, flags) + { + Profiler.Count("MethodWrapper"); + + this.method = method; + Debug.Assert(((returnType == null) == (parameterTypes == null)) || (returnType == PrimitiveTypeWrapper.VOID)); + this.returnTypeWrapper = returnType; + this.parameterTypeWrappers = parameterTypes; + if (Intrinsics.IsIntrinsic(this)) + SetIntrinsicFlag(); + + UpdateNonPublicTypeInSignatureFlag(); + } + + void UpdateNonPublicTypeInSignatureFlag() + { + if ((IsPublic || IsProtected) && (returnTypeWrapper != null && parameterTypeWrappers != null) && !(this is AccessStubMethodWrapper) && !(this is AccessStubConstructorMethodWrapper)) + { + if (!returnTypeWrapper.IsPublic && !returnTypeWrapper.IsUnloadable) + { + SetNonPublicTypeInSignatureFlag(); + } + else + { + foreach (TypeWrapper tw in parameterTypeWrappers) + { + if (!tw.IsPublic && !tw.IsUnloadable) + { + SetNonPublicTypeInSignatureFlag(); + break; + } + } + } + } + } + + internal void SetDeclaredExceptions(string[] exceptions) + { + if (exceptions == null) + { + exceptions = new string[0]; + } + this.declaredExceptions = (string[])exceptions.Clone(); + } + + internal string[] GetDeclaredExceptions() + { + return declaredExceptions; + } + +#if !IMPORTER && !EXPORTER + internal java.lang.reflect.Executable ToMethodOrConstructor(bool copy) + { +#if FIRST_PASS + return null; +#else + java.lang.reflect.Executable method = reflectionMethod; + if (method == null) + { + Link(); + ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader(); + TypeWrapper[] argTypes = GetParameters(); + java.lang.Class[] parameterTypes = new java.lang.Class[argTypes.Length]; + for (int i = 0; i < argTypes.Length; i++) + { + parameterTypes[i] = argTypes[i].EnsureLoadable(loader).ClassObject; + } + java.lang.Class[] checkedExceptions = GetExceptions(); + if (this.IsConstructor) + { + method = new java.lang.reflect.Constructor( + this.DeclaringType.ClassObject, + parameterTypes, + checkedExceptions, + (int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0), + Array.IndexOf(this.DeclaringType.GetMethods(), this), + this.DeclaringType.GetGenericMethodSignature(this), + null, + null + ); + } + else + { + method = new java.lang.reflect.Method( + this.DeclaringType.ClassObject, + this.Name, + parameterTypes, + this.ReturnType.EnsureLoadable(loader).ClassObject, + checkedExceptions, + (int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0), + Array.IndexOf(this.DeclaringType.GetMethods(), this), + this.DeclaringType.GetGenericMethodSignature(this), + null, + null, + null + ); + } + lock (this) + { + if (reflectionMethod == null) + { + reflectionMethod = method; + } + else + { + method = reflectionMethod; + } + } + } + if (copy) + { + java.lang.reflect.Constructor ctor = method as java.lang.reflect.Constructor; + if (ctor != null) + { + return ctor.copy(); + } + return ((java.lang.reflect.Method)method).copy(); + } + return method; +#endif + } + +#if !FIRST_PASS + private java.lang.Class[] GetExceptions() + { + string[] classes = declaredExceptions; + Type[] types = Type.EmptyTypes; + if (classes == null) + { + // NOTE if method is a MethodBuilder, GetCustomAttributes doesn't work (and if + // the method had any declared exceptions, the declaredExceptions field would have + // been set) + if (method != null && !(method is MethodBuilder)) + { + ThrowsAttribute attr = AttributeHelper.GetThrows(method); + if (attr != null) + { + classes = attr.classes; + types = attr.types; + } + } + } + if (classes != null) + { + java.lang.Class[] array = new java.lang.Class[classes.Length]; + for (int i = 0; i < classes.Length; i++) + { + array[i] = this.DeclaringType.GetClassLoader().LoadClassByDottedName(classes[i]).ClassObject; + } + return array; + } + else + { + java.lang.Class[] array = new java.lang.Class[types.Length]; + for (int i = 0; i < types.Length; i++) + { + array[i] = types[i]; + } + return array; + } + } +#endif // !FIRST_PASS + + internal static MethodWrapper FromExecutable(java.lang.reflect.Executable executable) + { +#if FIRST_PASS + return null; +#else + return TypeWrapper.FromClass(executable.getDeclaringClass()).GetMethods()[executable._slot()]; +#endif + } +#endif // !IMPORTER && !EXPORTER + + [System.Security.SecurityCritical] + internal static MethodWrapper FromCookie(IntPtr cookie) + { + return (MethodWrapper)FromCookieImpl(cookie); + } + + internal bool IsLinked + { + get + { + return parameterTypeWrappers != null; + } + } + + internal void Link() + { + Link(LoadMode.Link); + } + + internal void Link(LoadMode mode) + { + lock (this) + { + if (parameterTypeWrappers != null) + { + return; + } + } + ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader(); + TypeWrapper ret = loader.RetTypeWrapperFromSig(Signature, mode); + TypeWrapper[] parameters = loader.ArgTypeWrapperListFromSig(Signature, mode); + lock (this) + { + try + { + // critical code in the finally block to avoid Thread.Abort interrupting the thread + } + finally + { + if (parameterTypeWrappers == null) + { + Debug.Assert(returnTypeWrapper == null || returnTypeWrapper == PrimitiveTypeWrapper.VOID); + returnTypeWrapper = ret; + parameterTypeWrappers = parameters; + UpdateNonPublicTypeInSignatureFlag(); + if (method == null) + { + try + { + DoLinkMethod(); + } + catch + { + // HACK if linking fails, we unlink to make sure + // that the next link attempt will fail again + returnTypeWrapper = null; + parameterTypeWrappers = null; + throw; + } + } + } + } + } + } + + protected virtual void DoLinkMethod() + { + method = this.DeclaringType.LinkMethod(this); + } + + [Conditional("DEBUG")] + internal void AssertLinked() + { + if (!(parameterTypeWrappers != null && returnTypeWrapper != null)) + { + Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + this.Signature); + } + Debug.Assert(parameterTypeWrappers != null && returnTypeWrapper != null, this.DeclaringType.Name + "::" + this.Name + this.Signature); + } + + internal TypeWrapper ReturnType + { + get + { + AssertLinked(); + return returnTypeWrapper; + } + } + + internal TypeWrapper[] GetParameters() + { + AssertLinked(); + return parameterTypeWrappers; + } + +#if !EXPORTER + internal DefineMethodHelper GetDefineMethodHelper() + { + return new DefineMethodHelper(this); + } +#endif + + internal Type ReturnTypeForDefineMethod + { + get + { + return ReturnType.TypeAsSignatureType; + } + } + + internal Type[] GetParametersForDefineMethod() + { + TypeWrapper[] wrappers = GetParameters(); + int len = wrappers.Length; + if (HasCallerID) + { + len++; + } + Type[] temp = new Type[len]; + for (int i = 0; i < wrappers.Length; i++) + { + temp[i] = wrappers[i].TypeAsSignatureType; + } + if (HasCallerID) + { + temp[len - 1] = CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType; + } + return temp; + } + + // we expose the underlying MethodBase object, + // for Java types, this is the method that contains the compiled Java bytecode + // for remapped types, this is the mbCore method (not the helper) + // Note that for some artificial methods (notably wrap() in enums), method is null + internal MethodBase GetMethod() + { + AssertLinked(); + return method; + } + + internal string RealName + { + get + { + AssertLinked(); + return method.Name; + } + } + + internal bool IsAbstract + { + get + { + return (Modifiers & Modifiers.Abstract) != 0; + } + } + + internal bool RequiresNonVirtualDispatcher + { + get + { + return !IsConstructor + && !IsStatic + && !IsPrivate + && !IsAbstract + && !IsFinal + && !DeclaringType.IsFinal; + } + } + +#if !IMPORTER && !EXPORTER + + internal Type GetDelegateType() + { + var paramTypes = GetParameters(); + if (paramTypes.Length > MethodHandleUtil.MaxArity) + { + var type = DeclaringType.TypeAsBaseType.Assembly.GetType(ReturnType == PrimitiveTypeWrapper.VOID ? "__<>NVIV`" + paramTypes.Length : "__<>NVI`" + (paramTypes.Length + 1)); + if (type == null) + type = DeclaringType.GetClassLoader().GetTypeWrapperFactory().DefineDelegate(paramTypes.Length, ReturnType == PrimitiveTypeWrapper.VOID); + + var types = new Type[paramTypes.Length + (ReturnType == PrimitiveTypeWrapper.VOID ? 0 : 1)]; + for (int i = 0; i < paramTypes.Length; i++) + types[i] = paramTypes[i].TypeAsSignatureType; + + if (ReturnType != PrimitiveTypeWrapper.VOID) + types[types.Length - 1] = ReturnType.TypeAsSignatureType; + + return type.MakeGenericType(types); + } + + return MethodHandleUtil.CreateMemberWrapperDelegateType(paramTypes, ReturnType); + } + + /// + /// Resolves the potentially dynamic member into it's final runtime method. + /// + internal void ResolveMethod() + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + // if we've still got the builder object, we need to replace it with the real thing before we can call it + var mb = method as MethodBuilder; + if (mb != null) + { +#if NETFRAMEWORK + method = mb.Module.ResolveMethod(mb.GetToken().Token); +#else + // though ResolveMethod exists, Core 3.1 does not provide a stable way to obtain the resulting metadata token + // instead we have to scan the methods for the one that matches the signature using the runtime type instances + // FIXME .NET 6 + + var parameters = GetParameters(); + var typeLength = parameters.Length; + if (HasCallerID) + typeLength++; + + var types = new Type[typeLength]; + for (int i = 0; i < parameters.Length; i++) + { + parameters[i].Finish(); + types[i] = parameters[i].TypeAsSignatureType; + } + + if (HasCallerID) + types[typeLength - 1] = CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType; + + var flags = BindingFlags.DeclaredOnly; + flags |= mb.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; + flags |= mb.IsStatic ? BindingFlags.Static : BindingFlags.Instance; + method = DeclaringType.TypeAsTBD.GetMethod(mb.Name, flags, null, types, null); + if (method == null) + method = DeclaringType.TypeAsTBD.GetConstructor(flags, null, types, null); +#endif + } +#endif + } + + [HideFromJava] + internal virtual object InvokeNonvirtualRemapped(object obj, object[] args) + { + throw new InvalidOperationException(); + } + + /// + /// Dynamically invokes the method and potentially unwraps any exceptions. + /// + /// + /// + /// + /// + [HideFromJava] + protected static object InvokeAndUnwrapException(MethodBase mb, object obj, object[] args) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + return mb.Invoke(obj, args); + } + catch (TargetInvocationException e) + { + throw ikvm.runtime.Util.mapException(e.InnerException); + } +#endif + } + + /// + /// Dynamically invokes the method and potentially unwraps any exceptions. + /// + /// + /// + /// + [HideFromJava] + internal virtual object Invoke(object obj, object[] args) + { + return InvokeAndUnwrapException(method, obj, args); + } + + [HideFromJava] + internal virtual object CreateInstance(object[] args) + { +#if FIRST_PASS + throw new NotImplementedException(); +#else + try + { + return ((ConstructorInfo)method).Invoke(args); + } + catch (TargetInvocationException x) + { + throw ikvm.runtime.Util.mapException(x.InnerException); + } +#endif + } + +#endif // !IMPORTER && !EXPORTER + + internal static OpCode SimpleOpCodeToOpCode(SimpleOpCode opc) => opc switch + { + SimpleOpCode.Call => OpCodes.Call, + SimpleOpCode.Callvirt => OpCodes.Callvirt, + SimpleOpCode.Newobj => OpCodes.Newobj, + _ => throw new InvalidOperationException(), + }; + + internal virtual bool IsOptionalAttributeAnnotationValue => false; + + internal bool IsConstructor => Name == (object)StringConstants.INIT; + + internal bool IsClassInitializer => Name == (object)StringConstants.CLINIT; + + internal bool IsVirtual => (modifiers & (Modifiers.Static | Modifiers.Private)) == 0 && !IsConstructor; + + internal bool IsFinalizeOrClone + { + get + { + return IsProtected && (DeclaringType == CoreClasses.java.lang.Object.Wrapper || DeclaringType == CoreClasses.java.lang.Throwable.Wrapper) && (Name == StringConstants.CLONE || Name == StringConstants.FINALIZE); + } + } + } + +} diff --git a/src/IKVM.Runtime/MirandaMethodWrapper.cs b/src/IKVM.Runtime/MirandaMethodWrapper.cs new file mode 100644 index 0000000000..1da17b02ab --- /dev/null +++ b/src/IKVM.Runtime/MirandaMethodWrapper.cs @@ -0,0 +1,197 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + abstract class MirandaMethodWrapper : SmartMethodWrapper + { + + readonly MethodWrapper ifmethod; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + MirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, Modifiers modifiers) : + base(declaringType, ifmethod.Name, ifmethod.Signature, null, null, null, modifiers, MemberFlags.HideFromReflection | MemberFlags.MirandaMethod) + { + this.ifmethod = ifmethod; + } + + sealed class AbstractMirandaMethodWrapper : MirandaMethodWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + internal AbstractMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod) : + base(declaringType, ifmethod, Modifiers.Public | Modifiers.Abstract) + { + + } + + } + + sealed class DefaultMirandaMethodWrapper : MirandaMethodWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + internal DefaultMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod) : + base(declaringType, ifmethod, Modifiers.Public) + { + + } + + } + + sealed class ErrorMirandaMethodWrapper : MirandaMethodWrapper + { + + string error; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + internal ErrorMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, string error) : + base(declaringType, ifmethod, Modifiers.Public) + { + this.error = error; + } + + protected override MirandaMethodWrapper AddConflictError(MethodWrapper mw) + { + error += " " + mw.DeclaringType.Name + "." + mw.Name; + return this; + } + + internal override string Error + { + get { return error; } + } + + } + + internal static MirandaMethodWrapper Create(TypeWrapper declaringType, MethodWrapper ifmethod) + { + DefaultMirandaMethodWrapper dmmw = ifmethod as DefaultMirandaMethodWrapper; + if (dmmw != null) + { + return new DefaultMirandaMethodWrapper(declaringType, dmmw.BaseMethod); + } + return ifmethod.IsAbstract + ? (MirandaMethodWrapper)new AbstractMirandaMethodWrapper(declaringType, ifmethod) + : (MirandaMethodWrapper)new DefaultMirandaMethodWrapper(declaringType, ifmethod); + } + + internal MirandaMethodWrapper Update(MethodWrapper mw) + { + if (ifmethod == mw) + { + // an interface can be implemented multiple times + return this; + } + else if (ifmethod.DeclaringType.ImplementsInterface(mw.DeclaringType)) + { + // we can override a base interface without problems + return this; + } + else if (mw.DeclaringType.ImplementsInterface(ifmethod.DeclaringType)) + { + return Create(DeclaringType, mw); + } + else if (!ifmethod.IsAbstract && !mw.IsAbstract) + { + return AddConflictError(mw); + } + else if (!ifmethod.IsAbstract && mw.IsAbstract) + { + return new ErrorMirandaMethodWrapper(DeclaringType, mw, DeclaringType.Name + "." + Name + Signature); + } + else + { + return this; + } + } + + protected virtual MirandaMethodWrapper AddConflictError(MethodWrapper mw) + { + return new ErrorMirandaMethodWrapper(DeclaringType, ifmethod, "Conflicting default methods:") + .AddConflictError(ifmethod) + .AddConflictError(mw); + } + + internal bool IsConflictError + { + get { return Error != null && Error.StartsWith("Conflicting default methods:"); } + } + + internal MethodWrapper BaseMethod + { + get { return ifmethod; } + } + + internal virtual string Error + { + get { return null; } + } + +#if EMITTERS + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, GetMethod()); + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Callvirt, GetMethod()); + } +#endif // EMITTERS + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldelema.cs b/src/IKVM.Runtime/NamePrefix.cs similarity index 63% rename from src/ikvmc/IKVM/Internal/MapXml/Ldelema.cs rename to src/IKVM.Runtime/NamePrefix.cs index ecceb2bf7b..2c9089c1b0 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldelema.cs +++ b/src/IKVM.Runtime/NamePrefix.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,21 +22,19 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -using IKVM.Reflection.Emit; - -namespace IKVM.Internal.MapXml +namespace IKVM.Internal { - [XmlType("ldelema")] - public sealed class Ldelema : Instruction + static class NamePrefix { - [XmlAttribute("sig")] - public string Sig; - internal override void Generate(CodeGenContext context, CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Ldelema, context.ClassLoader.FieldTypeWrapperFromSig(Sig, LoadMode.LoadOrThrow).TypeAsArrayType); - } + internal const string Type2AccessStubBackingField = "__<>"; + internal const string AccessStub = ""; + internal const string NonVirtual = ""; + internal const string Bridge = ""; + internal const string Incomplete = ""; + internal const string DefaultMethod = ""; + internal const string PrivateInterfaceInstanceMethod = ""; + } + } diff --git a/src/IKVM.Runtime/Native.cs b/src/IKVM.Runtime/Native.cs deleted file mode 100644 index 1826172024..0000000000 --- a/src/IKVM.Runtime/Native.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace IKVM.Runtime.JNI -{ - - /// - /// Provides access to the native companion methods. - /// - static unsafe class Native - { - - public const string LIB_NAME = "ikvm-native"; - - /// - /// Initializes the static instance. - /// - static Native() - { -#if NETFRAMEWORK - Load(); -#else - System.Runtime.InteropServices.NativeLibrary.SetDllImportResolver(typeof(Native).Assembly, DllImportResolver); -#endif - } - -#if NETFRAMEWORK - - /// - /// Preloads the native DLL for down-level platforms. - /// - static void Load() - { - // attempt to load with default loader - var h = NativeLibrary.Load(LIB_NAME); - if (h != IntPtr.Zero) - return; - - // scan known paths - foreach (var path in GetLibraryPaths(LIB_NAME)) - { - h = NativeLibrary.Load(path); - if (h != IntPtr.Zero) - return; - } - } - -#endif - -#if NETCOREAPP - - /// - /// Attempts to resolve the specified assembly when running on .NET Core 3.1 and above. - /// - /// - /// - /// - /// - static nint DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) - { - if (libraryName == LIB_NAME) - { - nint h; - - // attempt to load with default loader - if ((h = NativeLibrary.Load(libraryName)) != 0) - return h; - - // scan known paths - foreach (var path in GetLibraryPaths(libraryName)) - if ((h = NativeLibrary.Load(path)) != 0) - return h; - } - - return 0; - } - -#endif - - /// - /// Gets the RID architecture. - /// - /// - static string GetRuntimeIdentifierArch() => RuntimeInformation.ProcessArchitecture switch - { - Architecture.X86 => "x86", - Architecture.X64 => "x64", - Architecture.Arm => "arm", - Architecture.Arm64 => "arm64", - _ => null, - }; - - /// - /// Gets the runtime identifiers of the current platform. - /// - /// - static IEnumerable GetRuntimeIdentifiers() - { - var arch = GetRuntimeIdentifierArch(); - if (arch == null) - yield break; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var v = Environment.OSVersion.Version; - - // Windows 10 - if (v.Major > 10 || (v.Major == 10 && v.Minor >= 0)) - yield return $"win10-{arch}"; - - // Windows 8.1 - if (v.Major > 6 || (v.Major == 6 && v.Minor >= 3)) - yield return $"win81-{arch}"; - - // Windows 7 - if (v.Major > 6 || (v.Major == 6 && v.Minor >= 1)) - yield return $"win7-{arch}"; - - // fallback - yield return $"win-{arch}"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - yield return $"linux-{arch}"; - } - } - - /// - /// Gets the appropriate - /// - /// - /// - static string GetLibraryFileName(string name) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return $"{name}.dll"; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return $"lib{name}.so"; - - throw new NotSupportedException(); - } - - /// - /// Gets some library paths to check. - /// - /// - static IEnumerable GetLibraryPaths(string name) - { - var self = Directory.GetParent(typeof(Native).Assembly.Location)?.FullName; - if (self == null) - yield break; - - var file = GetLibraryFileName(name); - - // search in runtime specific directories - foreach (var rid in GetRuntimeIdentifiers()) - yield return Path.Combine(self, "runtimes", rid, "native", file); - } - - [DllImport(LIB_NAME, EntryPoint = "ikvm_GetJNIEnvVTable")] - public static extern void** ikvm_GetJNIEnvVTable(); - - } - -} diff --git a/src/IKVM.Runtime/NativeLibrary.cs b/src/IKVM.Runtime/NativeLibrary.cs index 8d7f5dfcdc..121c8039f2 100644 --- a/src/IKVM.Runtime/NativeLibrary.cs +++ b/src/IKVM.Runtime/NativeLibrary.cs @@ -109,7 +109,7 @@ public static nint Load(string path) else throw new PlatformNotSupportedException(); #else - return System.Runtime.InteropServices.NativeLibrary.TryLoad(path, out var h) ? h : 0; + return System.Runtime.InteropServices.NativeLibrary.TryLoad(path, typeof(NativeLibrary).Assembly, null, out var h) ? h : 0; #endif } diff --git a/src/IKVM.Runtime/NestedTypeName.cs b/src/IKVM.Runtime/NestedTypeName.cs new file mode 100644 index 0000000000..7adabfc099 --- /dev/null +++ b/src/IKVM.Runtime/NestedTypeName.cs @@ -0,0 +1,49 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +namespace IKVM.Internal +{ + static class NestedTypeName + { + + internal const string CallerID = "__"; + internal const string InterfaceHelperMethods = "__<>IHM"; + internal const string PrivateInterfaceMethods = "__<>PIM"; + + // interop types (mangled if necessary) + internal const string Fields = "__Fields"; + internal const string Methods = "__Methods"; + internal const string DefaultMethods = "__DefaultMethods"; + + // prefixes + internal const string ThreadLocal = "___"; + internal const string AtomicReferenceFieldUpdater = "___"; + internal const string IndyCallSite = "__<>IndyCS"; + internal const string MethodHandleConstant = "__<>MHC"; + internal const string MethodTypeConstant = "__<>MTC"; + internal const string IntrinsifiedAnonymousClass = "__<>Anon"; + + } + +} diff --git a/src/IKVM.Runtime/PassiveWeakDictionary.cs b/src/IKVM.Runtime/PassiveWeakDictionary.cs deleted file mode 100644 index da90e705eb..0000000000 --- a/src/IKVM.Runtime/PassiveWeakDictionary.cs +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (C) 2008 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace IKVM.Internal -{ - /* - * This is a special purpose dictionary. It only keeps weak references to the keys, - * thereby making them eligable for GC at any time. - * - * However, it does not make any promises about cleaning up the values (hence "passive"). - * This class should only be used if memory consumption and lifetime of the values is of no consequence. - * - * Furthermore, the type also assumes that it needs to continue to work during AppDomain unload, - * meaning that it uses GC.SuppressFinalize() on the WeakReference objects it uses to prevent - * them from being finalized during AppDomain unload. - * As a consequence, if you want to dispose on object of this type prior to AppDomain unload, you *must* - * call Dispose() or you will leak GC handles. After calling Dispose() you should not call any other - * methods. - * - * This class is thread safe and TryGetValue is lock free. - * - */ - sealed class PassiveWeakDictionary - where TKey : class - where TValue : class - { - private volatile Bucket[] table = new Bucket[37]; - private int count; - private FreeNode freeList; - private int freeCount; - - internal void Dispose() - { - Bucket[] table = this.table; - for (int i = 0; i < table.Length; i++) - { - for (Bucket b = table[i]; b != null; b = b.Next) - { - GC.ReRegisterForFinalize(b.WeakRef); - } - } - for (FreeNode b = freeList; b != null; b = b.Next) - { - GC.ReRegisterForFinalize(b.WeakRef); - } - } - - internal void Add(TKey key, TValue value) - { - if (key == null) - throw new ArgumentNullException("key"); - - lock (this) - { - if (count > table.Length) - { - Rehash(); - } - int index = GetHashCode(key) % table.Length; - for (Bucket b = table[index]; b != null; b = b.Next) - { - if (b.Key == key) - { - throw new ArgumentException("Key already exists"); - } - } - table[index] = new Bucket(AllocWeakRef(key), value, table[index]); - count++; - } - } - - private WeakReference AllocWeakRef(TKey key) - { - WeakReference weakRef; - if (freeList != null) - { - weakRef = freeList.WeakRef; - freeList = freeList.Next; - freeCount--; - weakRef.Target = key; - } - else - { - weakRef = new WeakReference(key, true); - GC.SuppressFinalize(weakRef); - } - return weakRef; - } - - private static readonly int[] primes = { - 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 524309, 1048583, 2097169, - 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459, 536870923, 1073741827, 2147483647 }; - - private static int GetPrime(int count) - { - foreach (int p in primes) - { - if (p > count) - return p; - } - return count; - } - - private static int GetHashCode(TKey key) - { - return RuntimeHelpers.GetHashCode(key) & 0x7FFFFFFF; - } - - private void Rehash() - { - count = 0; - for (int i = 0; i < table.Length; i++) - { - for (Bucket b = table[i]; b != null; b = b.Next) - { - if (b.Key != null) - { - count++; - } - } - } - Bucket[] newTable = new Bucket[GetPrime(count * 2)]; - for (int i = 0; i < table.Length; i++) - { - for (Bucket b = table[i]; b != null; b = b.Next) - { - TKey key = b.Key; - if (key != null) - { - int index = GetHashCode(key) % newTable.Length; - WeakReference weakRef = AllocWeakRef(key); - newTable[index] = new Bucket(weakRef, b.Value, newTable[index]); - } - else - { - // don't cache an infinite amount, - // busy loop garbage allocation benchmarking shows that 8K is enough with current gen0 size - if (freeCount < 8192) - { - WeakReference weakRef = b.WeakRef; - b.Clear(); - freeList = new FreeNode(weakRef, freeList); - freeCount++; - } - else - { - GC.ReRegisterForFinalize(b.WeakRef); - b.Clear(); - } - } - } - } - this.table = newTable; - } - - internal bool TryGetValue(TKey key, out TValue value) - { - if (key == null) - throw new ArgumentNullException("key"); - - Bucket[] table = this.table; - int index = GetHashCode(key) % table.Length; - for (Bucket b = table[index]; b != null; b = b.Next) - { - if (b.Key == key) - { - value = b.Value; - return true; - } - } - value = null; - return false; - } - - private sealed class Bucket - { - private volatile WeakReference weakRef; - internal TValue Value; - internal readonly Bucket Next; - - internal Bucket(WeakReference weakRef, TValue value, Bucket next) - { - this.weakRef = weakRef; - this.Value = value; - this.Next = next; - } - - internal WeakReference WeakRef - { - get - { - return weakRef; - } - } - - internal TKey Key - { - get - { - WeakReference weakRef = this.weakRef; - if (weakRef != null) - { - TKey key = (TKey)weakRef.Target; - // this extra null check is to guard against the case where there was a GC and a Rehash and the weak ref was reused for another slot - // between the initial read of weakRef and accessing its Target property - if (this.weakRef != null) - { - return key; - } - } - return null; - } - } - - internal void Clear() - { - weakRef = null; - } - } - - private sealed class FreeNode - { - internal readonly WeakReference WeakRef; - internal readonly FreeNode Next; - - internal FreeNode(WeakReference weakRef, FreeNode next) - { - this.WeakRef = weakRef; - this.Next = next; - } - } - } -} diff --git a/src/IKVM.Runtime/PrimitiveTypeWrapper.cs b/src/IKVM.Runtime/PrimitiveTypeWrapper.cs new file mode 100644 index 0000000000..bd6d579d59 --- /dev/null +++ b/src/IKVM.Runtime/PrimitiveTypeWrapper.cs @@ -0,0 +1,147 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +using IKVM.Attributes; +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + sealed class PrimitiveTypeWrapper : TypeWrapper + { + + internal static readonly PrimitiveTypeWrapper BYTE = new PrimitiveTypeWrapper(Types.Byte, "B"); + internal static readonly PrimitiveTypeWrapper CHAR = new PrimitiveTypeWrapper(Types.Char, "C"); + internal static readonly PrimitiveTypeWrapper DOUBLE = new PrimitiveTypeWrapper(Types.Double, "D"); + internal static readonly PrimitiveTypeWrapper FLOAT = new PrimitiveTypeWrapper(Types.Single, "F"); + internal static readonly PrimitiveTypeWrapper INT = new PrimitiveTypeWrapper(Types.Int32, "I"); + internal static readonly PrimitiveTypeWrapper LONG = new PrimitiveTypeWrapper(Types.Int64, "J"); + internal static readonly PrimitiveTypeWrapper SHORT = new PrimitiveTypeWrapper(Types.Int16, "S"); + internal static readonly PrimitiveTypeWrapper BOOLEAN = new PrimitiveTypeWrapper(Types.Boolean, "Z"); + internal static readonly PrimitiveTypeWrapper VOID = new PrimitiveTypeWrapper(Types.Void, "V"); + + readonly Type type; + readonly string sigName; + + /// + /// Initializes a new instance. + /// + /// + /// + private PrimitiveTypeWrapper(Type type, string sigName) : + base(TypeFlags.None, Modifiers.Public | Modifiers.Abstract | Modifiers.Final, null) + { + this.type = type; + this.sigName = sigName; + } + + internal override TypeWrapper BaseTypeWrapper => null; + + internal static bool IsPrimitiveType(Type type) + { + return type == BYTE.type + || type == CHAR.type + || type == DOUBLE.type + || type == FLOAT.type + || type == INT.type + || type == LONG.type + || type == SHORT.type + || type == BOOLEAN.type + || type == VOID.type; + } + + internal override string SigName => sigName; + + internal override ClassLoaderWrapper GetClassLoader() => ClassLoaderWrapper.GetBootstrapClassLoader(); + + internal override Type TypeAsTBD => type; + + public override string ToString() => "PrimitiveTypeWrapper[" + sigName + "]"; + +#if EMITTERS + + internal override void EmitLdind(CodeEmitter il) + { + if (this == BOOLEAN) + il.Emit(OpCodes.Ldind_U1); + else if (this == BYTE) + il.Emit(OpCodes.Ldind_U1); + else if (this == CHAR) + il.Emit(OpCodes.Ldind_U2); + else if (this == SHORT) + il.Emit(OpCodes.Ldind_I2); + else if (this == INT) + il.Emit(OpCodes.Ldind_I4); + else if (this == LONG) + il.Emit(OpCodes.Ldind_I8); + else if (this == FLOAT) + il.Emit(OpCodes.Ldind_R4); + else if (this == DOUBLE) + il.Emit(OpCodes.Ldind_R8); + else + throw new InternalException(); + } + + internal override void EmitStind(CodeEmitter il) + { + if (this == BOOLEAN) + il.Emit(OpCodes.Stind_I1); + else if (this == BYTE) + il.Emit(OpCodes.Stind_I1); + else if (this == CHAR) + il.Emit(OpCodes.Stind_I2); + else if (this == SHORT) + il.Emit(OpCodes.Stind_I2); + else if (this == INT) + il.Emit(OpCodes.Stind_I4); + else if (this == LONG) + il.Emit(OpCodes.Stind_I8); + else if (this == FLOAT) + il.Emit(OpCodes.Stind_R4); + else if (this == DOUBLE) + il.Emit(OpCodes.Stind_R8); + else + throw new InternalException(); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/ReflectUtil.cs b/src/IKVM.Runtime/ReflectUtil.cs index 3bb8869f6d..40576ea1fa 100644 --- a/src/IKVM.Runtime/ReflectUtil.cs +++ b/src/IKVM.Runtime/ReflectUtil.cs @@ -23,7 +23,7 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -55,7 +55,7 @@ internal static Assembly GetAssembly(Type type) internal static bool IsDynamicAssembly(Assembly asm) { -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER return false; #else return asm.IsDynamic; @@ -99,7 +99,7 @@ internal static bool ContainsTypeBuilder(Type type) internal static bool IsVector(Type type) { -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER return type.__IsVector; #else // there's no API to distinguish an array of rank 1 from a vector, @@ -172,13 +172,14 @@ internal static MethodBuilder DefineConstructor(TypeBuilder tb, MethodAttributes return tb.DefineMethod(ConstructorInfo.ConstructorName, attribs | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, parameterTypes); } + /// + /// Returns true if the type can be the owner of a dynamic method. + /// + /// + /// internal static bool CanOwnDynamicMethod(Type type) { - return type != null - && !type.IsInterface - && !type.HasElementType - && !type.IsGenericTypeDefinition - && !type.IsGenericParameter; + return type != null && !type.IsInterface && !type.HasElementType && !type.IsGenericTypeDefinition && !type.IsGenericParameter; } internal static bool MatchParameterInfos(ParameterInfo p1, ParameterInfo p2) @@ -214,7 +215,7 @@ private static bool MatchTypes(Type[] t1, Type[] t2) return false; } -#if STATIC_COMPILER +#if IMPORTER internal static Type GetMissingType(Type type) { diff --git a/src/IKVM.Runtime/RuntimeHelperTypes.cs b/src/IKVM.Runtime/RuntimeHelperTypes.cs index 0e17164697..c9cc00d97b 100644 --- a/src/IKVM.Runtime/RuntimeHelperTypes.cs +++ b/src/IKVM.Runtime/RuntimeHelperTypes.cs @@ -22,20 +22,29 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER + +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; + #else using System.Reflection; using System.Reflection.Emit; #endif + using System.Diagnostics; namespace IKVM.Internal { + static class RuntimeHelperTypes { + private static Type classLiteralType; private static FieldInfo classLiteralField; @@ -44,13 +53,13 @@ internal static FieldInfo GetClassLiteralField(Type type) Debug.Assert(type != Types.Void); if (classLiteralType == null) { -#if STATIC_COMPILER - classLiteralType = JVM.CoreAssembly.GetType("ikvm.internal.ClassLiteral`1"); +#if IMPORTER + classLiteralType = JVM.BaseAssembly.GetType("ikvm.internal.ClassLiteral`1"); #elif !FIRST_PASS classLiteralType = typeof(ikvm.@internal.ClassLiteral<>); #endif } -#if !STATIC_COMPILER +#if !IMPORTER if (!IsTypeBuilder(type)) { return classLiteralType.MakeGenericType(type).GetField("Value", BindingFlags.Public | BindingFlags.Static); @@ -68,7 +77,7 @@ private static bool IsTypeBuilder(Type type) return type is TypeBuilder || (type.HasElementType && IsTypeBuilder(type.GetElementType())); } -#if STATIC_COMPILER +#if IMPORTER internal static void Create(CompilerClassLoader ccl) { EmitClassLiteral(ccl); diff --git a/src/IKVM.Runtime/RuntimeUtil.cs b/src/IKVM.Runtime/RuntimeUtil.cs new file mode 100644 index 0000000000..9e024dce8b --- /dev/null +++ b/src/IKVM.Runtime/RuntimeUtil.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +namespace IKVM.Runtime +{ + + /// + /// Maintains various information about the current runtime environment. + /// + public static class RuntimeUtil + { + + /// + /// Returns true if the current runtime is Mono. + /// + public static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; + + /// + /// Returns true if the current platform is Windows. + /// + public static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + /// + /// Returns true if the current platform is Linux. + /// + public static bool IsLinux { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + /// + /// Returns true if the current platform is Mac OS X. + /// + public static bool IsOSX { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + } + +} diff --git a/src/IKVM.Runtime/Serialization.cs b/src/IKVM.Runtime/Serialization.cs index c90f830421..c5f70fa896 100644 --- a/src/IKVM.Runtime/Serialization.cs +++ b/src/IKVM.Runtime/Serialization.cs @@ -24,11 +24,14 @@ Jeroen Frijters using System; using System.Runtime.Serialization; using System.Security; -using System.Security.Permissions; + using IKVM.Runtime; -#if STATIC_COMPILER + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; #else using System.Reflection; @@ -37,235 +40,210 @@ Jeroen Frijters namespace IKVM.Internal { - // This class deals with .NET serialization. When a class is Java serializable it will attempt to automagically make it .NET serializable. - static class Serialization - { - private static readonly CustomAttributeBuilder serializableAttribute = new CustomAttributeBuilder(JVM.Import(typeof(SerializableAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); - private static readonly CustomAttributeBuilder securityCriticalAttribute = new CustomAttributeBuilder(JVM.Import(typeof(SecurityCriticalAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); - private static readonly TypeWrapper iserializable = ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(ISerializable))); - private static readonly TypeWrapper iobjectreference = ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(IObjectReference))); - private static readonly TypeWrapper externalizable = ClassLoaderWrapper.LoadClassCritical("java.io.Externalizable"); - private static readonly PermissionSet psetSerializationFormatter; - static Serialization() - { - psetSerializationFormatter = new PermissionSet(PermissionState.None); - psetSerializationFormatter.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)); - } + static class Serialization + { - internal static bool IsISerializable(TypeWrapper wrapper) - { - return wrapper == iserializable; - } + static readonly CustomAttributeBuilder serializableAttribute = new CustomAttributeBuilder(JVM.Import(typeof(SerializableAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + static readonly CustomAttributeBuilder securityCriticalAttribute = new CustomAttributeBuilder(JVM.Import(typeof(SecurityCriticalAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + static readonly TypeWrapper iserializable = ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(ISerializable))); + static readonly TypeWrapper iobjectreference = ClassLoaderWrapper.GetWrapperFromType(JVM.Import(typeof(IObjectReference))); + static readonly TypeWrapper externalizable = ClassLoaderWrapper.LoadClassCritical("java.io.Externalizable"); - private static bool IsSafeForAutomagicSerialization(TypeWrapper wrapper) - { - if (wrapper.TypeAsBaseType.IsSerializable) - { - return false; - } - if (wrapper.IsSubTypeOf(iserializable)) - { - return false; - } - if (wrapper.IsSubTypeOf(iobjectreference)) - { - return false; - } - if (wrapper.GetMethodWrapper("GetObjectData", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null) - { - return false; - } - if (wrapper.GetMethodWrapper("", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null) - { - return false; - } - return true; - } + internal static bool IsISerializable(TypeWrapper wrapper) + { + return wrapper == iserializable; + } - internal static MethodBuilder AddAutomagicSerialization(DynamicTypeWrapper wrapper, TypeBuilder typeBuilder) - { - MethodBuilder serializationCtor = null; - if ((wrapper.Modifiers & IKVM.Attributes.Modifiers.Enum) != 0) - { - MarkSerializable(typeBuilder); - } - else if (wrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper) && IsSafeForAutomagicSerialization(wrapper)) - { - if (wrapper.IsSubTypeOf(externalizable)) - { - MethodWrapper ctor = wrapper.GetMethodWrapper("", "()V", false); - if (ctor != null && ctor.IsPublic) - { - MarkSerializable(typeBuilder); - ctor.Link(); - serializationCtor = AddConstructor(typeBuilder, ctor, null, true); - if (!wrapper.BaseTypeWrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper)) - { - AddGetObjectData(typeBuilder); - } - if (wrapper.BaseTypeWrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", true) != null) - { - RemoveReadResolve(typeBuilder); - } - } - } - else if (wrapper.BaseTypeWrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper)) - { - MethodBase baseCtor = wrapper.GetBaseSerializationConstructor(); - if (baseCtor != null && (baseCtor.IsFamily || baseCtor.IsFamilyOrAssembly)) - { - MarkSerializable(typeBuilder); - serializationCtor = AddConstructor(typeBuilder, null, baseCtor, false); - AddReadResolve(wrapper, typeBuilder); - } - } - else - { - MethodWrapper baseCtor = wrapper.BaseTypeWrapper.GetMethodWrapper("", "()V", false); - if (baseCtor != null && baseCtor.IsAccessibleFrom(wrapper.BaseTypeWrapper, wrapper, wrapper)) - { - MarkSerializable(typeBuilder); - AddGetObjectData(typeBuilder); -#if STATIC_COMPILER + static bool IsSafeForAutomagicSerialization(TypeWrapper wrapper) + { + if (wrapper.TypeAsBaseType.IsSerializable) + return false; + else if (wrapper.IsSubTypeOf(iserializable)) + return false; + else if (wrapper.IsSubTypeOf(iobjectreference)) + return false; + else if (wrapper.GetMethodWrapper("GetObjectData", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null) + return false; + else if (wrapper.GetMethodWrapper("", "(Lcli.System.Runtime.Serialization.SerializationInfo;Lcli.System.Runtime.Serialization.StreamingContext;)V", false) != null) + return false; + else + return true; + } + + internal static MethodBuilder AddAutomagicSerialization(DynamicTypeWrapper wrapper, TypeBuilder typeBuilder) + { + MethodBuilder serializationCtor = null; + if ((wrapper.Modifiers & IKVM.Attributes.Modifiers.Enum) != 0) + { + MarkSerializable(typeBuilder); + } + else if (wrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper) && IsSafeForAutomagicSerialization(wrapper)) + { + if (wrapper.IsSubTypeOf(externalizable)) + { + var ctor = wrapper.GetMethodWrapper("", "()V", false); + if (ctor != null && ctor.IsPublic) + { + MarkSerializable(typeBuilder); + ctor.Link(); + + serializationCtor = AddConstructor(typeBuilder, ctor, null, true); + if (!wrapper.BaseTypeWrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper)) + { + AddGetObjectData(typeBuilder); + } + if (wrapper.BaseTypeWrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", true) != null) + { + RemoveReadResolve(typeBuilder); + } + } + } + else if (wrapper.BaseTypeWrapper.IsSubTypeOf(CoreClasses.java.io.Serializable.Wrapper)) + { + var baseCtor = wrapper.GetBaseSerializationConstructor(); + if (baseCtor != null && (baseCtor.IsFamily || baseCtor.IsFamilyOrAssembly)) + { + MarkSerializable(typeBuilder); + serializationCtor = AddConstructor(typeBuilder, null, baseCtor, false); + AddReadResolve(wrapper, typeBuilder); + } + } + else + { + var baseCtor = wrapper.BaseTypeWrapper.GetMethodWrapper("", "()V", false); + if (baseCtor != null && baseCtor.IsAccessibleFrom(wrapper.BaseTypeWrapper, wrapper, wrapper)) + { + MarkSerializable(typeBuilder); + AddGetObjectData(typeBuilder); +#if IMPORTER // because the base type can be a __WorkaroundBaseClass__, we may need to replace the constructor baseCtor = ((AotTypeWrapper)wrapper).ReplaceMethodWrapper(baseCtor); #endif - baseCtor.Link(); - serializationCtor = AddConstructor(typeBuilder, baseCtor, null, true); - AddReadResolve(wrapper, typeBuilder); - } - } - } - return serializationCtor; - } + baseCtor.Link(); + serializationCtor = AddConstructor(typeBuilder, baseCtor, null, true); + AddReadResolve(wrapper, typeBuilder); + } + } + } - internal static MethodBuilder AddAutomagicSerializationToWorkaroundBaseClass(TypeBuilder typeBuilderWorkaroundBaseClass, MethodBase baseCtor) - { - if (typeBuilderWorkaroundBaseClass.BaseType.IsSerializable) - { - typeBuilderWorkaroundBaseClass.SetCustomAttribute(serializableAttribute); - if (baseCtor != null && (baseCtor.IsFamily || baseCtor.IsFamilyOrAssembly)) - { - return AddConstructor(typeBuilderWorkaroundBaseClass, null, baseCtor, false); - } - } - return null; - } + return serializationCtor; + } - internal static void MarkSerializable(TypeBuilder tb) - { - tb.SetCustomAttribute(serializableAttribute); - } + internal static MethodBuilder AddAutomagicSerializationToWorkaroundBaseClass(TypeBuilder typeBuilderWorkaroundBaseClass, MethodBase baseCtor) + { + if (typeBuilderWorkaroundBaseClass.BaseType.IsSerializable) + { + typeBuilderWorkaroundBaseClass.SetCustomAttribute(serializableAttribute); + if (baseCtor != null && (baseCtor.IsFamily || baseCtor.IsFamilyOrAssembly)) + return AddConstructor(typeBuilderWorkaroundBaseClass, null, baseCtor, false); + } - internal static void AddGetObjectData(TypeBuilder tb) - { - string name = tb.IsSealed - ? "System.Runtime.Serialization.ISerializable.GetObjectData" - : "GetObjectData"; - MethodAttributes attr = tb.IsSealed - ? MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final - : MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride; - tb.AddInterfaceImplementation(JVM.Import(typeof(ISerializable))); - MethodBuilder getObjectData = tb.DefineMethod(name, attr, null, - new Type[] { JVM.Import(typeof(SerializationInfo)), JVM.Import(typeof(StreamingContext)) }); - getObjectData.SetCustomAttribute(securityCriticalAttribute); - AttributeHelper.HideFromJava(getObjectData); - // AddDeclarativeSecurity does not exist in .net core -#if NETFRAMEWORK - getObjectData.AddDeclarativeSecurity(SecurityAction.Demand, psetSerializationFormatter); -#endif - tb.DefineMethodOverride(getObjectData, JVM.Import(typeof(ISerializable)).GetMethod("GetObjectData")); - CodeEmitter ilgen = CodeEmitter.Create(getObjectData); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldarg_1); - TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization"); - MethodWrapper mw = serializationHelper.GetMethodWrapper("writeObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false); - mw.Link(); - mw.EmitCall(ilgen); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - } + return null; + } - private static MethodBuilder AddConstructor(TypeBuilder tb, MethodWrapper defaultConstructor, MethodBase serializationConstructor, bool callReadObject) - { - MethodBuilder ctor = ReflectUtil.DefineConstructor(tb, MethodAttributes.Family, new Type[] { JVM.Import(typeof(SerializationInfo)), JVM.Import(typeof(StreamingContext)) }); - AttributeHelper.HideFromJava(ctor); - // AddDeclarativeSecurity does not exist in .net core -#if NETFRAMEWORK - ctor.AddDeclarativeSecurity(SecurityAction.Demand, psetSerializationFormatter); -#endif - CodeEmitter ilgen = CodeEmitter.Create(ctor); - ilgen.Emit(OpCodes.Ldarg_0); - if (defaultConstructor != null) - { - defaultConstructor.EmitCall(ilgen); - } - else - { - ilgen.Emit(OpCodes.Ldarg_1); - ilgen.Emit(OpCodes.Ldarg_2); - ilgen.Emit(OpCodes.Call, serializationConstructor); - } - if (callReadObject) - { - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ldarg_1); - TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization"); - MethodWrapper mw = serializationHelper.GetMethodWrapper("readObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false); - mw.Link(); - mw.EmitCall(ilgen); - } - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return ctor; - } + internal static void MarkSerializable(TypeBuilder tb) + { + tb.SetCustomAttribute(serializableAttribute); + } + + internal static void AddGetObjectData(TypeBuilder tb) + { + var name = tb.IsSealed ? "System.Runtime.Serialization.ISerializable.GetObjectData" : "GetObjectData"; + var attr = tb.IsSealed + ? MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final + : MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride; + tb.AddInterfaceImplementation(JVM.Import(typeof(ISerializable))); + var getObjectData = tb.DefineMethod(name, attr, null, new Type[] { JVM.Import(typeof(SerializationInfo)), JVM.Import(typeof(StreamingContext)) }); + getObjectData.SetCustomAttribute(securityCriticalAttribute); + AttributeHelper.HideFromJava(getObjectData); + tb.DefineMethodOverride(getObjectData, JVM.Import(typeof(ISerializable)).GetMethod("GetObjectData")); + CodeEmitter ilgen = CodeEmitter.Create(getObjectData); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldarg_1); + TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization"); + MethodWrapper mw = serializationHelper.GetMethodWrapper("writeObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false); + mw.Link(); + mw.EmitCall(ilgen); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + } + + static MethodBuilder AddConstructor(TypeBuilder tb, MethodWrapper defaultConstructor, MethodBase serializationConstructor, bool callReadObject) + { + MethodBuilder ctor = ReflectUtil.DefineConstructor(tb, MethodAttributes.Family, new Type[] { JVM.Import(typeof(SerializationInfo)), JVM.Import(typeof(StreamingContext)) }); + AttributeHelper.HideFromJava(ctor); + CodeEmitter ilgen = CodeEmitter.Create(ctor); + ilgen.Emit(OpCodes.Ldarg_0); + if (defaultConstructor != null) + { + defaultConstructor.EmitCall(ilgen); + } + else + { + ilgen.Emit(OpCodes.Ldarg_1); + ilgen.Emit(OpCodes.Ldarg_2); + ilgen.Emit(OpCodes.Call, serializationConstructor); + } + if (callReadObject) + { + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldarg_1); + TypeWrapper serializationHelper = ClassLoaderWrapper.LoadClassCritical("ikvm.internal.Serialization"); + MethodWrapper mw = serializationHelper.GetMethodWrapper("readObject", "(Ljava.lang.Object;Lcli.System.Runtime.Serialization.SerializationInfo;)V", false); + mw.Link(); + mw.EmitCall(ilgen); + } + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return ctor; + } + + static void AddReadResolve(DynamicTypeWrapper wrapper, TypeBuilder tb) + { + var mw = wrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", false); + if (mw != null && !wrapper.IsSubTypeOf(iobjectreference)) + { + tb.AddInterfaceImplementation(JVM.Import(typeof(IObjectReference))); + var getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, Types.Object, new Type[] { JVM.Import(typeof(StreamingContext)) }); + getRealObject.SetCustomAttribute(securityCriticalAttribute); + AttributeHelper.HideFromJava(getRealObject); + tb.DefineMethodOverride(getRealObject, JVM.Import(typeof(IObjectReference)).GetMethod("GetRealObject")); + var ilgen = CodeEmitter.Create(getRealObject); + mw.Link(); + if (!wrapper.IsFinal) + { + // readResolve is only applicable if it exists on the actual type of the object, so if we're a subclass don't call it + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); + ilgen.Emit(OpCodes.Ldtoken, wrapper.TypeAsBaseType); + ilgen.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitBeq(label); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ret); + ilgen.MarkLabel(label); + } + ilgen.Emit(OpCodes.Ldarg_0); + mw.EmitCall(ilgen); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + } + } + + static void RemoveReadResolve(TypeBuilder tb) + { + tb.AddInterfaceImplementation(JVM.Import(typeof(IObjectReference))); + MethodBuilder getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, Types.Object, new Type[] { JVM.Import(typeof(StreamingContext)) }); + getRealObject.SetCustomAttribute(securityCriticalAttribute); + AttributeHelper.HideFromJava(getRealObject); + tb.DefineMethodOverride(getRealObject, JVM.Import(typeof(IObjectReference)).GetMethod("GetRealObject")); + CodeEmitter ilgen = CodeEmitter.Create(getRealObject); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + } - private static void AddReadResolve(DynamicTypeWrapper wrapper, TypeBuilder tb) - { - MethodWrapper mw = wrapper.GetMethodWrapper("readResolve", "()Ljava.lang.Object;", false); - if (mw != null && !wrapper.IsSubTypeOf(iobjectreference)) - { - tb.AddInterfaceImplementation(JVM.Import(typeof(IObjectReference))); - MethodBuilder getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, - Types.Object, new Type[] { JVM.Import(typeof(StreamingContext)) }); - getRealObject.SetCustomAttribute(securityCriticalAttribute); - AttributeHelper.HideFromJava(getRealObject); - tb.DefineMethodOverride(getRealObject, JVM.Import(typeof(IObjectReference)).GetMethod("GetRealObject")); - CodeEmitter ilgen = CodeEmitter.Create(getRealObject); - mw.Link(); - if (!wrapper.IsFinal) - { - // readResolve is only applicable if it exists on the actual type of the object, so if we're a subclass don't call it - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); - ilgen.Emit(OpCodes.Ldtoken, wrapper.TypeAsBaseType); - ilgen.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitBeq(label); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ret); - ilgen.MarkLabel(label); - } - ilgen.Emit(OpCodes.Ldarg_0); - mw.EmitCall(ilgen); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - } - } + } - private static void RemoveReadResolve(TypeBuilder tb) - { - tb.AddInterfaceImplementation(JVM.Import(typeof(IObjectReference))); - MethodBuilder getRealObject = tb.DefineMethod("IObjectReference.GetRealObject", MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, - Types.Object, new Type[] { JVM.Import(typeof(StreamingContext)) }); - getRealObject.SetCustomAttribute(securityCriticalAttribute); - AttributeHelper.HideFromJava(getRealObject); - tb.DefineMethodOverride(getRealObject, JVM.Import(typeof(IObjectReference)).GetMethod("GetRealObject")); - CodeEmitter ilgen = CodeEmitter.Create(getRealObject); - ilgen.Emit(OpCodes.Ldarg_0); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - } - } } diff --git a/src/IKVM.Runtime/SimpleCallMethodWrapper.cs b/src/IKVM.Runtime/SimpleCallMethodWrapper.cs new file mode 100644 index 0000000000..eadebf62ca --- /dev/null +++ b/src/IKVM.Runtime/SimpleCallMethodWrapper.cs @@ -0,0 +1,83 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + sealed class SimpleCallMethodWrapper : MethodWrapper + { + + readonly SimpleOpCode call; + readonly SimpleOpCode callvirt; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal SimpleCallMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags, SimpleOpCode call, SimpleOpCode callvirt) : + base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) + { + this.call = call; + this.callvirt = callvirt; + } + +#if EMITTERS + + internal override void EmitCall(CodeEmitter ilgen) + { + ilgen.Emit(SimpleOpCodeToOpCode(call), GetMethod()); + } + + internal override void EmitCallvirt(CodeEmitter ilgen) + { + ilgen.Emit(SimpleOpCodeToOpCode(callvirt), GetMethod()); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/SimpleFieldWrapper.cs b/src/IKVM.Runtime/SimpleFieldWrapper.cs new file mode 100644 index 0000000000..30b1ab6b65 --- /dev/null +++ b/src/IKVM.Runtime/SimpleFieldWrapper.cs @@ -0,0 +1,320 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System.Diagnostics; + +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; + +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + /// + /// Field wrapper implementation for standard fields. + /// + sealed class SimpleFieldWrapper : FieldWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + internal SimpleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) : + base(declaringType, fieldType, name, sig, modifiers, fi) + { + Debug.Assert(!(fieldType == PrimitiveTypeWrapper.DOUBLE || fieldType == PrimitiveTypeWrapper.LONG) || !IsVolatile); + } + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + internal override object GetValue(object obj) + { + return GetField().GetValue(obj); + } + + internal override void SetValue(object obj, object value) + { + GetField().SetValue(obj, value); + } + +#endif + +#if EMITTERS + + protected override void EmitGetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic == false && DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + + // conduct load operation with volatile tag if field is volatile + if (IsVolatile) + { + il.EmitMemoryBarrier(); + il.Emit(OpCodes.Volatile); + } + + if (IsStatic) + il.Emit(OpCodes.Ldsfld, fi); + else + il.Emit(OpCodes.Ldfld, fi); + } + + protected override void EmitSetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic == false && DeclaringType.IsNonPrimitiveValueType) + { + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsLocalOrStackType); + il.Emit(OpCodes.Stloc, value); + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + il.Emit(OpCodes.Ldloc, value); + } + + // conduct store operation with volatile tag if field is volatile + if (IsVolatile) + il.Emit(OpCodes.Volatile); + + if (IsStatic) + il.Emit(OpCodes.Stsfld, fi); + else + il.Emit(OpCodes.Stfld, fi); + + if (IsVolatile) + il.EmitMemoryBarrier(); + } + + protected override void EmitUnsafeGetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic) + { + if (IsFinal) + { + // perform an indirect load to prevent the JIT from caching the value + il.Emit(OpCodes.Ldsflda, fi); + FieldTypeWrapper.EmitLdind(il); + } + else + { + il.Emit(OpCodes.Ldsfld, fi); + } + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Ldfld, fi); + } + } + + protected override void EmitUnsafeSetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic == false && DeclaringType.IsNonPrimitiveValueType) + { + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsLocalOrStackType); + il.Emit(OpCodes.Stloc, value); + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + il.Emit(OpCodes.Ldloc, value); + } + + if (IsStatic) + { + il.Emit(OpCodes.Stsfld, fi); + } + else + { + il.Emit(OpCodes.Stfld, fi); + } + } + + protected override void EmitUnsafeVolatileGetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Ldflda, fi); + } + + + if (FieldTypeWrapper == PrimitiveTypeWrapper.BOOLEAN) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadBoolean); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.BYTE) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadByte); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.CHAR) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadChar); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.SHORT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadShort); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.INT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadInt); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadLong); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.FLOAT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadFloat); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadDouble); + else + { + il.EmitMemoryBarrier(); + il.Emit(OpCodes.Volatile); + FieldTypeWrapper.EmitLdind(il); + } + } + + protected override void EmitUnsafeVolatileSetImpl(CodeEmitter il) + { + var fi = GetField(); + + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsLocalOrStackType); + il.Emit(OpCodes.Stloc, value); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Ldflda, fi); + } + + il.Emit(OpCodes.Ldloc, value); + + if (FieldTypeWrapper == PrimitiveTypeWrapper.BOOLEAN) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteBoolean); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.BYTE) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteByte); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.CHAR) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteChar); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.SHORT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteShort); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.INT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteInt); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteLong); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.FLOAT) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteFloat); + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteDouble); + else + { + il.Emit(OpCodes.Volatile); + FieldTypeWrapper.EmitStind(il); + il.EmitMemoryBarrier(); + } + } + + protected override void EmitUnsafeCompareAndSwapImpl(CodeEmitter il) + { + var fi = GetField(); + + var update = il.AllocTempLocal(FieldTypeWrapper.TypeAsLocalOrStackType); + var expect = il.AllocTempLocal(FieldTypeWrapper.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Stloc, update); + il.Emit(OpCodes.Stloc, expect); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsLocalOrStackType); + + il.Emit(OpCodes.Ldflda, fi); + } + + il.Emit(OpCodes.Ldloc, expect); + il.Emit(OpCodes.Ldloc, update); + + if (FieldTypeWrapper.IsPrimitive) + { + if (FieldTypeWrapper == PrimitiveTypeWrapper.INT) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapInt); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapDouble); + } + else + { + throw new InternalException(); + } + } + else + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapObject); + } + + il.ReleaseTempLocal(expect); + il.ReleaseTempLocal(update); + } + +#endif + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArg_1.cs b/src/IKVM.Runtime/SimpleOpCode.cs similarity index 77% rename from src/ikvmc/IKVM/Internal/MapXml/LdArg_1.cs rename to src/IKVM.Runtime/SimpleOpCode.cs index 17e56ed738..fd4eba0789 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArg_1.cs +++ b/src/IKVM.Runtime/SimpleOpCode.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2014 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,17 +22,25 @@ Jeroen Frijters */ -using System.Xml.Serialization; - +#if IMPORTER || EXPORTER +using IKVM.Reflection; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal { - [XmlType("ldarg_1")] - public sealed class LdArg_1 : Simple + enum SimpleOpCode : byte { - public LdArg_1() : base(OpCodes.Ldarg_1) - { - } + Call, + Callvirt, + Newobj } + } diff --git a/src/IKVM.Runtime/SmartMethodWrapper.cs b/src/IKVM.Runtime/SmartMethodWrapper.cs new file mode 100644 index 0000000000..74d34cbe33 --- /dev/null +++ b/src/IKVM.Runtime/SmartMethodWrapper.cs @@ -0,0 +1,115 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + abstract class SmartMethodWrapper : MethodWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal SmartMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) + { + + } + +#if EMITTERS + + internal sealed override void EmitCall(CodeEmitter ilgen) + { + AssertLinked(); + CallImpl(ilgen); + } + + protected virtual void CallImpl(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + + internal sealed override void EmitCallvirt(CodeEmitter ilgen) + { + AssertLinked(); + if (DeclaringType.IsNonPrimitiveValueType) + { + // callvirt isn't allowed on a value type + // (we don't need to check for a null reference, because we're always dealing with an unboxed value) + CallImpl(ilgen); + } + else + { + CallvirtImpl(ilgen); + } + } + + protected virtual void CallvirtImpl(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + + internal sealed override void EmitNewobj(CodeEmitter ilgen) + { + AssertLinked(); + NewobjImpl(ilgen); + if (DeclaringType.IsNonPrimitiveValueType) + { + DeclaringType.EmitBox(ilgen); + } + } + + protected virtual void NewobjImpl(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + +#endif // EMITTERS + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdLoc.cs b/src/IKVM.Runtime/StringConstants.cs similarity index 66% rename from src/ikvmc/IKVM/Internal/MapXml/LdLoc.cs rename to src/IKVM.Runtime/StringConstants.cs index 4de4daa708..1cc5f1f221 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdLoc.cs +++ b/src/IKVM.Runtime/StringConstants.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,21 +22,18 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -using IKVM.Reflection.Emit; - -namespace IKVM.Internal.MapXml +namespace IKVM.Internal { - [XmlType("ldloc")] - public sealed class LdLoc : Instruction + + static class StringConstants { - [XmlAttribute("name")] - public string Name; - internal override void Generate(CodeGenContext context, CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Ldloc, (CodeEmitterLocal)context[Name]); - } + internal static readonly string CLINIT = string.Intern(""); + internal static readonly string INIT = string.Intern(""); + internal static readonly string SIG_VOID = string.Intern("()V"); + internal static readonly string FINALIZE = string.Intern("finalize"); + internal static readonly string CLONE = string.Intern("clone"); + } + } diff --git a/src/IKVM.Runtime/StubGen/RuntimeVisibleAnnotationsAttribute.cs b/src/IKVM.Runtime/StubGen/RuntimeVisibleAnnotationsAttribute.cs index 4dff879e8b..5674f0f198 100644 --- a/src/IKVM.Runtime/StubGen/RuntimeVisibleAnnotationsAttribute.cs +++ b/src/IKVM.Runtime/StubGen/RuntimeVisibleAnnotationsAttribute.cs @@ -56,7 +56,7 @@ internal void Add(object[] annot) private static string DecodeTypeName(string typeName) { -#if !FIRST_PASS && !STUB_GENERATOR +#if !FIRST_PASS && !EXPORTER int index = typeName.IndexOf(','); if (index > 0) { diff --git a/src/IKVM.Runtime/StubGen/SerialVersionUID.cs b/src/IKVM.Runtime/StubGen/SerialVersionUID.cs index a40cccac89..915dec98d9 100644 --- a/src/IKVM.Runtime/StubGen/SerialVersionUID.cs +++ b/src/IKVM.Runtime/StubGen/SerialVersionUID.cs @@ -21,163 +21,132 @@ Jeroen Frijters jeroen@frijters.net */ -using System; -using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Security.Cryptography; using IKVM.Attributes; using IKVM.Internal; -#if STUB_GENERATOR -using Type = IKVM.Reflection.Type; -#endif namespace IKVM.StubGen { + static class SerialVersionUID - { - private readonly static System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed(); - - internal static long Compute(TypeWrapper tw) - { - MemoryStream mem = new MemoryStream(); - BigEndianStream bes = new BigEndianStream(mem); - WriteClassName(bes, tw); - WriteModifiers(bes, tw); - WriteInterfaces(bes, tw); - WriteFields(bes, tw); - WriteStaticInitializer(bes, tw); - WriteConstructors(bes, tw); - WriteMethods(bes, tw); - byte[] buf = sha1.ComputeHash(mem.ToArray()); - long hash = 0; - for (int i = 7; i >= 0; i--) - { - hash <<= 8; - hash |= buf[i]; - } - return hash; - } - - private static void WriteClassName(BigEndianStream bes, TypeWrapper tw) - { - bes.WriteUtf8(tw.Name); - } - - private static void WriteModifiers(BigEndianStream bes, TypeWrapper tw) - { - Modifiers mods = tw.ReflectiveModifiers & (Modifiers.Public | Modifiers.Final | Modifiers.Interface | Modifiers.Abstract); - if ((mods & Modifiers.Interface) != 0) - { - mods &= ~Modifiers.Abstract; - if (HasJavaMethods(tw)) - { - mods |= Modifiers.Abstract; - } - } - bes.WriteUInt32((uint)mods); - } - - private static bool HasJavaMethods(TypeWrapper tw) - { - foreach (MethodWrapper mw in tw.GetMethods()) - { - if (!mw.IsHideFromReflection && !mw.IsClassInitializer) - { - return true; - } - } - return false; - } - - private static void WriteInterfaces(BigEndianStream bes, TypeWrapper tw) - { - TypeWrapper[] interfaces = (TypeWrapper[])tw.Interfaces.Clone(); - Array.Sort(interfaces, delegate(TypeWrapper tw1, TypeWrapper tw2) { return String.CompareOrdinal(tw1.Name, tw2.Name); }); - foreach (TypeWrapper iface in interfaces) - { - bes.WriteUtf8(iface.Name); - } - } - - private static void WriteFields(BigEndianStream bes, TypeWrapper tw) - { - List list = new List(); - foreach (FieldWrapper fw in tw.GetFields()) - { - if (!fw.IsHideFromReflection) - { - list.Add(fw); - } - } - list.Sort(delegate(FieldWrapper fw1, FieldWrapper fw2) { return String.CompareOrdinal(fw1.Name, fw2.Name); }); - foreach (FieldWrapper fw in list) - { - Modifiers mods = fw.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Volatile | Modifiers.Transient); - if (((mods & Modifiers.Private) == 0) || ((mods & (Modifiers.Static | Modifiers.Transient)) == 0)) - { - bes.WriteUtf8(fw.Name); - bes.WriteUInt32((uint)mods); - bes.WriteUtf8(fw.Signature.Replace('.', '/')); - } - } - } - - private static void WriteStaticInitializer(BigEndianStream bes, TypeWrapper tw) - { - Type type = tw.TypeAsTBD; - if (!type.IsArray && type.TypeInitializer != null) - { - if (!AttributeHelper.IsHideFromJava(type.TypeInitializer)) - { - bes.WriteUtf8(""); - bes.WriteUInt32((uint)Modifiers.Static); - bes.WriteUtf8("()V"); - } - } - } - - private static void WriteConstructors(BigEndianStream bes, TypeWrapper tw) - { - List list = new List(); - foreach (MethodWrapper mw in tw.GetMethods()) - { - if (mw.IsConstructor && !mw.IsHideFromReflection && !mw.IsPrivate) - { - list.Add(mw); - } - } - list.Sort(delegate(MethodWrapper mw1, MethodWrapper mw2) { return String.CompareOrdinal(mw1.Signature, mw2.Signature); }); - foreach (MethodWrapper mw in list) - { - Modifiers mods = mw.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Synchronized | Modifiers.Native | Modifiers.Abstract | Modifiers.Strictfp); - bes.WriteUtf8(mw.Name); - bes.WriteUInt32((uint)mods); - bes.WriteUtf8(mw.Signature); - } - } - - private static void WriteMethods(BigEndianStream bes, TypeWrapper tw) - { - List list = new List(); - foreach (MethodWrapper mw in tw.GetMethods()) - { - if (!mw.IsConstructor && !mw.IsHideFromReflection && !mw.IsPrivate) - { - list.Add(mw); - } - } - list.Sort(delegate(MethodWrapper mw1, MethodWrapper mw2) { - if (mw1.Name == mw2.Name) - return String.CompareOrdinal(mw1.Signature, mw2.Signature); - return String.CompareOrdinal(mw1.Name, mw2.Name); - }); - foreach (MethodWrapper mw in list) - { - Modifiers mods = mw.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Synchronized | Modifiers.Native | Modifiers.Abstract | Modifiers.Strictfp); - bes.WriteUtf8(mw.Name); - bes.WriteUInt32((uint)mods); - bes.WriteUtf8(mw.Signature); - } - } - } + { + + readonly static SHA1 sha1 = SHA1.Create(); + + internal static long Compute(TypeWrapper tw) + { + var mem = new MemoryStream(); + var bes = new BigEndianStream(mem); + WriteClassName(bes, tw); + WriteModifiers(bes, tw); + WriteInterfaces(bes, tw); + WriteFields(bes, tw); + WriteStaticInitializer(bes, tw); + WriteConstructors(bes, tw); + WriteMethods(bes, tw); + mem.Position = 0; + var buf = sha1.ComputeHash(mem); + var hash = 0; + for (var i = 7; i >= 0; i--) + { + hash <<= 8; + hash |= buf[i]; + } + + return hash; + } + + static void WriteClassName(BigEndianStream bes, TypeWrapper tw) + { + bes.WriteUtf8(tw.Name); + } + + static void WriteModifiers(BigEndianStream bes, TypeWrapper tw) + { + var mods = tw.ReflectiveModifiers & (Modifiers.Public | Modifiers.Final | Modifiers.Interface | Modifiers.Abstract); + if ((mods & Modifiers.Interface) != 0) + { + mods &= ~Modifiers.Abstract; + if (HasJavaMethods(tw)) + mods |= Modifiers.Abstract; + } + + bes.WriteUInt32((uint)mods); + } + + static bool HasJavaMethods(TypeWrapper tw) + { + return tw.GetMethods().Any(i => !i.IsHideFromReflection && !i.IsClassInitializer); + } + + static void WriteInterfaces(BigEndianStream bes, TypeWrapper tw) + { + foreach (var i in tw.Interfaces.OrderBy(i => i.Name)) + bes.WriteUtf8(i.Name); + } + + static void WriteFields(BigEndianStream bes, TypeWrapper tw) + { + foreach (var fw in tw.GetFields().Where(i => !i.IsHideFromReflection).OrderBy(i => i.Name)) + { + var mods = fw.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Volatile | Modifiers.Transient); + if (((mods & Modifiers.Private) == 0) || ((mods & (Modifiers.Static | Modifiers.Transient)) == 0)) + { + bes.WriteUtf8(fw.Name); + bes.WriteUInt32((uint)mods); + bes.WriteUtf8(fw.Signature.Replace('.', '/')); + } + } + } + + static void WriteStaticInitializer(BigEndianStream bes, TypeWrapper tw) + { + var type = tw.TypeAsTBD; + if (!type.IsArray && type.TypeInitializer != null) + { + if (!AttributeHelper.IsHideFromJava(type.TypeInitializer)) + { + bes.WriteUtf8(""); + bes.WriteUInt32((uint)Modifiers.Static); + bes.WriteUtf8("()V"); + } + } + } + + static void WriteConstructors(BigEndianStream bes, TypeWrapper tw) + { + var ctors = tw.GetMethods() + .Where(i => i.IsConstructor && !i.IsHideFromReflection && !i.IsPrivate) + .OrderBy(i => i.Signature); + + foreach (var ctor in ctors) + { + var mods = ctor.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Synchronized | Modifiers.Native | Modifiers.Abstract | Modifiers.Strictfp); + bes.WriteUtf8(ctor.Name); + bes.WriteUInt32((uint)mods); + bes.WriteUtf8(ctor.Signature); + } + } + + static void WriteMethods(BigEndianStream bes, TypeWrapper tw) + { + var methods = tw.GetMethods() + .Where(i => !i.IsConstructor && !i.IsHideFromReflection && !i.IsPrivate) + .OrderBy(i => i.Name) + .ThenBy(i => i.Signature); + + foreach (var method in methods) + { + var mods = method.Modifiers & (Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static | Modifiers.Final | Modifiers.Synchronized | Modifiers.Native | Modifiers.Abstract | Modifiers.Strictfp); + bes.WriteUtf8(method.Name); + bes.WriteUInt32((uint)mods); + bes.WriteUtf8(method.Signature); + } + } + + } + } diff --git a/src/IKVM.Runtime/StubGen/StubGenerator.cs b/src/IKVM.Runtime/StubGen/StubGenerator.cs index 8f26c1c28c..3e08781a38 100644 --- a/src/IKVM.Runtime/StubGen/StubGenerator.cs +++ b/src/IKVM.Runtime/StubGen/StubGenerator.cs @@ -24,252 +24,249 @@ Jeroen Frijters using System; using System.IO; using System.Collections.Generic; -#if STUB_GENERATOR + +using IKVM.Attributes; +using IKVM.Internal; +using IKVM.Runtime; + +#if EXPORTER using IKVM.Reflection; + using Type = IKVM.Reflection.Type; #else using System.Reflection; - #endif -using IKVM.Attributes; -using IKVM.Internal; namespace IKVM.StubGen { + static class StubGenerator - { - internal static void WriteClass(Stream stream, TypeWrapper tw, bool includeNonPublicInterfaces, bool includeNonPublicMembers, bool includeSerialVersionUID, bool includeParameterNames) - { - string name = tw.Name.Replace('.', '/'); - string super = null; - if (tw.IsInterface) - { - super = "java/lang/Object"; - } - else if (tw.BaseTypeWrapper != null) - { - super = tw.BaseTypeWrapper.Name.Replace('.', '/'); - } - ClassFileWriter writer = new ClassFileWriter(tw.Modifiers, name, super, 0, includeParameterNames ? (ushort)52 : (ushort)49); - foreach (TypeWrapper iface in tw.Interfaces) - { - if (iface.IsPublic || includeNonPublicInterfaces) - { - writer.AddInterface(iface.Name.Replace('.', '/')); - } - } - InnerClassesAttribute innerClassesAttribute = null; - if (tw.DeclaringTypeWrapper != null) - { - TypeWrapper outer = tw.DeclaringTypeWrapper; - string innername = name; - int idx = name.LastIndexOf('$'); - if (idx >= 0) - { - innername = innername.Substring(idx + 1); - } - innerClassesAttribute = new InnerClassesAttribute(writer); - innerClassesAttribute.Add(name, outer.Name.Replace('.', '/'), innername, (ushort)tw.ReflectiveModifiers); - } - foreach (TypeWrapper inner in tw.InnerClasses) - { - if (inner.IsPublic) - { - if (innerClassesAttribute == null) - { - innerClassesAttribute = new InnerClassesAttribute(writer); - } - string namePart = inner.Name; - namePart = namePart.Substring(namePart.LastIndexOf('$') + 1); - innerClassesAttribute.Add(inner.Name.Replace('.', '/'), name, namePart, (ushort)inner.ReflectiveModifiers); - } - } - if (innerClassesAttribute != null) - { - writer.AddAttribute(innerClassesAttribute); - } - string genericTypeSignature = tw.GetGenericSignature(); - if (genericTypeSignature != null) - { - writer.AddStringAttribute("Signature", genericTypeSignature); - } - AddAnnotations(writer, writer, tw.TypeAsBaseType); - AddTypeAnnotations(writer, writer, tw, tw.GetRawTypeAnnotations()); - writer.AddStringAttribute("IKVM.NET.Assembly", GetAssemblyName(tw)); - if (tw.TypeAsBaseType.IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false)) - { - writer.AddAttribute(new DeprecatedAttribute(writer)); - } - foreach (MethodWrapper mw in tw.GetMethods()) - { - if (!mw.IsHideFromReflection && (mw.IsPublic || mw.IsProtected || includeNonPublicMembers)) - { - FieldOrMethod m; - // HACK javac has a bug in com.sun.tools.javac.code.Types.isSignaturePolymorphic() where it assumes that - // MethodHandle doesn't have any native methods with an empty argument list - // (or at least it throws a NPE when it examines the signature of a method without any parameters when it - // accesses argtypes.tail.tail) - if (mw.Name == "" || (tw == CoreClasses.java.lang.invoke.MethodHandle.Wrapper && (mw.Modifiers & Modifiers.Native) == 0)) - { - m = writer.AddMethod(mw.Modifiers, mw.Name, mw.Signature.Replace('.', '/')); - CodeAttribute code = new CodeAttribute(writer); - code.MaxLocals = (ushort)(mw.GetParameters().Length * 2 + 1); - code.MaxStack = 3; - ushort index1 = writer.AddClass("java/lang/UnsatisfiedLinkError"); - ushort index2 = writer.AddString("ikvmstub generated stubs can only be used on IKVM.NET"); - ushort index3 = writer.AddMethodRef("java/lang/UnsatisfiedLinkError", "", "(Ljava/lang/String;)V"); - code.ByteCode = new byte[] { - 187, (byte)(index1 >> 8), (byte)index1, // new java/lang/UnsatisfiedLinkError + { + + internal static void WriteClass(Stream stream, TypeWrapper tw, bool includeNonPublicTypes, bool includeNonPublicInterfaces, bool includeNonPublicMembers, bool includeParameterNames, bool includeSerialVersionUID) + { + string name = tw.Name.Replace('.', '/'); + string super = null; + + if (tw.IsInterface) + super = "java/lang/Object"; + else if (tw.BaseTypeWrapper != null) + super = tw.BaseTypeWrapper.Name.Replace('.', '/'); + + var writer = new ClassFileWriter(tw.Modifiers, name, super, 0, includeParameterNames ? (ushort)52 : (ushort)49); + foreach (var iface in tw.Interfaces) + if (iface.IsPublic || includeNonPublicInterfaces) + writer.AddInterface(iface.Name.Replace('.', '/')); + + InnerClassesAttribute innerClassesAttribute = null; + if (tw.DeclaringTypeWrapper != null) + { + var outer = tw.DeclaringTypeWrapper; + string innername = name; + int idx = name.LastIndexOf('$'); + if (idx >= 0) + innername = innername.Substring(idx + 1); + + innerClassesAttribute = new InnerClassesAttribute(writer); + innerClassesAttribute.Add(name, outer.Name.Replace('.', '/'), innername, (ushort)tw.ReflectiveModifiers); + } + + foreach (var inner in tw.InnerClasses) + { + if (inner.IsPublic || includeNonPublicTypes) + { + if (innerClassesAttribute == null) + innerClassesAttribute = new InnerClassesAttribute(writer); + + var namePart = inner.Name; + namePart = namePart.Substring(namePart.LastIndexOf('$') + 1); + innerClassesAttribute.Add(inner.Name.Replace('.', '/'), name, namePart, (ushort)inner.ReflectiveModifiers); + } + } + + if (innerClassesAttribute != null) + writer.AddAttribute(innerClassesAttribute); + + var genericTypeSignature = tw.GetGenericSignature(); + if (genericTypeSignature != null) + writer.AddStringAttribute("Signature", genericTypeSignature); + + AddAnnotations(writer, writer, tw.TypeAsBaseType); + AddTypeAnnotations(writer, writer, tw, tw.GetRawTypeAnnotations()); + writer.AddStringAttribute("IKVM.NET.Assembly", GetAssemblyName(tw)); + if (tw.TypeAsBaseType.IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false)) + writer.AddAttribute(new DeprecatedAttribute(writer)); + + foreach (var mw in tw.GetMethods()) + { + if (!mw.IsHideFromReflection && (mw.IsPublic || mw.IsProtected || includeNonPublicMembers)) + { + FieldOrMethod m; + // HACK javac has a bug in com.sun.tools.javac.code.Types.isSignaturePolymorphic() where it assumes that + // MethodHandle doesn't have any native methods with an empty argument list + // (or at least it throws a NPE when it examines the signature of a method without any parameters when it + // accesses argtypes.tail.tail) + if (mw.Name == "" || (tw == CoreClasses.java.lang.invoke.MethodHandle.Wrapper && (mw.Modifiers & Modifiers.Native) == 0)) + { + m = writer.AddMethod(mw.Modifiers, mw.Name, mw.Signature.Replace('.', '/')); + CodeAttribute code = new CodeAttribute(writer); + code.MaxLocals = (ushort)(mw.GetParameters().Length * 2 + 1); + code.MaxStack = 3; + ushort index1 = writer.AddClass("java/lang/UnsatisfiedLinkError"); + ushort index2 = writer.AddString("ikvmstub generated stubs can only be used on IKVM.NET"); + ushort index3 = writer.AddMethodRef("java/lang/UnsatisfiedLinkError", "", "(Ljava/lang/String;)V"); + code.ByteCode = new byte[] { + 187, (byte)(index1 >> 8), (byte)index1, // new java/lang/UnsatisfiedLinkError 89, // dup - 19, (byte)(index2 >> 8), (byte)index2, // ldc_w "..." + 19, (byte)(index2 >> 8), (byte)index2, // ldc_w "..." 183, (byte)(index3 >> 8), (byte)index3, // invokespecial java/lang/UnsatisfiedLinkError/init()V 191 // athrow }; - m.AddAttribute(code); - } - else - { - Modifiers mods = mw.Modifiers; - if ((mods & Modifiers.Abstract) == 0) - { - mods |= Modifiers.Native; - } - m = writer.AddMethod(mods, mw.Name, mw.Signature.Replace('.', '/')); - if (mw.IsOptionalAttributeAnnotationValue) - { - m.AddAttribute(new AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, mw.ReturnType))); - } - } - MethodBase mb = mw.GetMethod(); - if (mb != null) - { - ThrowsAttribute throws = AttributeHelper.GetThrows(mb); - if (throws == null) - { - string[] throwsArray = mw.GetDeclaredExceptions(); - if (throwsArray != null && throwsArray.Length > 0) - { - ExceptionsAttribute attrib = new ExceptionsAttribute(writer); - foreach (string ex in throwsArray) - { - attrib.Add(ex.Replace('.', '/')); - } - m.AddAttribute(attrib); - } - } - else - { - ExceptionsAttribute attrib = new ExceptionsAttribute(writer); - if (throws.classes != null) - { - foreach (string ex in throws.classes) - { - attrib.Add(ex.Replace('.', '/')); - } - } - if (throws.types != null) - { - foreach (Type ex in throws.types) - { - attrib.Add(ClassLoaderWrapper.GetWrapperFromType(ex).Name.Replace('.', '/')); - } - } - m.AddAttribute(attrib); - } - if (mb.IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false) - // HACK the instancehelper methods are marked as Obsolete (to direct people toward the ikvm.extensions methods instead) - // but in the Java world most of them are not deprecated (and to keep the Japi results clean we need to reflect this) - && (!mb.Name.StartsWith("instancehelper_") - || mb.DeclaringType.FullName != "java.lang.String" - // the Java deprecated methods actually have two Obsolete attributes - || GetObsoleteCount(mb) == 2)) - { - m.AddAttribute(new DeprecatedAttribute(writer)); - } - CustomAttributeData attr = GetAnnotationDefault(mb); - if (attr != null) - { - m.AddAttribute(new AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, attr.ConstructorArguments[0]))); - } - if (includeParameterNames) - { - MethodParametersEntry[] mp = tw.GetMethodParameters(mw); - if (mp == MethodParametersEntry.Malformed) - { - m.AddAttribute(new MethodParametersAttribute(writer, null, null)); - } - else if (mp != null) - { - ushort[] names = new ushort[mp.Length]; - ushort[] flags = new ushort[mp.Length]; - for (int i = 0; i < names.Length; i++) - { - if (mp[i].name != null) - { - names[i] = writer.AddUtf8(mp[i].name); - } - flags[i] = mp[i].flags; - } - m.AddAttribute(new MethodParametersAttribute(writer, names, flags)); - } - } - } - string sig = tw.GetGenericMethodSignature(mw); - if (sig != null) - { - m.AddAttribute(writer.MakeStringAttribute("Signature", sig)); - } - AddAnnotations(writer, m, mw.GetMethod()); - AddParameterAnnotations(writer, m, mw.GetMethod()); - AddTypeAnnotations(writer, m, tw, tw.GetMethodRawTypeAnnotations(mw)); - } - } - bool hasSerialVersionUID = false; - foreach (FieldWrapper fw in tw.GetFields()) - { - if (!fw.IsHideFromReflection) - { - bool isSerialVersionUID = includeSerialVersionUID && fw.IsSerialVersionUID; - hasSerialVersionUID |= isSerialVersionUID; - if (fw.IsPublic || fw.IsProtected || isSerialVersionUID || includeNonPublicMembers) - { - object constant = null; - if (fw.GetField() != null && fw.GetField().IsLiteral && (fw.FieldTypeWrapper.IsPrimitive || fw.FieldTypeWrapper == CoreClasses.java.lang.String.Wrapper)) - { - constant = fw.GetField().GetRawConstantValue(); - if (fw.GetField().FieldType.IsEnum) - { - constant = EnumHelper.GetPrimitiveValue(EnumHelper.GetUnderlyingType(fw.GetField().FieldType), constant); - } - } - FieldOrMethod f = writer.AddField(fw.Modifiers, fw.Name, fw.Signature.Replace('.', '/'), constant); - string sig = tw.GetGenericFieldSignature(fw); - if (sig != null) - { - f.AddAttribute(writer.MakeStringAttribute("Signature", sig)); - } - if (fw.GetField() != null && fw.GetField().IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false)) - { - f.AddAttribute(new DeprecatedAttribute(writer)); - } - AddAnnotations(writer, f, fw.GetField()); - AddTypeAnnotations(writer, f, tw, tw.GetFieldRawTypeAnnotations(fw)); - } - } - } - if (includeSerialVersionUID && !hasSerialVersionUID && IsSerializable(tw)) - { - // class is serializable but doesn't have an explicit serialVersionUID, so we add the field to record - // the serialVersionUID as we see it (mainly to make the Japi reports more realistic) - writer.AddField(Modifiers.Private | Modifiers.Static | Modifiers.Final, "serialVersionUID", "J", SerialVersionUID.Compute(tw)); - } - AddMetaAnnotations(writer, tw); - writer.Write(stream); - } + m.AddAttribute(code); + } + else + { + Modifiers mods = mw.Modifiers; + if ((mods & Modifiers.Abstract) == 0) + { + mods |= Modifiers.Native; + } + m = writer.AddMethod(mods, mw.Name, mw.Signature.Replace('.', '/')); + if (mw.IsOptionalAttributeAnnotationValue) + { + m.AddAttribute(new AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, mw.ReturnType))); + } + } + MethodBase mb = mw.GetMethod(); + if (mb != null) + { + ThrowsAttribute throws = AttributeHelper.GetThrows(mb); + if (throws == null) + { + string[] throwsArray = mw.GetDeclaredExceptions(); + if (throwsArray != null && throwsArray.Length > 0) + { + ExceptionsAttribute attrib = new ExceptionsAttribute(writer); + foreach (string ex in throwsArray) + { + attrib.Add(ex.Replace('.', '/')); + } + m.AddAttribute(attrib); + } + } + else + { + ExceptionsAttribute attrib = new ExceptionsAttribute(writer); + if (throws.classes != null) + { + foreach (string ex in throws.classes) + { + attrib.Add(ex.Replace('.', '/')); + } + } + if (throws.types != null) + { + foreach (Type ex in throws.types) + { + attrib.Add(ClassLoaderWrapper.GetWrapperFromType(ex).Name.Replace('.', '/')); + } + } + m.AddAttribute(attrib); + } + if (mb.IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false) + // HACK the instancehelper methods are marked as Obsolete (to direct people toward the ikvm.extensions methods instead) + // but in the Java world most of them are not deprecated (and to keep the Japi results clean we need to reflect this) + && (!mb.Name.StartsWith("instancehelper_") + || mb.DeclaringType.FullName != "java.lang.String" + // the Java deprecated methods actually have two Obsolete attributes + || GetObsoleteCount(mb) == 2)) + { + m.AddAttribute(new DeprecatedAttribute(writer)); + } + CustomAttributeData attr = GetAnnotationDefault(mb); + if (attr != null) + { + m.AddAttribute(new AnnotationDefaultClassFileAttribute(writer, GetAnnotationDefault(writer, attr.ConstructorArguments[0]))); + } + if (includeParameterNames) + { + MethodParametersEntry[] mp = tw.GetMethodParameters(mw); + if (mp == MethodParametersEntry.Malformed) + { + m.AddAttribute(new MethodParametersAttribute(writer, null, null)); + } + else if (mp != null) + { + ushort[] names = new ushort[mp.Length]; + ushort[] flags = new ushort[mp.Length]; + for (int i = 0; i < names.Length; i++) + { + if (mp[i].name != null) + { + names[i] = writer.AddUtf8(mp[i].name); + } + flags[i] = (ushort)mp[i].accessFlags; + } + m.AddAttribute(new MethodParametersAttribute(writer, names, flags)); + } + } + } + string sig = tw.GetGenericMethodSignature(mw); + if (sig != null) + { + m.AddAttribute(writer.MakeStringAttribute("Signature", sig)); + } + AddAnnotations(writer, m, mw.GetMethod()); + AddParameterAnnotations(writer, m, mw.GetMethod()); + AddTypeAnnotations(writer, m, tw, tw.GetMethodRawTypeAnnotations(mw)); + } + } + bool hasSerialVersionUID = false; + foreach (FieldWrapper fw in tw.GetFields()) + { + if (!fw.IsHideFromReflection) + { + bool isSerialVersionUID = includeSerialVersionUID && fw.IsSerialVersionUID; + hasSerialVersionUID |= isSerialVersionUID; + if (fw.IsPublic || fw.IsProtected || isSerialVersionUID || includeNonPublicMembers) + { + object constant = null; + if (fw.GetField() != null && fw.GetField().IsLiteral && (fw.FieldTypeWrapper.IsPrimitive || fw.FieldTypeWrapper == CoreClasses.java.lang.String.Wrapper)) + { + constant = fw.GetField().GetRawConstantValue(); + if (fw.GetField().FieldType.IsEnum) + { + constant = EnumHelper.GetPrimitiveValue(EnumHelper.GetUnderlyingType(fw.GetField().FieldType), constant); + } + } + FieldOrMethod f = writer.AddField(fw.Modifiers, fw.Name, fw.Signature.Replace('.', '/'), constant); + string sig = tw.GetGenericFieldSignature(fw); + if (sig != null) + { + f.AddAttribute(writer.MakeStringAttribute("Signature", sig)); + } + if (fw.GetField() != null && fw.GetField().IsDefined(JVM.Import(typeof(ObsoleteAttribute)), false)) + { + f.AddAttribute(new DeprecatedAttribute(writer)); + } + AddAnnotations(writer, f, fw.GetField()); + AddTypeAnnotations(writer, f, tw, tw.GetFieldRawTypeAnnotations(fw)); + } + } + } + if (includeSerialVersionUID && !hasSerialVersionUID && IsSerializable(tw)) + { + // class is serializable but doesn't have an explicit serialVersionUID, so we add the field to record + // the serialVersionUID as we see it (mainly to make the Japi reports more realistic) + writer.AddField(Modifiers.Private | Modifiers.Static | Modifiers.Final, "serialVersionUID", "J", SerialVersionUID.Compute(tw)); + } + AddMetaAnnotations(writer, tw); + writer.Write(stream); + } - private static void AddAnnotations(ClassFileWriter writer, IAttributeOwner target, MemberInfo source) - { -#if !FIRST_PASS && !STUB_GENERATOR + private static void AddAnnotations(ClassFileWriter writer, IAttributeOwner target, MemberInfo source) + { +#if !FIRST_PASS && !EXPORTER if (source != null) { RuntimeVisibleAnnotationsAttribute attr = null; @@ -291,11 +288,11 @@ private static void AddAnnotations(ClassFileWriter writer, IAttributeOwner targe } } #endif - } + } - private static void AddParameterAnnotations(ClassFileWriter writer, FieldOrMethod target, MethodBase source) - { -#if !FIRST_PASS && !STUB_GENERATOR + private static void AddParameterAnnotations(ClassFileWriter writer, FieldOrMethod target, MethodBase source) + { +#if !FIRST_PASS && !EXPORTER if (source != null) { RuntimeVisibleParameterAnnotationsAttribute attr = null; @@ -334,11 +331,11 @@ private static void AddParameterAnnotations(ClassFileWriter writer, FieldOrMetho } } #endif - } + } - private static void AddTypeAnnotations(ClassFileWriter writer, IAttributeOwner target, TypeWrapper tw, byte[] typeAnnotations) - { -#if !FIRST_PASS && !STUB_GENERATOR + private static void AddTypeAnnotations(ClassFileWriter writer, IAttributeOwner target, TypeWrapper tw, byte[] typeAnnotations) + { +#if !FIRST_PASS && !EXPORTER if (typeAnnotations != null) { typeAnnotations = (byte[])typeAnnotations.Clone(); @@ -359,121 +356,121 @@ private static void AddTypeAnnotations(ClassFileWriter writer, IAttributeOwner t target.AddAttribute(new RuntimeVisibleTypeAnnotationsAttribute(writer, typeAnnotations)); } #endif - } + } - private static void FixupTypeAnnotationConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) - { - switch (typeAnnotations[pos++]) // target_type - { - case 0x00: - case 0x01: - case 0x16: - pos++; - break; - case 0x10: - case 0x11: - case 0x12: - case 0x17: - pos += 2; - break; - case 0x13: - case 0x14: - case 0x15: - break; - default: - throw new IndexOutOfRangeException(); - } - byte path_length = typeAnnotations[pos++]; - pos += path_length * 2; - FixupAnnotationConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); - } + private static void FixupTypeAnnotationConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) + { + switch (typeAnnotations[pos++]) // target_type + { + case 0x00: + case 0x01: + case 0x16: + pos++; + break; + case 0x10: + case 0x11: + case 0x12: + case 0x17: + pos += 2; + break; + case 0x13: + case 0x14: + case 0x15: + break; + default: + throw new IndexOutOfRangeException(); + } + byte path_length = typeAnnotations[pos++]; + pos += path_length * 2; + FixupAnnotationConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); + } - private static void FixupAnnotationConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) - { - FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); - ushort num_components = ReadUInt16BE(typeAnnotations, ref pos); - for (int i = 0; i < num_components; i++) - { - FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); - FixupAnnotationComponentValueConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); - } - } + private static void FixupAnnotationConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) + { + FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); + ushort num_components = ReadUInt16BE(typeAnnotations, ref pos); + for (int i = 0; i < num_components; i++) + { + FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); + FixupAnnotationComponentValueConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); + } + } - private static void FixupConstantPoolIndex(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) - { - ushort index = ReadUInt16BE(typeAnnotations, ref pos); - object item = constantPool[index]; - if (item is int) - { - index = writer.AddInt((int)item); - } - else if (item is long) - { - index = writer.AddLong((long)item); - } - else if (item is float) - { - index = writer.AddFloat((float)item); - } - else if (item is double) - { - index = writer.AddDouble((double)item); - } - else if (item is string) - { - index = writer.AddUtf8((string)item); - } - else - { - throw new IndexOutOfRangeException(); - } - typeAnnotations[pos - 2] = (byte)(index >> 8); - typeAnnotations[pos - 1] = (byte)(index >> 0); - } + private static void FixupConstantPoolIndex(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) + { + ushort index = ReadUInt16BE(typeAnnotations, ref pos); + object item = constantPool[index]; + if (item is int) + { + index = writer.AddInt((int)item); + } + else if (item is long) + { + index = writer.AddLong((long)item); + } + else if (item is float) + { + index = writer.AddFloat((float)item); + } + else if (item is double) + { + index = writer.AddDouble((double)item); + } + else if (item is string) + { + index = writer.AddUtf8((string)item); + } + else + { + throw new IndexOutOfRangeException(); + } + typeAnnotations[pos - 2] = (byte)(index >> 8); + typeAnnotations[pos - 1] = (byte)(index >> 0); + } - private static void FixupAnnotationComponentValueConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) - { - switch ((char)typeAnnotations[pos++]) // tag - { - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - case 's': - case 'c': - FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); - break; - case 'e': - FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); - FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); - break; - case '@': - FixupAnnotationConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); - break; - case '[': - ushort num_values = ReadUInt16BE(typeAnnotations, ref pos); - for (int i = 0; i < num_values; i++) - { - FixupAnnotationComponentValueConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); - } - break; - default: - throw new IndexOutOfRangeException(); - } - } + private static void FixupAnnotationComponentValueConstantPoolIndexes(ClassFileWriter writer, byte[] typeAnnotations, object[] constantPool, ref int pos) + { + switch ((char)typeAnnotations[pos++]) // tag + { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 's': + case 'c': + FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); + break; + case 'e': + FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); + FixupConstantPoolIndex(writer, typeAnnotations, constantPool, ref pos); + break; + case '@': + FixupAnnotationConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); + break; + case '[': + ushort num_values = ReadUInt16BE(typeAnnotations, ref pos); + for (int i = 0; i < num_values; i++) + { + FixupAnnotationComponentValueConstantPoolIndexes(writer, typeAnnotations, constantPool, ref pos); + } + break; + default: + throw new IndexOutOfRangeException(); + } + } - private static ushort ReadUInt16BE(byte[] buf, ref int pos) - { - ushort s = (ushort)((buf[pos] << 8) + buf[pos + 1]); - pos += 2; - return s; - } + private static ushort ReadUInt16BE(byte[] buf, ref int pos) + { + ushort s = (ushort)((buf[pos] << 8) + buf[pos + 1]); + pos += 2; + return s; + } -#if !FIRST_PASS && !STUB_GENERATOR +#if !FIRST_PASS && !EXPORTER private static object[] GetAnnotation(CustomAttributeData cad) { if (cad.ConstructorArguments.Count == 1 && cad.ConstructorArguments[0].ArgumentType == typeof(object[]) && @@ -557,37 +554,37 @@ private static string EncodeTypeName(TypeWrapper tw) } #endif - private static object[] UnpackArray(IList list) - { - object[] arr = new object[list.Count]; - for (int i = 0; i < arr.Length; i++) - { - if (list[i].Value is IList) - { - arr[i] = UnpackArray((IList)list[i].Value); - } - else - { - arr[i] = list[i].Value; - } - } - return arr; - } + private static object[] UnpackArray(IList list) + { + object[] arr = new object[list.Count]; + for (int i = 0; i < arr.Length; i++) + { + if (list[i].Value is IList) + { + arr[i] = UnpackArray((IList)list[i].Value); + } + else + { + arr[i] = list[i].Value; + } + } + return arr; + } - private static int GetObsoleteCount(MethodBase mb) - { -#if STUB_GENERATOR - return mb.__GetCustomAttributes(JVM.Import(typeof(ObsoleteAttribute)), false).Count; + private static int GetObsoleteCount(MethodBase mb) + { +#if EXPORTER + return mb.__GetCustomAttributes(JVM.Import(typeof(ObsoleteAttribute)), false).Count; #else return mb.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length; #endif - } + } - private static CustomAttributeData GetAnnotationDefault(MethodBase mb) - { -#if STUB_GENERATOR - IList attr = CustomAttributeData.__GetCustomAttributes(mb, JVM.LoadType(typeof(AnnotationDefaultAttribute)), false); - return attr.Count == 1 ? attr[0] : null; + private static CustomAttributeData GetAnnotationDefault(MethodBase mb) + { +#if EXPORTER + IList attr = CustomAttributeData.__GetCustomAttributes(mb, JVM.LoadType(typeof(AnnotationDefaultAttribute)), false); + return attr.Count == 1 ? attr[0] : null; #else foreach (CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(mb)) { @@ -598,298 +595,298 @@ private static CustomAttributeData GetAnnotationDefault(MethodBase mb) } return null; #endif - } + } - private static string GetAssemblyName(TypeWrapper tw) - { - ClassLoaderWrapper loader = tw.GetClassLoader(); - AssemblyClassLoader acl = loader as AssemblyClassLoader; - if (acl != null) - { - return acl.GetAssembly(tw).FullName; - } - else - { - return ((GenericClassLoaderWrapper)loader).GetName(); - } - } + private static string GetAssemblyName(TypeWrapper tw) + { + ClassLoaderWrapper loader = tw.GetClassLoader(); + AssemblyClassLoader acl = loader as AssemblyClassLoader; + if (acl != null) + { + return acl.GetAssembly(tw).FullName; + } + else + { + return ((GenericClassLoaderWrapper)loader).GetName(); + } + } - private static bool IsSerializable(TypeWrapper tw) - { - if (tw.Name == "java.io.Serializable") - { - return true; - } - while (tw != null) - { - foreach (TypeWrapper iface in tw.Interfaces) - { - if (IsSerializable(iface)) - { - return true; - } - } - tw = tw.BaseTypeWrapper; - } - return false; - } + private static bool IsSerializable(TypeWrapper tw) + { + if (tw.Name == "java.io.Serializable") + { + return true; + } + while (tw != null) + { + foreach (TypeWrapper iface in tw.Interfaces) + { + if (IsSerializable(iface)) + { + return true; + } + } + tw = tw.BaseTypeWrapper; + } + return false; + } - private static void AddMetaAnnotations(ClassFileWriter writer, TypeWrapper tw) - { - DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase attributeAnnotation = tw as DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase; - if (attributeAnnotation != null) - { - // TODO write the annotation directly, instead of going thru the object[] encoding - RuntimeVisibleAnnotationsAttribute annot = new RuntimeVisibleAnnotationsAttribute(writer); - annot.Add(new object[] { - AnnotationDefaultAttribute.TAG_ANNOTATION, - "Ljava/lang/annotation/Retention;", - "value", - new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME" } - }); - AttributeTargets validOn = attributeAnnotation.AttributeTargets; - List targets = new List(); - targets.Add(AnnotationDefaultAttribute.TAG_ARRAY); - if ((validOn & (AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate | AttributeTargets.Assembly)) != 0) - { - targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "TYPE" }); - } - if ((validOn & AttributeTargets.Constructor) != 0) - { - targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "CONSTRUCTOR" }); - } - if ((validOn & AttributeTargets.Field) != 0) - { - targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "FIELD" }); - } - if ((validOn & (AttributeTargets.Method | AttributeTargets.ReturnValue)) != 0) - { - targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "METHOD" }); - } - if ((validOn & AttributeTargets.Parameter) != 0) - { - targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "PARAMETER" }); - } - annot.Add(new object[] { - AnnotationDefaultAttribute.TAG_ANNOTATION, - "Ljava/lang/annotation/Target;", - "value", - targets.ToArray() - }); - if (IsRepeatableAnnotation(tw)) - { - annot.Add(new object[] { - AnnotationDefaultAttribute.TAG_ANNOTATION, - "Ljava/lang/annotation/Repeatable;", - "value", - new object[] { AnnotationDefaultAttribute.TAG_CLASS, "L" + (tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix).Replace('.', '/') + ";" } - }); - } - writer.AddAttribute(annot); - } - } + private static void AddMetaAnnotations(ClassFileWriter writer, TypeWrapper tw) + { + DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase attributeAnnotation = tw as DotNetTypeWrapper.AttributeAnnotationTypeWrapperBase; + if (attributeAnnotation != null) + { + // TODO write the annotation directly, instead of going thru the object[] encoding + RuntimeVisibleAnnotationsAttribute annot = new RuntimeVisibleAnnotationsAttribute(writer); + annot.Add(new object[] { + AnnotationDefaultAttribute.TAG_ANNOTATION, + "Ljava/lang/annotation/Retention;", + "value", + new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME" } + }); + AttributeTargets validOn = attributeAnnotation.AttributeTargets; + List targets = new List(); + targets.Add(AnnotationDefaultAttribute.TAG_ARRAY); + if ((validOn & (AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate | AttributeTargets.Assembly)) != 0) + { + targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "TYPE" }); + } + if ((validOn & AttributeTargets.Constructor) != 0) + { + targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "CONSTRUCTOR" }); + } + if ((validOn & AttributeTargets.Field) != 0) + { + targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "FIELD" }); + } + if ((validOn & (AttributeTargets.Method | AttributeTargets.ReturnValue)) != 0) + { + targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "METHOD" }); + } + if ((validOn & AttributeTargets.Parameter) != 0) + { + targets.Add(new object[] { AnnotationDefaultAttribute.TAG_ENUM, "Ljava/lang/annotation/ElementType;", "PARAMETER" }); + } + annot.Add(new object[] { + AnnotationDefaultAttribute.TAG_ANNOTATION, + "Ljava/lang/annotation/Target;", + "value", + targets.ToArray() + }); + if (IsRepeatableAnnotation(tw)) + { + annot.Add(new object[] { + AnnotationDefaultAttribute.TAG_ANNOTATION, + "Ljava/lang/annotation/Repeatable;", + "value", + new object[] { AnnotationDefaultAttribute.TAG_CLASS, "L" + (tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix).Replace('.', '/') + ";" } + }); + } + writer.AddAttribute(annot); + } + } - private static bool IsRepeatableAnnotation(TypeWrapper tw) - { - foreach (TypeWrapper nested in tw.InnerClasses) - { - if (nested.Name == tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix) - { - return true; - } - } - return false; - } + private static bool IsRepeatableAnnotation(TypeWrapper tw) + { + foreach (TypeWrapper nested in tw.InnerClasses) + { + if (nested.Name == tw.Name + DotNetTypeWrapper.AttributeAnnotationMultipleSuffix) + { + return true; + } + } + return false; + } - private static byte[] GetAnnotationDefault(ClassFileWriter classFile, TypeWrapper type) - { - MemoryStream mem = new MemoryStream(); - BigEndianStream bes = new BigEndianStream(mem); - if (type == PrimitiveTypeWrapper.BOOLEAN) - { - bes.WriteByte((byte)'Z'); - bes.WriteUInt16(classFile.AddInt(0)); - } - else if (type == PrimitiveTypeWrapper.BYTE) - { - bes.WriteByte((byte)'B'); - bes.WriteUInt16(classFile.AddInt(0)); - } - else if (type == PrimitiveTypeWrapper.CHAR) - { - bes.WriteByte((byte)'C'); - bes.WriteUInt16(classFile.AddInt(0)); - } - else if (type == PrimitiveTypeWrapper.SHORT) - { - bes.WriteByte((byte)'S'); - bes.WriteUInt16(classFile.AddInt(0)); - } - else if (type == PrimitiveTypeWrapper.INT) - { - bes.WriteByte((byte)'I'); - bes.WriteUInt16(classFile.AddInt(0)); - } - else if (type == PrimitiveTypeWrapper.FLOAT) - { - bes.WriteByte((byte)'F'); - bes.WriteUInt16(classFile.AddFloat(0)); - } - else if (type == PrimitiveTypeWrapper.LONG) - { - bes.WriteByte((byte)'J'); - bes.WriteUInt16(classFile.AddLong(0)); - } - else if (type == PrimitiveTypeWrapper.DOUBLE) - { - bes.WriteByte((byte)'D'); - bes.WriteUInt16(classFile.AddDouble(0)); - } - else if (type == CoreClasses.java.lang.String.Wrapper) - { - bes.WriteByte((byte)'s'); - bes.WriteUInt16(classFile.AddUtf8("")); - } - else if ((type.Modifiers & Modifiers.Enum) != 0) - { - bes.WriteByte((byte)'e'); - bes.WriteUInt16(classFile.AddUtf8("L" + type.Name.Replace('.', '/') + ";")); - bes.WriteUInt16(classFile.AddUtf8("__unspecified")); - } - else if (type == CoreClasses.java.lang.Class.Wrapper) - { - bes.WriteByte((byte)'c'); - bes.WriteUInt16(classFile.AddUtf8("Likvm/internal/__unspecified;")); - } - else if (type.IsArray) - { - bes.WriteByte((byte)'['); - bes.WriteUInt16(0); - } - else - { - throw new InvalidOperationException(); - } - return mem.ToArray(); - } + private static byte[] GetAnnotationDefault(ClassFileWriter classFile, TypeWrapper type) + { + MemoryStream mem = new MemoryStream(); + BigEndianStream bes = new BigEndianStream(mem); + if (type == PrimitiveTypeWrapper.BOOLEAN) + { + bes.WriteByte((byte)'Z'); + bes.WriteUInt16(classFile.AddInt(0)); + } + else if (type == PrimitiveTypeWrapper.BYTE) + { + bes.WriteByte((byte)'B'); + bes.WriteUInt16(classFile.AddInt(0)); + } + else if (type == PrimitiveTypeWrapper.CHAR) + { + bes.WriteByte((byte)'C'); + bes.WriteUInt16(classFile.AddInt(0)); + } + else if (type == PrimitiveTypeWrapper.SHORT) + { + bes.WriteByte((byte)'S'); + bes.WriteUInt16(classFile.AddInt(0)); + } + else if (type == PrimitiveTypeWrapper.INT) + { + bes.WriteByte((byte)'I'); + bes.WriteUInt16(classFile.AddInt(0)); + } + else if (type == PrimitiveTypeWrapper.FLOAT) + { + bes.WriteByte((byte)'F'); + bes.WriteUInt16(classFile.AddFloat(0)); + } + else if (type == PrimitiveTypeWrapper.LONG) + { + bes.WriteByte((byte)'J'); + bes.WriteUInt16(classFile.AddLong(0)); + } + else if (type == PrimitiveTypeWrapper.DOUBLE) + { + bes.WriteByte((byte)'D'); + bes.WriteUInt16(classFile.AddDouble(0)); + } + else if (type == CoreClasses.java.lang.String.Wrapper) + { + bes.WriteByte((byte)'s'); + bes.WriteUInt16(classFile.AddUtf8("")); + } + else if ((type.Modifiers & Modifiers.Enum) != 0) + { + bes.WriteByte((byte)'e'); + bes.WriteUInt16(classFile.AddUtf8("L" + type.Name.Replace('.', '/') + ";")); + bes.WriteUInt16(classFile.AddUtf8("__unspecified")); + } + else if (type == CoreClasses.java.lang.Class.Wrapper) + { + bes.WriteByte((byte)'c'); + bes.WriteUInt16(classFile.AddUtf8("Likvm/internal/__unspecified;")); + } + else if (type.IsArray) + { + bes.WriteByte((byte)'['); + bes.WriteUInt16(0); + } + else + { + throw new InvalidOperationException(); + } + return mem.ToArray(); + } - private static byte[] GetAnnotationDefault(ClassFileWriter classFile, CustomAttributeTypedArgument value) - { - MemoryStream mem = new MemoryStream(); - BigEndianStream bes = new BigEndianStream(mem); - try - { - WriteAnnotationElementValue(classFile, bes, value); - } - catch (InvalidCastException) - { - Warning("Warning: incorrect annotation default value"); - } - catch (IndexOutOfRangeException) - { - Warning("Warning: incorrect annotation default value"); - } - return mem.ToArray(); - } + private static byte[] GetAnnotationDefault(ClassFileWriter classFile, CustomAttributeTypedArgument value) + { + MemoryStream mem = new MemoryStream(); + BigEndianStream bes = new BigEndianStream(mem); + try + { + WriteAnnotationElementValue(classFile, bes, value); + } + catch (InvalidCastException) + { + Warning("Warning: incorrect annotation default value"); + } + catch (IndexOutOfRangeException) + { + Warning("Warning: incorrect annotation default value"); + } + return mem.ToArray(); + } - private static void WriteAnnotationElementValue(ClassFileWriter classFile, BigEndianStream bes, CustomAttributeTypedArgument value) - { - if (value.ArgumentType == Types.Boolean) - { - bes.WriteByte((byte)'Z'); - bes.WriteUInt16(classFile.AddInt((bool)value.Value ? 1 : 0)); - } - else if (value.ArgumentType == Types.Byte) - { - bes.WriteByte((byte)'B'); - bes.WriteUInt16(classFile.AddInt((byte)value.Value)); - } - else if (value.ArgumentType == Types.Char) - { - bes.WriteByte((byte)'C'); - bes.WriteUInt16(classFile.AddInt((char)value.Value)); - } - else if (value.ArgumentType == Types.Int16) - { - bes.WriteByte((byte)'S'); - bes.WriteUInt16(classFile.AddInt((short)value.Value)); - } - else if (value.ArgumentType == Types.Int32) - { - bes.WriteByte((byte)'I'); - bes.WriteUInt16(classFile.AddInt((int)value.Value)); - } - else if (value.ArgumentType == Types.Single) - { - bes.WriteByte((byte)'F'); - bes.WriteUInt16(classFile.AddFloat((float)value.Value)); - } - else if (value.ArgumentType == Types.Int64) - { - bes.WriteByte((byte)'J'); - bes.WriteUInt16(classFile.AddLong((long)value.Value)); - } - else if (value.ArgumentType == Types.Double) - { - bes.WriteByte((byte)'D'); - bes.WriteUInt16(classFile.AddDouble((double)value.Value)); - } - else if (value.ArgumentType == Types.String) - { - bes.WriteByte((byte)'s'); - bes.WriteUInt16(classFile.AddUtf8((string)value.Value)); - } - else if (value.ArgumentType == Types.Object.MakeArrayType()) - { - CustomAttributeTypedArgument[] array = (CustomAttributeTypedArgument[])value.Value; - byte type = (byte)array[0].Value; - if (type == AnnotationDefaultAttribute.TAG_ARRAY) - { - bes.WriteByte((byte)'['); - bes.WriteUInt16((ushort)(array.Length - 1)); - for (int i = 1; i < array.Length; i++) - { - WriteAnnotationElementValue(classFile, bes, array[i]); - } - } - else if (type == AnnotationDefaultAttribute.TAG_CLASS) - { - bes.WriteByte((byte)'c'); - bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); - } - else if (type == AnnotationDefaultAttribute.TAG_ENUM) - { - bes.WriteByte((byte)'e'); - bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); - bes.WriteUInt16(classFile.AddUtf8((string)array[2].Value)); - } - else if (type == AnnotationDefaultAttribute.TAG_ANNOTATION) - { - bes.WriteByte((byte)'@'); - bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); - bes.WriteUInt16((ushort)((array.Length - 2) / 2)); - for (int i = 2; i < array.Length; i += 2) - { - bes.WriteUInt16(classFile.AddUtf8((string)array[i].Value)); - WriteAnnotationElementValue(classFile, bes, array[i + 1]); - } - } - else - { - Warning("Warning: incorrect annotation default element tag: " + type); - } - } - else - { - Warning("Warning: incorrect annotation default element type: " + value.ArgumentType); - } - } + private static void WriteAnnotationElementValue(ClassFileWriter classFile, BigEndianStream bes, CustomAttributeTypedArgument value) + { + if (value.ArgumentType == Types.Boolean) + { + bes.WriteByte((byte)'Z'); + bes.WriteUInt16(classFile.AddInt((bool)value.Value ? 1 : 0)); + } + else if (value.ArgumentType == Types.Byte) + { + bes.WriteByte((byte)'B'); + bes.WriteUInt16(classFile.AddInt((byte)value.Value)); + } + else if (value.ArgumentType == Types.Char) + { + bes.WriteByte((byte)'C'); + bes.WriteUInt16(classFile.AddInt((char)value.Value)); + } + else if (value.ArgumentType == Types.Int16) + { + bes.WriteByte((byte)'S'); + bes.WriteUInt16(classFile.AddInt((short)value.Value)); + } + else if (value.ArgumentType == Types.Int32) + { + bes.WriteByte((byte)'I'); + bes.WriteUInt16(classFile.AddInt((int)value.Value)); + } + else if (value.ArgumentType == Types.Single) + { + bes.WriteByte((byte)'F'); + bes.WriteUInt16(classFile.AddFloat((float)value.Value)); + } + else if (value.ArgumentType == Types.Int64) + { + bes.WriteByte((byte)'J'); + bes.WriteUInt16(classFile.AddLong((long)value.Value)); + } + else if (value.ArgumentType == Types.Double) + { + bes.WriteByte((byte)'D'); + bes.WriteUInt16(classFile.AddDouble((double)value.Value)); + } + else if (value.ArgumentType == Types.String) + { + bes.WriteByte((byte)'s'); + bes.WriteUInt16(classFile.AddUtf8((string)value.Value)); + } + else if (value.ArgumentType == Types.Object.MakeArrayType()) + { + var array = (IReadOnlyList)value.Value; + byte type = (byte)array[0].Value; + if (type == AnnotationDefaultAttribute.TAG_ARRAY) + { + bes.WriteByte((byte)'['); + bes.WriteUInt16((ushort)(array.Count - 1)); + for (int i = 1; i < array.Count; i++) + { + WriteAnnotationElementValue(classFile, bes, array[i]); + } + } + else if (type == AnnotationDefaultAttribute.TAG_CLASS) + { + bes.WriteByte((byte)'c'); + bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); + } + else if (type == AnnotationDefaultAttribute.TAG_ENUM) + { + bes.WriteByte((byte)'e'); + bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); + bes.WriteUInt16(classFile.AddUtf8((string)array[2].Value)); + } + else if (type == AnnotationDefaultAttribute.TAG_ANNOTATION) + { + bes.WriteByte((byte)'@'); + bes.WriteUInt16(classFile.AddUtf8((string)array[1].Value)); + bes.WriteUInt16((ushort)((array.Count - 2) / 2)); + for (int i = 2; i < array.Count; i += 2) + { + bes.WriteUInt16(classFile.AddUtf8((string)array[i].Value)); + WriteAnnotationElementValue(classFile, bes, array[i + 1]); + } + } + else + { + Warning("Warning: incorrect annotation default element tag: " + type); + } + } + else + { + Warning("Warning: incorrect annotation default element type: " + value.ArgumentType); + } + } - private static void Warning(string message) - { -#if STUB_GENERATOR - Console.Error.WriteLine(message); + private static void Warning(string message) + { +#if EXPORTER + Console.Error.WriteLine(message); #endif - } - } + } + } } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Box.cs b/src/IKVM.Runtime/TypeFlags.cs similarity index 69% rename from src/ikvmc/IKVM/Internal/MapXml/Box.cs rename to src/IKVM.Runtime/TypeFlags.cs index 3ad3775ad7..8d942f5521 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Box.cs +++ b/src/IKVM.Runtime/TypeFlags.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2010 Jeroen Frijters + Copyright (C) 2002-2015 Jeroen Frijters This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,17 +22,25 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System; -using IKVM.Reflection.Emit; - -namespace IKVM.Internal.MapXml +namespace IKVM.Internal { - [XmlType("box")] - public sealed class Box : TypeInstruction + + [Flags] + enum TypeFlags : ushort { - public Box() : base(OpCodes.Box) - { - } + + None = 0, + HasIncompleteInterfaceImplementation = 1, + InternalAccess = 2, + HasStaticInitializer = 4, + VerifyError = 8, + ClassFormatError = 16, + HasUnsupportedAbstractMethods = 32, + Anonymous = 64, + Linked = 128, + } + } diff --git a/src/IKVM.Runtime/TypeNameUtil.cs b/src/IKVM.Runtime/TypeNameUtil.cs new file mode 100644 index 0000000000..ba501098ef --- /dev/null +++ b/src/IKVM.Runtime/TypeNameUtil.cs @@ -0,0 +1,154 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + static class TypeNameUtil + { + // note that MangleNestedTypeName() assumes that there are less than 16 special characters + private const string specialCharactersString = "\\+,[]*&\u0000"; + internal const string ProxiesContainer = "__"; + + internal static string ReplaceIllegalCharacters(string name) + { + name = UnicodeUtil.EscapeInvalidSurrogates(name); + // only the NUL character is illegal in CLR type names, so we replace it with a space + return name.Replace('\u0000', ' '); + } + + internal static string Unescape(string name) + { + int pos = name.IndexOf('\\'); + if (pos == -1) + { + return name; + } + System.Text.StringBuilder sb = new System.Text.StringBuilder(name.Length); + sb.Append(name, 0, pos); + for (int i = pos; i < name.Length; i++) + { + char c = name[i]; + if (c == '\\') + { + c = name[++i]; + } + sb.Append(c); + } + return sb.ToString(); + } + + internal static string MangleNestedTypeName(string name) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + foreach (char c in name) + { + int index = specialCharactersString.IndexOf(c); + if (c == '.') + { + sb.Append("_"); + } + else if (c == '_') + { + sb.Append("^-"); + } + else if (index == -1) + { + sb.Append(c); + if (c == '^') + { + sb.Append(c); + } + } + else + { + sb.Append('^').AppendFormat("{0:X1}", index); + } + } + return sb.ToString(); + } + + internal static string UnmangleNestedTypeName(string name) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + int index = specialCharactersString.IndexOf(c); + if (c == '_') + { + sb.Append('.'); + } + else if (c == '^') + { + c = name[++i]; + if (c == '-') + { + sb.Append('_'); + } + else if (c == '^') + { + sb.Append('^'); + } + else + { + sb.Append(specialCharactersString[c - '0']); + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + internal static string GetProxyNestedName(TypeWrapper[] interfaces) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + foreach (TypeWrapper tw in interfaces) + { + sb.Append(tw.Name.Length).Append('|').Append(tw.Name); + } + return TypeNameUtil.MangleNestedTypeName(sb.ToString()); + } + + internal static string GetProxyName(TypeWrapper[] interfaces) + { + return ProxiesContainer + "+" + GetProxyNestedName(interfaces); + } + } + +} diff --git a/src/IKVM.Runtime/TypeWrapper.cs b/src/IKVM.Runtime/TypeWrapper.cs index d2255c840d..08225a5ccd 100644 --- a/src/IKVM.Runtime/TypeWrapper.cs +++ b/src/IKVM.Runtime/TypeWrapper.cs @@ -24,14 +24,11 @@ Jeroen Frijters using System; using System.Collections.Generic; using System.Diagnostics; -using System.Security; -using System.Security.Permissions; using IKVM.Attributes; using IKVM.Runtime; -using IKVM.Runtime.Syntax; -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; @@ -41,5819 +38,1665 @@ Jeroen Frijters using System.Reflection.Emit; #endif +#if IMPORTER +using IKVM.Tools.Importer; +#endif + namespace IKVM.Internal { - static class StringConstants - { - - internal static readonly string CLINIT = string.Intern(""); - internal static readonly string INIT = string.Intern(""); - internal static readonly string SIG_VOID = string.Intern("()V"); - internal static readonly string FINALIZE = string.Intern("finalize"); - internal static readonly string CLONE = string.Intern("clone"); - - } - - struct ExModifiers - { - internal readonly Modifiers Modifiers; - internal readonly bool IsInternal; - - internal ExModifiers(Modifiers modifiers, bool isInternal) - { - this.Modifiers = modifiers; - this.IsInternal = isInternal; - } - } - - struct MethodParametersEntry + /// + /// Encapsulates the information about a type available to Java. + /// + internal abstract class TypeWrapper { - internal static readonly MethodParametersEntry[] Malformed = new MethodParametersEntry[0]; - internal string name; - internal ushort flags; - } - static class AttributeHelper - { -#if STATIC_COMPILER - private static CustomAttributeBuilder ghostInterfaceAttribute; - private static CustomAttributeBuilder deprecatedAttribute; - private static CustomAttributeBuilder editorBrowsableNever; - private static ConstructorInfo implementsAttribute; - private static ConstructorInfo throwsAttribute; - private static ConstructorInfo sourceFileAttribute; - private static ConstructorInfo lineNumberTableAttribute1; - private static ConstructorInfo lineNumberTableAttribute2; - private static ConstructorInfo enclosingMethodAttribute; - private static ConstructorInfo signatureAttribute; - private static ConstructorInfo methodParametersAttribute; - private static ConstructorInfo runtimeVisibleTypeAnnotationsAttribute; - private static ConstructorInfo constantPoolAttribute; - private static CustomAttributeBuilder paramArrayAttribute; - private static ConstructorInfo nonNestedInnerClassAttribute; - private static ConstructorInfo nonNestedOuterClassAttribute; - private static readonly Type typeofModifiers = JVM.LoadType(typeof(Modifiers)); - private static readonly Type typeofSourceFileAttribute = JVM.LoadType(typeof(SourceFileAttribute)); - private static readonly Type typeofLineNumberTableAttribute = JVM.LoadType(typeof(LineNumberTableAttribute)); -#endif // STATIC_COMPILER - private static readonly Type typeofRemappedClassAttribute = JVM.LoadType(typeof(RemappedClassAttribute)); - private static readonly Type typeofRemappedTypeAttribute = JVM.LoadType(typeof(RemappedTypeAttribute)); - private static readonly Type typeofModifiersAttribute = JVM.LoadType(typeof(ModifiersAttribute)); - private static readonly Type typeofRemappedInterfaceMethodAttribute = JVM.LoadType(typeof(RemappedInterfaceMethodAttribute)); - private static readonly Type typeofNameSigAttribute = JVM.LoadType(typeof(NameSigAttribute)); - private static readonly Type typeofJavaModuleAttribute = JVM.LoadType(typeof(JavaModuleAttribute)); - private static readonly Type typeofSignatureAttribute = JVM.LoadType(typeof(SignatureAttribute)); - private static readonly Type typeofInnerClassAttribute = JVM.LoadType(typeof(InnerClassAttribute)); - private static readonly Type typeofImplementsAttribute = JVM.LoadType(typeof(ImplementsAttribute)); - private static readonly Type typeofGhostInterfaceAttribute = JVM.LoadType(typeof(GhostInterfaceAttribute)); - private static readonly Type typeofExceptionIsUnsafeForMappingAttribute = JVM.LoadType(typeof(ExceptionIsUnsafeForMappingAttribute)); - private static readonly Type typeofThrowsAttribute = JVM.LoadType(typeof(ThrowsAttribute)); - private static readonly Type typeofHideFromJavaAttribute = JVM.LoadType(typeof(HideFromJavaAttribute)); - private static readonly Type typeofHideFromJavaFlags = JVM.LoadType(typeof(HideFromJavaFlags)); - private static readonly Type typeofNoPackagePrefixAttribute = JVM.LoadType(typeof(NoPackagePrefixAttribute)); - private static readonly Type typeofAnnotationAttributeAttribute = JVM.LoadType(typeof(AnnotationAttributeAttribute)); - private static readonly Type typeofNonNestedInnerClassAttribute = JVM.LoadType(typeof(NonNestedInnerClassAttribute)); - private static readonly Type typeofNonNestedOuterClassAttribute = JVM.LoadType(typeof(NonNestedOuterClassAttribute)); - private static readonly Type typeofEnclosingMethodAttribute = JVM.LoadType(typeof(EnclosingMethodAttribute)); - private static readonly Type typeofMethodParametersAttribute = JVM.LoadType(typeof(MethodParametersAttribute)); - private static readonly Type typeofRuntimeVisibleTypeAnnotationsAttribute = JVM.LoadType(typeof(RuntimeVisibleTypeAnnotationsAttribute)); - private static readonly Type typeofConstantPoolAttribute = JVM.LoadType(typeof(ConstantPoolAttribute)); - private static readonly CustomAttributeBuilder hideFromJavaAttribute = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(Type.EmptyTypes), new object[0]); - private static readonly CustomAttributeBuilder hideFromReflection = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(new Type[] { typeofHideFromJavaFlags }), new object[] { HideFromJavaFlags.Reflection | HideFromJavaFlags.StackTrace | HideFromJavaFlags.StackWalk }); - - // we don't want beforefieldinit - static AttributeHelper() { } - -#if STATIC_COMPILER - private static object ParseValue(ClassLoaderWrapper loader, TypeWrapper tw, string val) - { - if (tw == CoreClasses.java.lang.String.Wrapper) - { - return val; - } - else if (tw.IsUnloadable) - { - throw new FatalCompilerErrorException(Message.MapFileTypeNotFound, tw.Name); - } - else if (tw.TypeAsTBD.IsEnum) - { - return EnumHelper.Parse(tw.TypeAsTBD, val); - } - else if (tw.TypeAsTBD == Types.Type) - { - TypeWrapper valtw = loader.LoadClassByDottedNameFast(val); - if (valtw != null) - { - return valtw.TypeAsBaseType; - } - return StaticCompiler.Universe.GetType(val, true); - } - else if (tw == PrimitiveTypeWrapper.BOOLEAN) - { - return bool.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.BYTE) - { - return (byte)sbyte.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.CHAR) - { - return char.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.SHORT) - { - return short.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.INT) - { - return int.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.FLOAT) - { - return float.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.LONG) - { - return long.Parse(val); - } - else if (tw == PrimitiveTypeWrapper.DOUBLE) - { - return double.Parse(val); - } - else - { - throw new NotImplementedException(); - } - } + internal const Modifiers UnloadableModifiersHack = Modifiers.Final | Modifiers.Interface | Modifiers.Private; + internal const Modifiers VerifierTypeModifiersHack = Modifiers.Final | Modifiers.Interface; - internal static void SetCustomAttribute(ClassLoaderWrapper loader, TypeBuilder tb, IKVM.Internal.MapXml.Attribute attr) - { - bool declarativeSecurity; - CustomAttributeBuilder cab = CreateCustomAttribute(loader, attr, out declarativeSecurity); - if (declarativeSecurity) - { - tb.__AddDeclarativeSecurity(cab); - } - else - { - tb.SetCustomAttribute(cab); - } - } + internal static readonly TypeWrapper[] EmptyArray = new TypeWrapper[0]; + static readonly object flagsLock = new object(); + + readonly string name; // java name (e.g. java.lang.Object) + readonly Modifiers modifiers; + TypeFlags flags; + MethodWrapper[] methods; + FieldWrapper[] fields; +#if !IMPORTER && !EXPORTER + java.lang.Class classObject; +#endif - internal static void SetCustomAttribute(ClassLoaderWrapper loader, FieldBuilder fb, IKVM.Internal.MapXml.Attribute attr) + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + internal TypeWrapper(TypeFlags flags, Modifiers modifiers, string name) { - fb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); - } + Profiler.Count("TypeWrapper"); - internal static void SetCustomAttribute(ClassLoaderWrapper loader, ParameterBuilder pb, IKVM.Internal.MapXml.Attribute attr) - { - pb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); + this.flags = flags; + this.modifiers = modifiers; + this.name = name == null ? null : String.Intern(name); } - internal static void SetCustomAttribute(ClassLoaderWrapper loader, MethodBuilder mb, IKVM.Internal.MapXml.Attribute attr) - { - bool declarativeSecurity; - CustomAttributeBuilder cab = CreateCustomAttribute(loader, attr, out declarativeSecurity); - if (declarativeSecurity) - { - mb.__AddDeclarativeSecurity(cab); - } - else - { - mb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); - } - } +#if EMITTERS - internal static void SetCustomAttribute(ClassLoaderWrapper loader, PropertyBuilder pb, IKVM.Internal.MapXml.Attribute attr) + internal void EmitClassLiteral(CodeEmitter ilgen) { - pb.SetCustomAttribute(CreateCustomAttribute(loader, attr)); - } + Debug.Assert(!this.IsPrimitive); - internal static void SetCustomAttribute(ClassLoaderWrapper loader, AssemblyBuilder ab, IKVM.Internal.MapXml.Attribute attr) - { - ab.SetCustomAttribute(CreateCustomAttribute(loader, attr)); - } + Type type = GetClassLiteralType(); - private static void GetAttributeArgsAndTypes(ClassLoaderWrapper loader, IKVM.Internal.MapXml.Attribute attr, out Type[] argTypes, out object[] args) - { - // TODO add error handling - TypeWrapper[] twargs = loader.ArgTypeWrapperListFromSig(attr.Sig, LoadMode.Link); - argTypes = new Type[twargs.Length]; - args = new object[argTypes.Length]; - for (int i = 0; i < twargs.Length; i++) + // note that this has to be the same check as in LazyInitClass + if (!this.IsFastClassLiteralSafe || IsForbiddenTypeParameterType(type)) { - argTypes[i] = twargs[i].TypeAsSignatureType; - TypeWrapper tw = twargs[i]; - if (tw == CoreClasses.java.lang.Object.Wrapper) + int rank = 0; + while (ReflectUtil.IsVector(type)) { - tw = loader.FieldTypeWrapperFromSig(attr.Params[i].Sig, LoadMode.Link); + rank++; + type = type.GetElementType(); } - if (tw.IsArray) + if (rank == 0) { - Array arr = Array.CreateInstance(Type.__GetSystemType(Type.GetTypeCode(tw.ElementTypeWrapper.TypeAsArrayType)), attr.Params[i].Elements.Length); - for (int j = 0; j < arr.Length; j++) - { - arr.SetValue(ParseValue(loader, tw.ElementTypeWrapper, attr.Params[i].Elements[j].Value), j); - } - args[i] = arr; + ilgen.Emit(OpCodes.Ldtoken, type); + Compiler.getClassFromTypeHandle.EmitCall(ilgen); } else { - args[i] = ParseValue(loader, tw, attr.Params[i].Value); + ilgen.Emit(OpCodes.Ldtoken, type); + ilgen.EmitLdc_I4(rank); + Compiler.getClassFromTypeHandle2.EmitCall(ilgen); } } + else + { + ilgen.Emit(OpCodes.Ldsfld, RuntimeHelperTypes.GetClassLiteralField(type)); + } } - private static CustomAttributeBuilder CreateCustomAttribute(ClassLoaderWrapper loader, IKVM.Internal.MapXml.Attribute attr) - { - bool ignore; - return CreateCustomAttribute(loader, attr, out ignore); - } +#endif - private static CustomAttributeBuilder CreateCustomAttribute(ClassLoaderWrapper loader, IKVM.Internal.MapXml.Attribute attr, out bool isDeclarativeSecurity) + private Type GetClassLiteralType() { - // TODO add error handling - Type[] argTypes; - object[] args; - GetAttributeArgsAndTypes(loader, attr, out argTypes, out args); - if (attr.Type != null) + Debug.Assert(!this.IsPrimitive); + + TypeWrapper tw = this; + if (tw.IsGhostArray) { - Type t = StaticCompiler.GetTypeForMapXml(loader, attr.Type); - isDeclarativeSecurity = t.IsSubclassOf(Types.SecurityAttribute); - ConstructorInfo ci = t.GetConstructor(argTypes); - if (ci == null) - { - throw new InvalidOperationException(string.Format("Constructor missing: {0}::{1}", attr.Type, attr.Sig)); - } - PropertyInfo[] namedProperties; - object[] propertyValues; - if (attr.Properties != null) - { - namedProperties = new PropertyInfo[attr.Properties.Length]; - propertyValues = new object[attr.Properties.Length]; - for (int i = 0; i < namedProperties.Length; i++) - { - namedProperties[i] = t.GetProperty(attr.Properties[i].Name); - propertyValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Properties[i].Sig, LoadMode.Link), attr.Properties[i].Value); - } - } - else - { - namedProperties = new PropertyInfo[0]; - propertyValues = new object[0]; - } - FieldInfo[] namedFields; - object[] fieldValues; - if (attr.Fields != null) - { - namedFields = new FieldInfo[attr.Fields.Length]; - fieldValues = new object[attr.Fields.Length]; - for (int i = 0; i < namedFields.Length; i++) - { - namedFields[i] = t.GetField(attr.Fields[i].Name); - fieldValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Fields[i].Sig, LoadMode.Link), attr.Fields[i].Value); - } - } - else - { - namedFields = new FieldInfo[0]; - fieldValues = new object[0]; - } - return new CustomAttributeBuilder(ci, args, namedProperties, propertyValues, namedFields, fieldValues); + var rank = tw.ArrayRank; + while (tw.IsArray) + tw = tw.ElementTypeWrapper; + + return ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, rank); } else { - if (attr.Properties != null) - { - throw new NotImplementedException("Setting property values on Java attributes is not implemented"); - } - TypeWrapper t = loader.LoadClassByDottedName(attr.Class); - isDeclarativeSecurity = t.TypeAsBaseType.IsSubclassOf(Types.SecurityAttribute); - FieldInfo[] namedFields; - object[] fieldValues; - if (attr.Fields != null) - { - namedFields = new FieldInfo[attr.Fields.Length]; - fieldValues = new object[attr.Fields.Length]; - for (int i = 0; i < namedFields.Length; i++) - { - FieldWrapper fw = t.GetFieldWrapper(attr.Fields[i].Name, attr.Fields[i].Sig); - fw.Link(); - namedFields[i] = fw.GetField(); - fieldValues[i] = ParseValue(loader, loader.FieldTypeWrapperFromSig(attr.Fields[i].Sig, LoadMode.Link), attr.Fields[i].Value); - } - } - else - { - namedFields = new FieldInfo[0]; - fieldValues = new object[0]; - } - MethodWrapper mw = t.GetMethodWrapper("", attr.Sig, false); - if (mw == null) - { - throw new InvalidOperationException(string.Format("Constructor missing: {0}::{1}", attr.Class, attr.Sig)); - } - mw.Link(); - ConstructorInfo ci = (mw.GetMethod() as ConstructorInfo) ?? ((MethodInfo)mw.GetMethod()).__AsConstructorInfo(); - return new CustomAttributeBuilder(ci, args, namedFields, fieldValues); + return tw.IsRemapped ? tw.TypeAsBaseType : tw.TypeAsTBD; } } - private static CustomAttributeBuilder GetEditorBrowsableNever() + private static bool IsForbiddenTypeParameterType(Type type) { - if (editorBrowsableNever == null) - { - // to avoid having to load (and find) System.dll, we construct a symbolic CustomAttributeBuilder - AssemblyName name = Types.Object.Assembly.GetName(); + // these are the types that may not be used as a type argument when instantiating a generic type + return type == Types.Void #if NETFRAMEWORK - name.Name = "System"; + || type == JVM.Import(typeof(ArgIterator)) #endif - Universe u = StaticCompiler.Universe; - Type typeofEditorBrowsableAttribute = u.ResolveType(Types.Object.Assembly, "System.ComponentModel.EditorBrowsableAttribute, " + name.FullName); - Type typeofEditorBrowsableState = u.ResolveType(Types.Object.Assembly, "System.ComponentModel.EditorBrowsableState, " + name.FullName); - u.MissingTypeIsValueType += delegate (Type type) { return type == typeofEditorBrowsableState; }; - ConstructorInfo ctor = (ConstructorInfo)typeofEditorBrowsableAttribute.__CreateMissingMethod(ConstructorInfo.ConstructorName, - CallingConventions.Standard | CallingConventions.HasThis, null, default(CustomModifiers), new Type[] { typeofEditorBrowsableState }, null); - editorBrowsableNever = CustomAttributeBuilder.__FromBlob(ctor, new byte[] { 01, 00, 01, 00, 00, 00, 00, 00 }); - } - return editorBrowsableNever; + || type == JVM.Import(typeof(RuntimeArgumentHandle)) + || type == JVM.Import(typeof(TypedReference)) + || type.ContainsGenericParameters + || type.IsByRef; } - internal static void SetEditorBrowsableNever(TypeBuilder tb) + internal virtual bool IsFastClassLiteralSafe { - tb.SetCustomAttribute(GetEditorBrowsableNever()); + get { return false; } } - internal static void SetEditorBrowsableNever(MethodBuilder mb) - { - mb.SetCustomAttribute(GetEditorBrowsableNever()); - } +#if !IMPORTER && !EXPORTER - internal static void SetEditorBrowsableNever(PropertyBuilder pb) + internal void SetClassObject(java.lang.Class classObject) { - pb.SetCustomAttribute(GetEditorBrowsableNever()); + this.classObject = classObject; } - internal static void SetDeprecatedAttribute(MethodBuilder mb) + internal java.lang.Class ClassObject { - if (deprecatedAttribute == null) + get { - deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); - } - mb.SetCustomAttribute(deprecatedAttribute); - } + Debug.Assert(!IsUnloadable && !IsVerifierType); + if (classObject == null) + LazyInitClass(); - internal static void SetDeprecatedAttribute(TypeBuilder tb) - { - if (deprecatedAttribute == null) - { - deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + return classObject; } - tb.SetCustomAttribute(deprecatedAttribute); } - internal static void SetDeprecatedAttribute(FieldBuilder fb) - { - if (deprecatedAttribute == null) - { - deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); - } - fb.SetCustomAttribute(deprecatedAttribute); - } +#if !FIRST_PASS - internal static void SetDeprecatedAttribute(PropertyBuilder pb) + private java.lang.Class GetPrimitiveClass() { - if (deprecatedAttribute == null) + if (this == PrimitiveTypeWrapper.BYTE) { - deprecatedAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + return java.lang.Byte.TYPE; } - pb.SetCustomAttribute(deprecatedAttribute); - } - - internal static void SetThrowsAttribute(MethodBuilder mb, string[] exceptions) - { - if (exceptions != null && exceptions.Length != 0) + else if (this == PrimitiveTypeWrapper.CHAR) { - if (throwsAttribute == null) - { - throwsAttribute = typeofThrowsAttribute.GetConstructor(new Type[] { JVM.Import(typeof(string[])) }); - } - exceptions = UnicodeUtil.EscapeInvalidSurrogates(exceptions); - mb.SetCustomAttribute(new CustomAttributeBuilder(throwsAttribute, new object[] { exceptions })); + return java.lang.Character.TYPE; } - } - - internal static void SetGhostInterface(TypeBuilder typeBuilder) - { - if (ghostInterfaceAttribute == null) + else if (this == PrimitiveTypeWrapper.DOUBLE) { - ghostInterfaceAttribute = new CustomAttributeBuilder(typeofGhostInterfaceAttribute.GetConstructor(Type.EmptyTypes), new object[0]); + return java.lang.Double.TYPE; } - typeBuilder.SetCustomAttribute(ghostInterfaceAttribute); - } - - internal static void SetNonNestedInnerClass(TypeBuilder typeBuilder, string className) - { - if (nonNestedInnerClassAttribute == null) + else if (this == PrimitiveTypeWrapper.FLOAT) { - nonNestedInnerClassAttribute = typeofNonNestedInnerClassAttribute.GetConstructor(new Type[] { Types.String }); + return java.lang.Float.TYPE; } - typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(nonNestedInnerClassAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(className) })); - } - - internal static void SetNonNestedOuterClass(TypeBuilder typeBuilder, string className) - { - if (nonNestedOuterClassAttribute == null) + else if (this == PrimitiveTypeWrapper.INT) { - nonNestedOuterClassAttribute = typeofNonNestedOuterClassAttribute.GetConstructor(new Type[] { Types.String }); + return java.lang.Integer.TYPE; } - typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(nonNestedOuterClassAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(className) })); - } -#endif // STATIC_COMPILER - - internal static void HideFromReflection(MethodBuilder mb) - { - mb.SetCustomAttribute(hideFromReflection); - } - - internal static void HideFromReflection(FieldBuilder fb) - { - fb.SetCustomAttribute(hideFromReflection); - } - - internal static void HideFromReflection(PropertyBuilder pb) - { - pb.SetCustomAttribute(hideFromReflection); - } - - internal static void HideFromJava(TypeBuilder typeBuilder) - { - typeBuilder.SetCustomAttribute(hideFromJavaAttribute); - } - - internal static void HideFromJava(MethodBuilder mb) - { - mb.SetCustomAttribute(hideFromJavaAttribute); - } - - internal static void HideFromJava(MethodBuilder mb, HideFromJavaFlags flags) - { - CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(new Type[] { typeofHideFromJavaFlags }), new object[] { flags }); - mb.SetCustomAttribute(cab); - } - - internal static void HideFromJava(FieldBuilder fb) - { - fb.SetCustomAttribute(hideFromJavaAttribute); - } - -#if STATIC_COMPILER - internal static void HideFromJava(PropertyBuilder pb) - { - pb.SetCustomAttribute(hideFromJavaAttribute); - } -#endif // STATIC_COMPILER - - internal static bool IsHideFromJava(Type type) - { - return type.IsDefined(typeofHideFromJavaAttribute, false) - || (type.IsNested && (type.DeclaringType.IsDefined(typeofHideFromJavaAttribute, false) || type.Name.StartsWith("__<", StringComparison.Ordinal))); - } - - internal static bool IsHideFromJava(MemberInfo mi) - { - return (GetHideFromJavaFlags(mi) & HideFromJavaFlags.Code) != 0; - } - - internal static HideFromJavaFlags GetHideFromJavaFlags(MemberInfo mi) - { - // NOTE all privatescope fields and methods are "hideFromJava" - // because Java cannot deal with the potential name clashes - FieldInfo fi = mi as FieldInfo; - if (fi != null && (fi.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope) + else if (this == PrimitiveTypeWrapper.LONG) { - return HideFromJavaFlags.All; + return java.lang.Long.TYPE; } - MethodBase mb = mi as MethodBase; - if (mb != null && (mb.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope) + else if (this == PrimitiveTypeWrapper.SHORT) { - return HideFromJavaFlags.All; + return java.lang.Short.TYPE; } - if (mi.Name.StartsWith("__<", StringComparison.Ordinal)) + else if (this == PrimitiveTypeWrapper.BOOLEAN) { - return HideFromJavaFlags.All; + return java.lang.Boolean.TYPE; } -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = mi.GetCustomAttributes(typeofHideFromJavaAttribute, false); - if (attr.Length == 1) + else if (this == PrimitiveTypeWrapper.VOID) { - return ((HideFromJavaAttribute)attr[0]).Flags; + return java.lang.Void.TYPE; } -#else - IList attr = CustomAttributeData.__GetCustomAttributes(mi, typeofHideFromJavaAttribute, false); - if (attr.Count == 1) + else { - IList args = attr[0].ConstructorArguments; - if (args.Count == 1) - { - return (HideFromJavaFlags)args[0].Value; - } - return HideFromJavaFlags.All; + throw new InvalidOperationException(); } -#endif - return HideFromJavaFlags.None; } +#endif -#if STATIC_COMPILER - internal static void SetImplementsAttribute(TypeBuilder typeBuilder, TypeWrapper[] ifaceWrappers) + private void LazyInitClass() { - string[] interfaces = new string[ifaceWrappers.Length]; - for (int i = 0; i < interfaces.Length; i++) - { - interfaces[i] = UnicodeUtil.EscapeInvalidSurrogates(ifaceWrappers[i].Name); - } - if (implementsAttribute == null) + lock (this) { - implementsAttribute = typeofImplementsAttribute.GetConstructor(new Type[] { JVM.Import(typeof(string[])) }); - } - typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(implementsAttribute, new object[] { interfaces })); - } + if (classObject == null) + { + // DynamicTypeWrapper should haved already had SetClassObject explicitly + Debug.Assert(!IsDynamic); +#if !FIRST_PASS + java.lang.Class clazz; + // note that this has to be the same check as in EmitClassLiteral + if (!this.IsFastClassLiteralSafe) + { + if (this.IsPrimitive) + { + clazz = GetPrimitiveClass(); + } + else + { + clazz = new java.lang.Class(null); + } + } + else + { + Type type = GetClassLiteralType(); + if (IsForbiddenTypeParameterType(type)) + { + clazz = new java.lang.Class(type); + } + else + { + clazz = (java.lang.Class)typeof(ikvm.@internal.ClassLiteral<>).MakeGenericType(type).GetField("Value").GetValue(null); + } + } +#if __MonoCS__ + SetTypeWrapperHack(clazz, this); +#else + clazz.typeWrapper = this; #endif - - internal static bool IsGhostInterface(Type type) - { - return type.IsDefined(typeofGhostInterfaceAttribute, false); + // MONOBUG Interlocked.Exchange is broken on Mono, so we use CompareExchange + System.Threading.Interlocked.CompareExchange(ref classObject, clazz, null); +#endif + } + } } - internal static bool IsRemappedType(Type type) - { - return type.IsDefined(typeofRemappedTypeAttribute, false); - } +#if __MonoCS__ + // MONOBUG this method is to work around an mcs bug + internal static void SetTypeWrapperHack(object clazz, TypeWrapper type) + { +#if !FIRST_PASS + typeof(java.lang.Class).GetField("typeWrapper", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(clazz, type); +#endif + } +#endif + +#if !FIRST_PASS - internal static bool IsExceptionIsUnsafeForMapping(Type type) + private static void ResolvePrimitiveTypeWrapperClasses() { - return type.IsDefined(typeofExceptionIsUnsafeForMappingAttribute, false); + // note that we're evaluating all ClassObject properties for the side effect + // (to initialize and associate the ClassObject with the TypeWrapper) + if (PrimitiveTypeWrapper.BYTE.ClassObject == null + || PrimitiveTypeWrapper.CHAR.ClassObject == null + || PrimitiveTypeWrapper.DOUBLE.ClassObject == null + || PrimitiveTypeWrapper.FLOAT.ClassObject == null + || PrimitiveTypeWrapper.INT.ClassObject == null + || PrimitiveTypeWrapper.LONG.ClassObject == null + || PrimitiveTypeWrapper.SHORT.ClassObject == null + || PrimitiveTypeWrapper.BOOLEAN.ClassObject == null + || PrimitiveTypeWrapper.VOID.ClassObject == null) + { + throw new InvalidOperationException(); + } } +#endif - internal static ModifiersAttribute GetModifiersAttribute(MemberInfo member) + internal static TypeWrapper FromClass(java.lang.Class clazz) { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = member.GetCustomAttributes(typeof(ModifiersAttribute), false); - return attr.Length == 1 ? (ModifiersAttribute)attr[0] : null; +#if FIRST_PASS + throw new NotImplementedException(); #else - IList attr = CustomAttributeData.__GetCustomAttributes(member, typeofModifiersAttribute, false); - if (attr.Count == 1) + // MONOBUG redundant cast to workaround mcs bug + var tw = (TypeWrapper)(object)clazz.typeWrapper; + if (tw == null) { - IList args = attr[0].ConstructorArguments; - if (args.Count == 2) + var type = clazz.type; + if (type == null) { - return new ModifiersAttribute((Modifiers)args[0].Value, (bool)args[1].Value); + ResolvePrimitiveTypeWrapperClasses(); + return FromClass(clazz); } - return new ModifiersAttribute((Modifiers)args[0].Value); + + if (type == typeof(void) || type.IsPrimitive || ClassLoaderWrapper.IsRemappedType(type)) + tw = DotNetTypeWrapper.GetWrapperFromDotNetType(type); + else + tw = ClassLoaderWrapper.GetWrapperFromType(type); + +#if __MonoCS__ + SetTypeWrapperHack(clazz, tw); +#else + clazz.typeWrapper = tw; +#endif } - return null; + + return tw; #endif } - internal static ExModifiers GetModifiers(MethodBase mb, bool assemblyIsPrivate) +#endif // !IMPORTER && !EXPORTER + + public override string ToString() { - ModifiersAttribute attr = GetModifiersAttribute(mb); - if (attr != null) - { - return new ExModifiers(attr.Modifiers, attr.IsInternal); - } - Modifiers modifiers = 0; - if (mb.IsPublic) - { - modifiers |= Modifiers.Public; - } - else if (mb.IsPrivate) - { - modifiers |= Modifiers.Private; - } - else if (mb.IsFamily || mb.IsFamilyOrAssembly) - { - modifiers |= Modifiers.Protected; - } - else if (assemblyIsPrivate) - { - modifiers |= Modifiers.Private; - } - // NOTE Java doesn't support non-virtual methods, but we set the Final modifier for - // non-virtual methods to approximate the semantics - if ((mb.IsFinal || (!mb.IsVirtual && ((modifiers & Modifiers.Private) == 0))) && !mb.IsStatic && !mb.IsConstructor) - { - modifiers |= Modifiers.Final; - } - if (mb.IsAbstract) - { - modifiers |= Modifiers.Abstract; - } - else - { - // Some .NET interfaces (like System._AppDomain) have synchronized methods, - // Java doesn't allow synchronized on an abstract methods, so we ignore it for - // abstract methods. - if ((mb.GetMethodImplementationFlags() & MethodImplAttributes.Synchronized) != 0) - { - modifiers |= Modifiers.Synchronized; - } - } - if (mb.IsStatic) - { - modifiers |= Modifiers.Static; - } - if ((mb.Attributes & MethodAttributes.PinvokeImpl) != 0) - { - modifiers |= Modifiers.Native; - } - ParameterInfo[] parameters = mb.GetParameters(); - if (parameters.Length > 0 && parameters[parameters.Length - 1].IsDefined(JVM.Import(typeof(ParamArrayAttribute)), false)) - { - modifiers |= Modifiers.VarArgs; - } - return new ExModifiers(modifiers, false); + return GetType().Name + "[" + name + "]"; } - internal static ExModifiers GetModifiers(FieldInfo fi, bool assemblyIsPrivate) + // For UnloadableTypeWrapper it tries to load the type through the specified loader + // and if that fails it throw a NoClassDefFoundError (not a java.lang.NoClassDefFoundError), + // for all other types this is a no-op. + internal virtual TypeWrapper EnsureLoadable(ClassLoaderWrapper loader) { - ModifiersAttribute attr = GetModifiersAttribute(fi); - if (attr != null) - { - return new ExModifiers(attr.Modifiers, attr.IsInternal); - } - Modifiers modifiers = 0; - if (fi.IsPublic) - { - modifiers |= Modifiers.Public; - } - else if (fi.IsPrivate) - { - modifiers |= Modifiers.Private; - } - else if (fi.IsFamily || fi.IsFamilyOrAssembly) - { - modifiers |= Modifiers.Protected; - } - else if (assemblyIsPrivate) - { - modifiers |= Modifiers.Private; - } - if (fi.IsInitOnly || fi.IsLiteral) - { - modifiers |= Modifiers.Final; - } - if (fi.IsNotSerialized) - { - modifiers |= Modifiers.Transient; - } - if (fi.IsStatic) - { - modifiers |= Modifiers.Static; - } - if (Array.IndexOf(fi.GetRequiredCustomModifiers(), Types.IsVolatile) != -1) - { - modifiers |= Modifiers.Volatile; - } - return new ExModifiers(modifiers, false); + return this; } -#if STATIC_COMPILER - internal static void SetModifiers(MethodBuilder mb, Modifiers modifiers, bool isInternal) + private void SetTypeFlag(TypeFlags flag) { - CustomAttributeBuilder customAttributeBuilder; - if (isInternal) - { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); - } - else + // we use a global lock object, since the chance of contention is very small + lock (flagsLock) { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + flags |= flag; } - mb.SetCustomAttribute(customAttributeBuilder); } - internal static void SetModifiers(FieldBuilder fb, Modifiers modifiers, bool isInternal) + internal bool HasIncompleteInterfaceImplementation { - CustomAttributeBuilder customAttributeBuilder; - if (isInternal) - { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); - } - else + get { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); + TypeWrapper baseWrapper = this.BaseTypeWrapper; + return (flags & TypeFlags.HasIncompleteInterfaceImplementation) != 0 || (baseWrapper != null && baseWrapper.HasIncompleteInterfaceImplementation); } - fb.SetCustomAttribute(customAttributeBuilder); } - internal static void SetModifiers(PropertyBuilder pb, Modifiers modifiers, bool isInternal) + internal void SetHasIncompleteInterfaceImplementation() { - CustomAttributeBuilder customAttributeBuilder; - if (isInternal) - { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); - } - else - { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); - } - pb.SetCustomAttribute(customAttributeBuilder); + SetTypeFlag(TypeFlags.HasIncompleteInterfaceImplementation); } - internal static void SetModifiers(TypeBuilder tb, Modifiers modifiers, bool isInternal) + internal bool HasUnsupportedAbstractMethods { - CustomAttributeBuilder customAttributeBuilder; - if (isInternal) - { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers, Types.Boolean }), new object[] { modifiers, isInternal }); - } - else + get { - customAttributeBuilder = new CustomAttributeBuilder(typeofModifiersAttribute.GetConstructor(new Type[] { typeofModifiers }), new object[] { modifiers }); - } - tb.SetCustomAttribute(customAttributeBuilder); - } + foreach (var iface in this.Interfaces) + if (iface.HasUnsupportedAbstractMethods) + return true; - internal static void SetNameSig(MethodBuilder mb, string name, string sig) - { - CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(typeofNameSigAttribute.GetConstructor(new Type[] { Types.String, Types.String }), - new object[] { UnicodeUtil.EscapeInvalidSurrogates(name), UnicodeUtil.EscapeInvalidSurrogates(sig) }); - mb.SetCustomAttribute(customAttributeBuilder); + var baseWrapper = this.BaseTypeWrapper; + return (flags & TypeFlags.HasUnsupportedAbstractMethods) != 0 || (baseWrapper != null && baseWrapper.HasUnsupportedAbstractMethods); + } } - internal static void SetInnerClass(TypeBuilder typeBuilder, string innerClass, Modifiers modifiers) + internal void SetHasUnsupportedAbstractMethods() { - Type[] argTypes = new Type[] { Types.String, typeofModifiers }; - object[] args = new object[] { UnicodeUtil.EscapeInvalidSurrogates(innerClass), modifiers }; - ConstructorInfo ci = typeofInnerClassAttribute.GetConstructor(argTypes); - CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(ci, args); - typeBuilder.SetCustomAttribute(customAttributeBuilder); + SetTypeFlag(TypeFlags.HasUnsupportedAbstractMethods); } - internal static void SetSourceFile(TypeBuilder typeBuilder, string filename) + internal virtual bool HasStaticInitializer { - if (sourceFileAttribute == null) + get { - sourceFileAttribute = typeofSourceFileAttribute.GetConstructor(new Type[] { Types.String }); + return (flags & TypeFlags.HasStaticInitializer) != 0; } - typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(sourceFileAttribute, new object[] { filename })); } - internal static void SetSourceFile(ModuleBuilder moduleBuilder, string filename) + internal void SetHasStaticInitializer() { - if (sourceFileAttribute == null) - { - sourceFileAttribute = typeofSourceFileAttribute.GetConstructor(new Type[] { Types.String }); - } - moduleBuilder.SetCustomAttribute(new CustomAttributeBuilder(sourceFileAttribute, new object[] { filename })); + SetTypeFlag(TypeFlags.HasStaticInitializer); } - internal static void SetLineNumberTable(MethodBuilder mb, IKVM.Attributes.LineNumberTableAttribute.LineNumberWriter writer) + internal bool HasVerifyError { - object arg; - ConstructorInfo con; - if (writer.Count == 1) - { - if (lineNumberTableAttribute2 == null) - { - lineNumberTableAttribute2 = typeofLineNumberTableAttribute.GetConstructor(new Type[] { Types.UInt16 }); - } - con = lineNumberTableAttribute2; - arg = (ushort)writer.LineNo; - } - else + get { - if (lineNumberTableAttribute1 == null) - { - lineNumberTableAttribute1 = typeofLineNumberTableAttribute.GetConstructor(new Type[] { JVM.Import(typeof(byte[])) }); - } - con = lineNumberTableAttribute1; - arg = writer.ToArray(); + return (flags & TypeFlags.VerifyError) != 0; } - mb.SetCustomAttribute(new CustomAttributeBuilder(con, new object[] { arg })); } - internal static void SetEnclosingMethodAttribute(TypeBuilder tb, string className, string methodName, string methodSig) + internal void SetHasVerifyError() { - if (enclosingMethodAttribute == null) - { - enclosingMethodAttribute = typeofEnclosingMethodAttribute.GetConstructor(new Type[] { Types.String, Types.String, Types.String }); - } - tb.SetCustomAttribute(new CustomAttributeBuilder(enclosingMethodAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(className), UnicodeUtil.EscapeInvalidSurrogates(methodName), UnicodeUtil.EscapeInvalidSurrogates(methodSig) })); + SetTypeFlag(TypeFlags.VerifyError); } - internal static void SetSignatureAttribute(TypeBuilder tb, string signature) + internal bool HasClassFormatError { - if (signatureAttribute == null) + get { - signatureAttribute = typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); + return (flags & TypeFlags.ClassFormatError) != 0; } - tb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); } - internal static void SetSignatureAttribute(FieldBuilder fb, string signature) + internal void SetHasClassFormatError() { - if (signatureAttribute == null) - { - signatureAttribute = typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); - } - fb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); + SetTypeFlag(TypeFlags.ClassFormatError); } - internal static void SetSignatureAttribute(MethodBuilder mb, string signature) + internal virtual bool IsFakeTypeContainer { - if (signatureAttribute == null) + get { - signatureAttribute = typeofSignatureAttribute.GetConstructor(new Type[] { Types.String }); + return false; } - mb.SetCustomAttribute(new CustomAttributeBuilder(signatureAttribute, - new object[] { UnicodeUtil.EscapeInvalidSurrogates(signature) })); } - internal static void SetMethodParametersAttribute(MethodBuilder mb, Modifiers[] modifiers) + internal virtual bool IsFakeNestedType { - if (methodParametersAttribute == null) + get { - methodParametersAttribute = typeofMethodParametersAttribute.GetConstructor(new Type[] { typeofModifiers.MakeArrayType() }); + return false; } - mb.SetCustomAttribute(new CustomAttributeBuilder(methodParametersAttribute, new object[] { modifiers })); } - internal static void SetRuntimeVisibleTypeAnnotationsAttribute(TypeBuilder tb, byte[] data) + // is this an anonymous class (in the sense of Unsafe.defineAnonymousClass(), not the JLS) + internal bool IsUnsafeAnonymous { - if (runtimeVisibleTypeAnnotationsAttribute == null) - { - runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); - } - tb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data })); + get { return (flags & TypeFlags.Anonymous) != 0; } } - internal static void SetRuntimeVisibleTypeAnnotationsAttribute(FieldBuilder fb, byte[] data) + // a ghost is an interface that appears to be implemented by a .NET type + // (e.g. System.String (aka java.lang.String) appears to implement java.lang.CharSequence, + // so java.lang.CharSequence is a ghost) + internal virtual bool IsGhost { - if (runtimeVisibleTypeAnnotationsAttribute == null) + get { - runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); + return false; } - fb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data })); } - internal static void SetRuntimeVisibleTypeAnnotationsAttribute(MethodBuilder mb, byte[] data) + // is this an array type of which the ultimate element type is a ghost? + internal bool IsGhostArray { - if (runtimeVisibleTypeAnnotationsAttribute == null) + get { - runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() }); + return !IsUnloadable && IsArray && (ElementTypeWrapper.IsGhost || ElementTypeWrapper.IsGhostArray); } - mb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data })); } - internal static void SetConstantPoolAttribute(TypeBuilder tb, object[] constantPool) + internal virtual FieldInfo GhostRefField { - if (constantPoolAttribute == null) + get { - constantPoolAttribute = typeofConstantPoolAttribute.GetConstructor(new Type[] { Types.Object.MakeArrayType() }); + throw new InvalidOperationException(); } - tb.SetCustomAttribute(new CustomAttributeBuilder(constantPoolAttribute, new object[] { constantPool })); } - internal static void SetParamArrayAttribute(ParameterBuilder pb) + internal virtual bool IsRemapped { - if (paramArrayAttribute == null) + get { - paramArrayAttribute = new CustomAttributeBuilder(JVM.Import(typeof(ParamArrayAttribute)).GetConstructor(Type.EmptyTypes), new object[0]); + return false; } - pb.SetCustomAttribute(paramArrayAttribute); } -#endif // STATIC_COMPILER - internal static NameSigAttribute GetNameSig(MemberInfo member) + internal bool IsArray { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = member.GetCustomAttributes(typeof(NameSigAttribute), false); - return attr.Length == 1 ? (NameSigAttribute)attr[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofNameSigAttribute, false)) + get { - IList args = cad.ConstructorArguments; - return new NameSigAttribute((string)args[0].Value, (string)args[1].Value); + return name != null && name[0] == '['; } - return null; -#endif } - internal static T[] DecodeArray(CustomAttributeTypedArgument arg) + // NOTE for non-array types this returns 0 + internal int ArrayRank { - IList elems = (IList)arg.Value; - T[] arr = new T[elems.Count]; - for (int i = 0; i < arr.Length; i++) + get { - arr[i] = (T)elems[i].Value; + int i = 0; + if (name != null) + { + while (name[i] == '[') + { + i++; + } + } + return i; } - return arr; } - internal static ImplementsAttribute GetImplements(Type type) + internal virtual TypeWrapper GetUltimateElementTypeWrapper() { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = type.GetCustomAttributes(typeof(ImplementsAttribute), false); - return attribs.Length == 1 ? (ImplementsAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofImplementsAttribute, false)) - { - IList args = cad.ConstructorArguments; - return new ImplementsAttribute(DecodeArray(args[0])); - } - return null; -#endif + throw new InvalidOperationException(); } - internal static ThrowsAttribute GetThrows(MethodBase mb) + internal bool IsNonPrimitiveValueType { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = mb.GetCustomAttributes(typeof(ThrowsAttribute), false); - return attribs.Length == 1 ? (ThrowsAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(mb, typeofThrowsAttribute, false)) + get { - IList args = cad.ConstructorArguments; - if (args[0].ArgumentType == Types.String.MakeArrayType()) - { - return new ThrowsAttribute(DecodeArray(args[0])); - } - else if (args[0].ArgumentType == Types.Type.MakeArrayType()) - { - return new ThrowsAttribute(DecodeArray(args[0])); - } - else - { - return new ThrowsAttribute((Type)args[0].Value); - } + return this != VerifierTypeWrapper.Null && !IsPrimitive && !IsGhost && TypeAsTBD.IsValueType; } - return null; -#endif } - internal static string[] GetNonNestedInnerClasses(Type t) + internal bool IsPrimitive { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = t.GetCustomAttributes(typeof(NonNestedInnerClassAttribute), false); - string[] classes = new string[attribs.Length]; - for (int i = 0; i < attribs.Length; i++) + get { - classes[i] = ((NonNestedInnerClassAttribute)attribs[i]).InnerClassName; + return name == null; } - return classes; -#else - List list = new List(); - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(t, typeofNonNestedInnerClassAttribute, false)) + } + + internal bool IsWidePrimitive + { + get { - IList args = cad.ConstructorArguments; - list.Add(UnicodeUtil.UnescapeInvalidSurrogates((string)args[0].Value)); + return this == PrimitiveTypeWrapper.LONG || this == PrimitiveTypeWrapper.DOUBLE; } - return list.ToArray(); -#endif } - internal static string GetNonNestedOuterClasses(Type t) + internal bool IsIntOnStackPrimitive { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = t.GetCustomAttributes(typeof(NonNestedOuterClassAttribute), false); - return attribs.Length == 1 ? ((NonNestedOuterClassAttribute)attribs[0]).OuterClassName : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(t, typeofNonNestedOuterClassAttribute, false)) + get { - IList args = cad.ConstructorArguments; - return UnicodeUtil.UnescapeInvalidSurrogates((string)args[0].Value); + return name == null && + (this == PrimitiveTypeWrapper.BOOLEAN || + this == PrimitiveTypeWrapper.BYTE || + this == PrimitiveTypeWrapper.CHAR || + this == PrimitiveTypeWrapper.SHORT || + this == PrimitiveTypeWrapper.INT); } - return null; -#endif } - internal static SignatureAttribute GetSignature(MemberInfo member) + private static bool IsJavaPrimitive(Type type) { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = member.GetCustomAttributes(typeof(SignatureAttribute), false); - return attribs.Length == 1 ? (SignatureAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofSignatureAttribute, false)) + return type == PrimitiveTypeWrapper.BOOLEAN.TypeAsTBD + || type == PrimitiveTypeWrapper.BYTE.TypeAsTBD + || type == PrimitiveTypeWrapper.CHAR.TypeAsTBD + || type == PrimitiveTypeWrapper.DOUBLE.TypeAsTBD + || type == PrimitiveTypeWrapper.FLOAT.TypeAsTBD + || type == PrimitiveTypeWrapper.INT.TypeAsTBD + || type == PrimitiveTypeWrapper.LONG.TypeAsTBD + || type == PrimitiveTypeWrapper.SHORT.TypeAsTBD + || type == PrimitiveTypeWrapper.VOID.TypeAsTBD; + } + + internal bool IsBoxedPrimitive + { + get { - IList args = cad.ConstructorArguments; - return new SignatureAttribute((string)args[0].Value); + return !IsPrimitive && IsJavaPrimitive(TypeAsSignatureType); } - return null; -#endif } - internal static MethodParametersAttribute GetMethodParameters(MethodBase method) + internal bool IsErasedOrBoxedPrimitiveOrRemapped { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = method.GetCustomAttributes(typeof(MethodParametersAttribute), false); - return attribs.Length == 1 ? (MethodParametersAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(method, typeofMethodParametersAttribute, false)) + get { - IList args = cad.ConstructorArguments; - return new MethodParametersAttribute(DecodeArray(args[0])); + bool erased = IsUnloadable || IsGhostArray; + return erased || IsBoxedPrimitive || (IsRemapped && this is DotNetTypeWrapper); } - return null; -#endif } - internal static object[] GetConstantPool(Type type) + internal bool IsUnloadable { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = type.GetCustomAttributes(typeof(ConstantPoolAttribute), false); - return attribs.Length == 1 ? ((ConstantPoolAttribute)attribs[0]).constantPool : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofConstantPoolAttribute, false)) + get { - return ConstantPoolAttribute.Decompress(DecodeArray(cad.ConstructorArguments[0])); + // NOTE we abuse modifiers to note unloadable classes + return modifiers == UnloadableModifiersHack; } - return null; -#endif } - internal static byte[] GetRuntimeVisibleTypeAnnotations(MemberInfo member) + internal bool IsVerifierType { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = member.GetCustomAttributes(typeof(RuntimeVisibleTypeAnnotationsAttribute), false); - return attribs.Length == 1 ? ((RuntimeVisibleTypeAnnotationsAttribute)attribs[0]).data : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofRuntimeVisibleTypeAnnotationsAttribute, false)) + get { - return DecodeArray(cad.ConstructorArguments[0]); + // NOTE we abuse modifiers to note verifier types + return modifiers == VerifierTypeModifiersHack; } - return null; -#endif } - internal static InnerClassAttribute GetInnerClass(Type type) + internal virtual bool IsMapUnsafeException { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = type.GetCustomAttributes(typeof(InnerClassAttribute), false); - return attribs.Length == 1 ? (InnerClassAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofInnerClassAttribute, false)) + get { - IList args = cad.ConstructorArguments; - return new InnerClassAttribute((string)args[0].Value, (Modifiers)args[1].Value); + return false; } - return null; -#endif } - internal static RemappedInterfaceMethodAttribute[] GetRemappedInterfaceMethods(Type type) + internal Modifiers Modifiers { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = type.GetCustomAttributes(typeof(RemappedInterfaceMethodAttribute), false); - RemappedInterfaceMethodAttribute[] attr1 = new RemappedInterfaceMethodAttribute[attr.Length]; - Array.Copy(attr, attr1, attr.Length); - return attr1; -#else - List attrs = new List(); - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofRemappedInterfaceMethodAttribute, false)) + get { - IList args = cad.ConstructorArguments; - attrs.Add(new RemappedInterfaceMethodAttribute((string)args[0].Value, (string)args[1].Value, DecodeArray(args[2]))); + return modifiers; } - return attrs.ToArray(); -#endif } - internal static RemappedTypeAttribute GetRemappedType(Type type) + // since for inner classes, the modifiers returned by Class.getModifiers are different from the actual + // modifiers (as used by the VM access control mechanism), we have this additional property + internal virtual Modifiers ReflectiveModifiers { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attribs = type.GetCustomAttributes(typeof(RemappedTypeAttribute), false); - return attribs.Length == 1 ? (RemappedTypeAttribute)attribs[0] : null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofRemappedTypeAttribute, false)) + get { - IList args = cad.ConstructorArguments; - return new RemappedTypeAttribute((Type)args[0].Value); + return modifiers; } - return null; -#endif } - internal static RemappedClassAttribute[] GetRemappedClasses(Assembly coreAssembly) + internal bool IsInternal { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = coreAssembly.GetCustomAttributes(typeof(RemappedClassAttribute), false); - RemappedClassAttribute[] attr1 = new RemappedClassAttribute[attr.Length]; - Array.Copy(attr, attr1, attr.Length); - return attr1; -#else - List attrs = new List(); - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(coreAssembly, typeofRemappedClassAttribute, false)) + get { - IList args = cad.ConstructorArguments; - attrs.Add(new RemappedClassAttribute((string)args[0].Value, (Type)args[1].Value)); + return (flags & TypeFlags.InternalAccess) != 0; } - return attrs.ToArray(); -#endif } - internal static string GetAnnotationAttributeType(Type type) + internal bool IsPublic { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = type.GetCustomAttributes(typeof(AnnotationAttributeAttribute), false); - if (attr.Length == 1) + get { - return ((AnnotationAttributeAttribute)attr[0]).AttributeType; + return (modifiers & Modifiers.Public) != 0; } - return null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofAnnotationAttributeAttribute, false)) + } + + internal bool IsAbstract + { + get { - return UnicodeUtil.UnescapeInvalidSurrogates((string)cad.ConstructorArguments[0].Value); + // interfaces don't need to marked abstract explicitly (and javac 1.1 didn't do it) + return (modifiers & (Modifiers.Abstract | Modifiers.Interface)) != 0; } - return null; -#endif } - internal static AssemblyName[] GetInternalsVisibleToAttributes(Assembly assembly) + internal bool IsFinal { - List list = new List(); - foreach (CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(assembly)) + get { - if (cad.Constructor.DeclaringType == JVM.Import(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute))) - { - try - { - list.Add(new AssemblyName((string)cad.ConstructorArguments[0].Value)); - } - catch - { - // HACK since there is no list of exception that the AssemblyName constructor can throw, we simply catch all - } - } + return (modifiers & Modifiers.Final) != 0; } - return list.ToArray(); } - internal static bool IsJavaModule(Module mod) + internal bool IsInterface { - return mod.IsDefined(typeofJavaModuleAttribute, false); + get + { + Debug.Assert(!IsUnloadable && !IsVerifierType); + return (modifiers & Modifiers.Interface) != 0; + } } - internal static object[] GetJavaModuleAttributes(Module mod) + // this exists because interfaces and arrays of interfaces are treated specially + // by the verifier, interfaces don't have a common base (other than java.lang.Object) + // so any object reference or object array reference can be used where an interface + // or interface array reference is expected (the compiler will insert the required casts). + internal bool IsInterfaceOrInterfaceArray { -#if !STATIC_COMPILER && !STUB_GENERATOR - return mod.GetCustomAttributes(typeofJavaModuleAttribute, false); -#else - List attrs = new List(); - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(mod, typeofJavaModuleAttribute, false)) + get { - IList args = cad.ConstructorArguments; - if (args.Count == 0) - { - attrs.Add(new JavaModuleAttribute()); - } - else + TypeWrapper tw = this; + while (tw.IsArray) { - attrs.Add(new JavaModuleAttribute(DecodeArray(args[0]))); + tw = tw.ElementTypeWrapper; } + return tw.IsInterface; } - return attrs.ToArray(); -#endif } - internal static bool IsNoPackagePrefix(Type type) - { - return type.IsDefined(typeofNoPackagePrefixAttribute, false) || type.Assembly.IsDefined(typeofNoPackagePrefixAttribute, false); - } + internal abstract ClassLoaderWrapper GetClassLoader(); - internal static bool HasEnclosingMethodAttribute(Type type) + /// + /// Searches for the with the specified name and signature. + /// + /// + /// + /// + internal FieldWrapper GetFieldWrapper(string fieldName, string fieldSig) { - return type.IsDefined(typeofEnclosingMethodAttribute, false); - } + foreach (var fw in GetFields()) + if (fw.Name == fieldName && fw.Signature == fieldSig) + return fw; - internal static EnclosingMethodAttribute GetEnclosingMethodAttribute(Type type) - { -#if !STATIC_COMPILER && !STUB_GENERATOR - object[] attr = type.GetCustomAttributes(typeof(EnclosingMethodAttribute), false); - if (attr.Length == 1) - { - return ((EnclosingMethodAttribute)attr[0]).SetClassName(type); - } - return null; -#else - foreach (CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofEnclosingMethodAttribute, false)) + foreach (var iface in Interfaces) { - return new EnclosingMethodAttribute((string)cad.ConstructorArguments[0].Value, (string)cad.ConstructorArguments[1].Value, (string)cad.ConstructorArguments[2].Value).SetClassName(type); + var fw = iface.GetFieldWrapper(fieldName, fieldSig); + if (fw != null) + return fw; } - return null; -#endif - } -#if STATIC_COMPILER - internal static void SetRemappedClass(AssemblyBuilder assemblyBuilder, string name, Type shadowType) - { - ConstructorInfo remappedClassAttribute = typeofRemappedClassAttribute.GetConstructor(new Type[] { Types.String, Types.Type }); - assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(remappedClassAttribute, new object[] { name, shadowType })); - } + var baseWrapper = BaseTypeWrapper; + if (baseWrapper != null) + return baseWrapper.GetFieldWrapper(fieldName, fieldSig); - internal static void SetRemappedType(TypeBuilder typeBuilder, Type shadowType) - { - ConstructorInfo remappedTypeAttribute = typeofRemappedTypeAttribute.GetConstructor(new Type[] { Types.Type }); - typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(remappedTypeAttribute, new object[] { shadowType })); + return null; } - internal static void SetRemappedInterfaceMethod(TypeBuilder typeBuilder, string name, string mappedTo, string[] throws) + protected virtual void LazyPublishMembers() { - CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofRemappedInterfaceMethodAttribute.GetConstructor(new Type[] { Types.String, Types.String, Types.String.MakeArrayType() }), new object[] { name, mappedTo, throws }); - typeBuilder.SetCustomAttribute(cab); + methods ??= MethodWrapper.EmptyArray; + fields ??= FieldWrapper.EmptyArray; } - internal static void SetExceptionIsUnsafeForMapping(TypeBuilder typeBuilder) + protected virtual void LazyPublishMethods() { - CustomAttributeBuilder cab = new CustomAttributeBuilder(typeofExceptionIsUnsafeForMappingAttribute.GetConstructor(Type.EmptyTypes), new object[0]); - typeBuilder.SetCustomAttribute(cab); + LazyPublishMembers(); } -#endif // STATIC_COMPILER - internal static void SetRuntimeCompatibilityAttribute(AssemblyBuilder assemblyBuilder) + protected virtual void LazyPublishFields() { - Type runtimeCompatibilityAttribute = JVM.Import(typeof(System.Runtime.CompilerServices.RuntimeCompatibilityAttribute)); - assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder( - runtimeCompatibilityAttribute.GetConstructor(Type.EmptyTypes), new object[0], - new PropertyInfo[] { runtimeCompatibilityAttribute.GetProperty("WrapNonExceptionThrows") }, new object[] { true }, - new FieldInfo[0], new object[0])); + LazyPublishMembers(); } - internal static void SetInternalsVisibleToAttribute(AssemblyBuilder assemblyBuilder, string assemblyName) + /// + /// Gets the set of methods defined by this type. + /// + /// + internal MethodWrapper[] GetMethods() { - Type internalsVisibleToAttribute = JVM.Import(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute)); - CustomAttributeBuilder cab = new CustomAttributeBuilder( - internalsVisibleToAttribute.GetConstructor(new Type[] { Types.String }), new object[] { assemblyName }); - assemblyBuilder.SetCustomAttribute(cab); + if (methods == null) + { + lock (this) + { + if (methods == null) + { +#if IMPORTER + if (IsUnloadable || !CheckMissingBaseTypes(TypeAsBaseType)) + return methods = MethodWrapper.EmptyArray; +#endif + LazyPublishMethods(); + } + } + } + + return methods; } - } - static class EnumHelper - { - internal static Type GetUnderlyingType(Type enumType) + /// + /// Gets the set of fields declared by this type. + /// + /// + internal FieldWrapper[] GetFields() { -#if STATIC_COMPILER || STUB_GENERATOR - return enumType.GetEnumUnderlyingType(); -#else - return Enum.GetUnderlyingType(enumType); + if (fields == null) + { + lock (this) + { + if (fields == null) + { +#if IMPORTER + if (IsUnloadable || !CheckMissingBaseTypes(TypeAsBaseType)) + return fields = FieldWrapper.EmptyArray; #endif + + LazyPublishFields(); + } + } + } + + return fields; } -#if STATIC_COMPILER - internal static object Parse(Type type, string value) +#if IMPORTER + private static bool CheckMissingBaseTypes(Type type) { - object retval = null; - foreach (string str in value.Split(',')) + while (type != null) { - FieldInfo field = type.GetField(str.Trim(), BindingFlags.Public | BindingFlags.Static); - if (field == null) + if (type.__ContainsMissingType) { - throw new InvalidOperationException("Enum value '" + str + "' not found in " + type.FullName); + StaticCompiler.IssueMissingTypeMessage(type); + return false; } - if (retval == null) + bool ok = true; + foreach (Type iface in type.__GetDeclaredInterfaces()) { - retval = field.GetRawConstantValue(); + ok &= CheckMissingBaseTypes(iface); } - else + if (!ok) { - retval = OrBoxedIntegrals(retval, field.GetRawConstantValue()); + return false; } + type = type.BaseType; } - return retval; + return true; } #endif - // note that we only support the integer types that C# supports - // (the CLI also supports bool, char, IntPtr & UIntPtr) - internal static object OrBoxedIntegrals(object v1, object v2) + internal MethodWrapper GetMethodWrapper(string name, string sig, bool inherit) { - Debug.Assert(v1.GetType() == v2.GetType()); - if (v1 is ulong) - { - ulong l1 = (ulong)v1; - ulong l2 = (ulong)v2; - return l1 | l2; - } - else + // we need to get the methods before calling string.IsInterned, because getting them might cause the strings to be interned + var methods = GetMethods(); + + var _name = string.IsInterned(name); + var _sig = string.IsInterned(sig); + foreach (var mw in methods) { - long v = ((IConvertible)v1).ToInt64(null) | ((IConvertible)v2).ToInt64(null); - switch (Type.GetTypeCode(JVM.Import(v1.GetType()))) - { - case TypeCode.SByte: - return (sbyte)v; - case TypeCode.Byte: - return (byte)v; - case TypeCode.Int16: - return (short)v; - case TypeCode.UInt16: - return (ushort)v; - case TypeCode.Int32: - return (int)v; - case TypeCode.UInt32: - return (uint)v; - case TypeCode.Int64: - return (long)v; - default: - throw new InvalidOperationException(); - } + // NOTE we can use ref equality, because names and signatures are always interned by MemberWrapper + if (ReferenceEquals(mw.Name, _name) && ReferenceEquals(mw.Signature, _sig)) + return mw; } + + var baseWrapper = BaseTypeWrapper; + if (inherit && baseWrapper != null) + return baseWrapper.GetMethodWrapper(name, sig, inherit); + + return null; } - // this method can be used to convert an enum value or its underlying value to a Java primitive - internal static object GetPrimitiveValue(Type underlyingType, object obj) + internal MethodWrapper GetInterfaceMethod(string name, string sig) { - // Note that this method doesn't trust that obj is of the correct type, - // because it turns out there exist assemblies (e.g. gtk-sharp.dll) that - // have incorrectly typed enum constant values (e.g. int32 instead of uint32). - long value; - if (obj is ulong || (obj is Enum && underlyingType == Types.UInt64)) + MethodWrapper method = GetMethodWrapper(name, sig, false); + if (method != null) { - value = unchecked((long)((IConvertible)obj).ToUInt64(null)); + return method; } - else - { - value = ((IConvertible)obj).ToInt64(null); - } - if (underlyingType == Types.SByte || underlyingType == Types.Byte) - { - return unchecked((byte)value); - } - else if (underlyingType == Types.Int16 || underlyingType == Types.UInt16) - { - return unchecked((short)value); - } - else if (underlyingType == Types.Int32 || underlyingType == Types.UInt32) - { - return unchecked((int)value); - } - else if (underlyingType == Types.Int64 || underlyingType == Types.UInt64) - { - return value; - } - else - { - throw new InvalidOperationException(); - } - } - } - - static class TypeNameUtil - { - // note that MangleNestedTypeName() assumes that there are less than 16 special characters - private const string specialCharactersString = "\\+,[]*&\u0000"; - internal const string ProxiesContainer = "__"; - - internal static string ReplaceIllegalCharacters(string name) - { - name = UnicodeUtil.EscapeInvalidSurrogates(name); - // only the NUL character is illegal in CLR type names, so we replace it with a space - return name.Replace('\u0000', ' '); - } - - internal static string Unescape(string name) - { - int pos = name.IndexOf('\\'); - if (pos == -1) - { - return name; - } - System.Text.StringBuilder sb = new System.Text.StringBuilder(name.Length); - sb.Append(name, 0, pos); - for (int i = pos; i < name.Length; i++) - { - char c = name[i]; - if (c == '\\') - { - c = name[++i]; - } - sb.Append(c); - } - return sb.ToString(); - } - - internal static string MangleNestedTypeName(string name) - { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - foreach (char c in name) + TypeWrapper[] interfaces = Interfaces; + for (int i = 0; i < interfaces.Length; i++) { - int index = specialCharactersString.IndexOf(c); - if (c == '.') - { - sb.Append("_"); - } - else if (c == '_') - { - sb.Append("^-"); - } - else if (index == -1) - { - sb.Append(c); - if (c == '^') - { - sb.Append(c); - } - } - else + method = interfaces[i].GetInterfaceMethod(name, sig); + if (method != null) { - sb.Append('^').AppendFormat("{0:X1}", index); + return method; } } - return sb.ToString(); + return null; } - internal static string UnmangleNestedTypeName(string name) + internal void SetMethods(MethodWrapper[] methods) { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - for (int i = 0; i < name.Length; i++) - { - char c = name[i]; - int index = specialCharactersString.IndexOf(c); - if (c == '_') - { - sb.Append('.'); - } - else if (c == '^') - { - c = name[++i]; - if (c == '-') - { - sb.Append('_'); - } - else if (c == '^') - { - sb.Append('^'); - } - else - { - sb.Append(specialCharactersString[c - '0']); - } - } - else - { - sb.Append(c); - } - } - return sb.ToString(); + Debug.Assert(methods != null); + System.Threading.Thread.MemoryBarrier(); + this.methods = methods; } - internal static string GetProxyNestedName(TypeWrapper[] interfaces) + internal void SetFields(FieldWrapper[] fields) { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - foreach (TypeWrapper tw in interfaces) - { - sb.Append(tw.Name.Length).Append('|').Append(tw.Name); - } - return TypeNameUtil.MangleNestedTypeName(sb.ToString()); + Debug.Assert(fields != null); + System.Threading.Thread.MemoryBarrier(); + this.fields = fields; } - internal static string GetProxyName(TypeWrapper[] interfaces) - { - return ProxiesContainer + "+" + GetProxyNestedName(interfaces); - } - } + internal string Name => name; - static class ArrayUtil - { - internal static T[] Concat(X obj, T[] arr) - where X : T - { - T[] narr = new T[arr.Length + 1]; - narr[0] = obj; - Array.Copy(arr, 0, narr, 1, arr.Length); - return narr; - } + /// + /// The name of the type as it appears in a Java signature string (e.g. "Ljava.lang.Object;" or "I"). + /// + internal virtual string SigName => "L" + Name + ";"; - internal static T[] Concat(T[] arr, X obj) - where X : T + // returns true iff wrapper is allowed to access us + internal bool IsAccessibleFrom(TypeWrapper wrapper) { - Array.Resize(ref arr, arr.Length + 1); - arr[arr.Length - 1] = obj; - return arr; + return IsPublic + || (IsInternal && InternalsVisibleTo(wrapper)) + || IsPackageAccessibleFrom(wrapper); } - internal static T[] DropFirst(T[] arr) + internal bool InternalsVisibleTo(TypeWrapper wrapper) { - T[] narr = new T[arr.Length - 1]; - Array.Copy(arr, 1, narr, 0, narr.Length); - return narr; + return GetClassLoader().InternalsVisibleToImpl(this, wrapper); } - } - static class UnicodeUtil - { - // We use part of the Supplementary Private Use Area-B to encode - // invalid surrogates. If we encounter either of these two - // markers, we always encode the surrogate (single or pair) - private const char HighSurrogatePrefix = '\uDBFF'; - private const char LowSurrogatePrefix = '\uDBFE'; - - // Identifiers in ECMA CLI metadata and strings in custom attribute blobs are encoded - // using UTF-8 and don't allow partial surrogates, so we have to "complete" them to - // produce valid Unicode and reverse the process when we read back the names. - internal static string EscapeInvalidSurrogates(string str) + internal virtual bool IsPackageAccessibleFrom(TypeWrapper wrapper) { - if (str != null) + if (MatchingPackageNames(name, wrapper.name)) { - for (int i = 0; i < str.Length; i++) +#if IMPORTER + CompilerClassLoader ccl = GetClassLoader() as CompilerClassLoader; + if (ccl != null) { - char c = str[i]; - if (Char.IsLowSurrogate(c)) - { - str = str.Substring(0, i) + LowSurrogatePrefix + c + str.Substring(i + 1); - i++; - } - else if (Char.IsHighSurrogate(c)) - { - i++; - // always escape the markers - if (c == HighSurrogatePrefix || c == LowSurrogatePrefix || i == str.Length || !Char.IsLowSurrogate(str[i])) - { - str = str.Substring(0, i - 1) + HighSurrogatePrefix + (char)(c + 0x400) + str.Substring(i); - } - } + // this is a hack for multi target -sharedclassloader compilation + // (during compilation we have multiple CompilerClassLoader instances to represent the single shared runtime class loader) + return ccl.IsEquivalentTo(wrapper.GetClassLoader()); } +#endif + return GetClassLoader() == wrapper.GetClassLoader(); } - return str; - } - - internal static string UnescapeInvalidSurrogates(string str) - { - if (str != null) + else { - for (int i = 0; i < str.Length; i++) - { - switch (str[i]) - { - case HighSurrogatePrefix: - str = str.Substring(0, i) + (char)(str[i + 1] - 0x400) + str.Substring(i + 2); - break; - case LowSurrogatePrefix: - str = str.Substring(0, i) + str[i + 1] + str.Substring(i + 2); - break; - } - } + return false; } - return str; } - internal static string[] EscapeInvalidSurrogates(string[] str) + private static bool MatchingPackageNames(string name1, string name2) { - if (str != null) + int index1 = name1.LastIndexOf('.'); + int index2 = name2.LastIndexOf('.'); + if (index1 == -1 && index2 == -1) { - for (int i = 0; i < str.Length; i++) - { - str[i] = EscapeInvalidSurrogates(str[i]); - } + return true; } - return str; - } - - internal static string[] UnescapeInvalidSurrogates(string[] str) - { - if (str != null) + // for array types we need to skip the brackets + int skip1 = 0; + int skip2 = 0; + while (name1[skip1] == '[') { - for (int i = 0; i < str.Length; i++) - { - str[i] = UnescapeInvalidSurrogates(str[i]); - } + skip1++; + } + while (name2[skip2] == '[') + { + skip2++; + } + if (skip1 > 0) + { + // skip over the L that follows the brackets + skip1++; + } + if (skip2 > 0) + { + // skip over the L that follows the brackets + skip2++; + } + if ((index1 - skip1) != (index2 - skip2)) + { + return false; } - return str; + return String.CompareOrdinal(name1, skip1, name2, skip2, index1 - skip1) == 0; } - } - - abstract class Annotation - { -#if STATIC_COMPILER - - internal static Annotation LoadAssemblyCustomAttribute(ClassLoaderWrapper loader, object[] def) + internal abstract Type TypeAsTBD { - if (def.Length == 0) - throw new ArgumentException("LoadAssemblyCustomAttribute did not receive any definitions."); - if (object.Equals(def[0], AnnotationDefaultAttribute.TAG_ANNOTATION) == false) - throw new InternalException("LoadAssemblyCustomAttribute did not receive AnnotationDefaultAttribute.TAG_ANNOTATION."); + get; + } - string annotationClass = (string)def[1]; - if (ClassFile.IsValidFieldSig(annotationClass)) + internal Type TypeAsSignatureType + { + get { - try + if (IsUnloadable) { - return loader.RetTypeWrapperFromSig(annotationClass.Replace('/', '.'), LoadMode.LoadOrThrow).Annotation; + return ((UnloadableTypeWrapper)this).MissingType ?? Types.Object; } - catch (RetargetableJavaException) + if (IsGhostArray) { - + return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); } + return TypeAsTBD; } - return null; } -#endif - -#if !STUB_GENERATOR - // NOTE this method returns null if the type could not be found - // or if the type is not a Custom Attribute and we're not in the static compiler - internal static Annotation Load(TypeWrapper owner, object[] def) + internal Type TypeAsPublicSignatureType { - Debug.Assert(def[0].Equals(AnnotationDefaultAttribute.TAG_ANNOTATION)); - string annotationClass = (string)def[1]; -#if !STATIC_COMPILER - if (!annotationClass.EndsWith("$Annotation;") - && !annotationClass.EndsWith("$Annotation$__ReturnValue;") - && !annotationClass.EndsWith("$Annotation$__Multiple;")) - { - // we don't want to try to load an annotation in dynamic mode, - // unless it is a .NET custom attribute (which can affect runtime behavior) - return null; - } -#endif - if (ClassFile.IsValidFieldSig(annotationClass)) + get { - TypeWrapper tw = owner.GetClassLoader().RetTypeWrapperFromSig(annotationClass.Replace('/', '.'), LoadMode.Link); - // Java allows inaccessible annotations to be used, so when the annotation isn't visible - // we fall back to using the DynamicAnnotationAttribute. - if (!tw.IsUnloadable && tw.IsAccessibleFrom(owner)) - { - return tw.Annotation; - } + return (IsPublic ? this : GetPublicBaseTypeWrapper()).TypeAsSignatureType; } - Tracer.Warning(Tracer.Compiler, "Unable to load annotation class {0}", annotationClass); -#if STATIC_COMPILER - return new CompiledTypeWrapper.CompiledAnnotation(StaticCompiler.GetRuntimeType("IKVM.Attributes.DynamicAnnotationAttribute")); -#else - return null; -#endif } -#endif - private static object LookupEnumValue(Type enumType, string value) + internal virtual Type TypeAsBaseType { - FieldInfo field = enumType.GetField(value, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - if (field != null) + get { - return field.GetRawConstantValue(); + return TypeAsTBD; } - // both __unspecified and missing values end up here - return EnumHelper.GetPrimitiveValue(EnumHelper.GetUnderlyingType(enumType), 0); } - protected static object ConvertValue(ClassLoaderWrapper loader, Type targetType, object obj) + internal Type TypeAsLocalOrStackType { - if (targetType.IsEnum) + get { - // TODO check the obj descriptor matches the type we expect - if (((object[])obj)[0].Equals(AnnotationDefaultAttribute.TAG_ARRAY)) + if (IsUnloadable || IsGhost) { - object[] arr = (object[])obj; - object value = null; - for (int i = 1; i < arr.Length; i++) - { - // TODO check the obj descriptor matches the type we expect - string s = ((object[])arr[i])[2].ToString(); - object newval = LookupEnumValue(targetType, s); - if (value == null) - { - value = newval; - } - else - { - value = EnumHelper.OrBoxedIntegrals(value, newval); - } - } - return value; + return Types.Object; } - else + if (IsNonPrimitiveValueType) { - string s = ((object[])obj)[2].ToString(); - if (s == "__unspecified") - { - // TODO we should probably return null and handle that - } - return LookupEnumValue(targetType, s); + // return either System.ValueType or System.Enum + return TypeAsTBD.BaseType; } - } - else if (targetType == Types.Type) - { - // TODO check the obj descriptor matches the type we expect - return loader.FieldTypeWrapperFromSig(((string)((object[])obj)[1]).Replace('/', '.'), LoadMode.LoadOrThrow).TypeAsTBD; - } - else if (targetType.IsArray) - { - // TODO check the obj descriptor matches the type we expect - object[] arr = (object[])obj; - Type elementType = targetType.GetElementType(); - object[] targetArray = new object[arr.Length - 1]; - for (int i = 1; i < arr.Length; i++) + if (IsGhostArray) { - targetArray[i - 1] = ConvertValue(loader, elementType, arr[i]); + return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); } - return targetArray; - } - else - { - return obj; + return TypeAsTBD; } } -#if !STATIC_COMPILER && !STUB_GENERATOR - internal static bool MakeDeclSecurity(Type type, object annotation, out SecurityAction action, out PermissionSet permSet) + /** Use this if the type is used as an array or array element */ + internal Type TypeAsArrayType { - ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(SecurityAction) }); - if (ci == null) - { - // TODO should we support HostProtectionAttribute? (which has a no-arg constructor) - // TODO issue message? - action = 0; - permSet = null; - return false; - } - SecurityAttribute attr = null; - object[] arr = (object[])annotation; - for (int i = 2; i < arr.Length; i += 2) + get { - string name = (string)arr[i]; - if (name == "value") + if (IsUnloadable || IsGhost) { - attr = (SecurityAttribute)ci.Invoke(new object[] { ConvertValue(null, typeof(SecurityAction), arr[i + 1]) }); + return Types.Object; } - } - if (attr == null) - { - // TODO issue message? - action = 0; - permSet = null; - return false; - } - for (int i = 2; i < arr.Length; i += 2) - { - string name = (string)arr[i]; - if (name != "value") + if (IsGhostArray) { - PropertyInfo pi = type.GetProperty(name); - pi.SetValue(attr, ConvertValue(null, pi.PropertyType, arr[i + 1]), null); + return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); } + return TypeAsTBD; } - action = attr.Action; - permSet = new PermissionSet(PermissionState.None); - permSet.AddPermission(attr.CreatePermission()); - return true; } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - internal static bool HasRetentionPolicyRuntime(object[] annotations) + internal Type TypeAsExceptionType { - if (annotations != null) + get { - foreach (object[] def in annotations) + if (IsUnloadable) { - if (def[1].Equals("Ljava/lang/annotation/Retention;")) - { - for (int i = 2; i < def.Length; i += 2) - { - if (def[i].Equals("value")) - { - object[] val = def[i + 1] as object[]; - if (val != null - && val.Length == 3 - && val[0].Equals(AnnotationDefaultAttribute.TAG_ENUM) - && val[1].Equals("Ljava/lang/annotation/RetentionPolicy;") - && val[2].Equals("RUNTIME")) - { - return true; - } - } - } - } + return Types.Exception; } + return TypeAsTBD; } - return false; } - internal static bool HasObsoleteAttribute(object[] annotations) + internal abstract TypeWrapper BaseTypeWrapper { - if (annotations != null) - { - foreach (object[] def in annotations) - { - if (def[1].Equals("Lcli/System/ObsoleteAttribute$Annotation;")) - { - return true; - } - } - } - return false; + get; } - protected static object QualifyClassNames(ClassLoaderWrapper loader, object annotation) + internal TypeWrapper ElementTypeWrapper { - bool copy = false; - object[] def = (object[])annotation; - for (int i = 3; i < def.Length; i += 2) + get { - object[] val = def[i] as object[]; - if (val != null) - { - object[] newval = ValueQualifyClassNames(loader, val); - if (newval != val) - { - if (!copy) - { - copy = true; - object[] newdef = new object[def.Length]; - Array.Copy(def, newdef, def.Length); - def = newdef; - } - def[i] = newval; - } - } - } - return def; - } - - private static object[] ValueQualifyClassNames(ClassLoaderWrapper loader, object[] val) - { - if (val[0].Equals(AnnotationDefaultAttribute.TAG_ANNOTATION)) - { - return (object[])QualifyClassNames(loader, val); - } - else if (val[0].Equals(AnnotationDefaultAttribute.TAG_CLASS)) - { - string sig = (string)val[1]; - if (sig.StartsWith("L")) - { - TypeWrapper tw = loader.LoadClassByDottedNameFast(sig.Substring(1, sig.Length - 2).Replace('/', '.')); - if (tw != null) - { - return new object[] { AnnotationDefaultAttribute.TAG_CLASS, "L" + tw.TypeAsBaseType.AssemblyQualifiedName.Replace('.', '/') + ";" }; - } - } - return val; - } - else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ENUM)) - { - string sig = (string)val[1]; - TypeWrapper tw = loader.LoadClassByDottedNameFast(sig.Substring(1, sig.Length - 2).Replace('/', '.')); - if (tw != null) - { - return new object[] { AnnotationDefaultAttribute.TAG_ENUM, "L" + tw.TypeAsBaseType.AssemblyQualifiedName.Replace('.', '/') + ";", val[2] }; - } - return val; - } - else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ARRAY)) - { - bool copy = false; - for (int i = 1; i < val.Length; i++) - { - object[] nval = val[i] as object[]; - if (nval != null) - { - object newnval = ValueQualifyClassNames(loader, nval); - if (newnval != nval) - { - if (!copy) - { - copy = true; - object[] newval = new object[val.Length]; - Array.Copy(val, newval, val.Length); - val = newval; - } - val[i] = newnval; - } - } - } - return val; - } - else if (val[0].Equals(AnnotationDefaultAttribute.TAG_ERROR)) - { - return val; - } - else - { - throw new InvalidOperationException(); - } - } - - internal abstract void Apply(ClassLoaderWrapper loader, TypeBuilder tb, object annotation); - internal abstract void Apply(ClassLoaderWrapper loader, MethodBuilder mb, object annotation); - internal abstract void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation); - internal abstract void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation); - internal abstract void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation); - internal abstract void Apply(ClassLoaderWrapper loader, PropertyBuilder pb, object annotation); - - internal virtual void ApplyReturnValue(ClassLoaderWrapper loader, MethodBuilder mb, ref ParameterBuilder pb, object annotation) - { - } - - internal abstract bool IsCustomAttribute { get; } - } - - [Flags] - enum TypeFlags : ushort - { - - None = 0, - HasIncompleteInterfaceImplementation = 1, - InternalAccess = 2, - HasStaticInitializer = 4, - VerifyError = 8, - ClassFormatError = 16, - HasUnsupportedAbstractMethods = 32, - Anonymous = 64, - Linked = 128, - - } - - static class NamePrefix - { - - internal const string Type2AccessStubBackingField = "__<>"; - internal const string AccessStub = ""; - internal const string NonVirtual = ""; - internal const string Bridge = ""; - internal const string Incomplete = ""; - internal const string DefaultMethod = ""; - internal const string PrivateInterfaceInstanceMethod = ""; - - } - - static class NestedTypeName - { - - internal const string CallerID = "__"; - internal const string InterfaceHelperMethods = "__<>IHM"; - internal const string PrivateInterfaceMethods = "__<>PIM"; - - // interop types (mangled if necessary) - internal const string Fields = "__Fields"; - internal const string Methods = "__Methods"; - internal const string DefaultMethods = "__DefaultMethods"; - - // prefixes - internal const string ThreadLocal = "___"; - internal const string AtomicReferenceFieldUpdater = "___"; - internal const string IndyCallSite = "__<>IndyCS"; - internal const string MethodHandleConstant = "__<>MHC"; - internal const string MethodTypeConstant = "__<>MTC"; - internal const string IntrinsifiedAnonymousClass = "__<>Anon"; - - } - - internal abstract class TypeWrapper - { - - private static readonly object flagsLock = new object(); - private readonly string name; // java name (e.g. java.lang.Object) - private readonly Modifiers modifiers; - private TypeFlags flags; - private MethodWrapper[] methods; - private FieldWrapper[] fields; -#if !STATIC_COMPILER && !STUB_GENERATOR - private java.lang.Class classObject; -#endif - internal static readonly TypeWrapper[] EmptyArray = new TypeWrapper[0]; - internal const Modifiers UnloadableModifiersHack = Modifiers.Final | Modifiers.Interface | Modifiers.Private; - internal const Modifiers VerifierTypeModifiersHack = Modifiers.Final | Modifiers.Interface; - - /// - /// Initializes a new instance. - /// - /// - /// - /// - /// - internal TypeWrapper(TypeFlags flags, Modifiers modifiers, string name) - { - Profiler.Count("TypeWrapper"); - - this.flags = flags; - this.modifiers = modifiers; - this.name = name == null ? null : String.Intern(name); - } - -#if EMITTERS - - internal void EmitClassLiteral(CodeEmitter ilgen) - { - Debug.Assert(!this.IsPrimitive); - - Type type = GetClassLiteralType(); - - // note that this has to be the same check as in LazyInitClass - if (!this.IsFastClassLiteralSafe || IsForbiddenTypeParameterType(type)) - { - int rank = 0; - while (ReflectUtil.IsVector(type)) - { - rank++; - type = type.GetElementType(); - } - if (rank == 0) - { - ilgen.Emit(OpCodes.Ldtoken, type); - Compiler.getClassFromTypeHandle.EmitCall(ilgen); - } - else - { - ilgen.Emit(OpCodes.Ldtoken, type); - ilgen.EmitLdc_I4(rank); - Compiler.getClassFromTypeHandle2.EmitCall(ilgen); - } - } - else - { - ilgen.Emit(OpCodes.Ldsfld, RuntimeHelperTypes.GetClassLiteralField(type)); - } - } -#endif // EMITTERS - - private Type GetClassLiteralType() - { - Debug.Assert(!this.IsPrimitive); - - TypeWrapper tw = this; - if (tw.IsGhostArray) - { - var rank = tw.ArrayRank; - while (tw.IsArray) - tw = tw.ElementTypeWrapper; - - return ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, rank); - } - else - { - return tw.IsRemapped ? tw.TypeAsBaseType : tw.TypeAsTBD; - } - } - - private static bool IsForbiddenTypeParameterType(Type type) - { - // these are the types that may not be used as a type argument when instantiating a generic type - return type == Types.Void -#if NETFRAMEWORK - || type == JVM.Import(typeof(ArgIterator)) -#endif - || type == JVM.Import(typeof(RuntimeArgumentHandle)) - || type == JVM.Import(typeof(TypedReference)) - || type.ContainsGenericParameters - || type.IsByRef; - } - - internal virtual bool IsFastClassLiteralSafe - { - get { return false; } - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - - internal void SetClassObject(java.lang.Class classObject) - { - this.classObject = classObject; - } - - internal java.lang.Class ClassObject - { - get - { - Debug.Assert(!IsUnloadable && !IsVerifierType); - if (classObject == null) - LazyInitClass(); - - return classObject; - } - } - -#if !FIRST_PASS - - private java.lang.Class GetPrimitiveClass() - { - if (this == PrimitiveTypeWrapper.BYTE) - { - return java.lang.Byte.TYPE; - } - else if (this == PrimitiveTypeWrapper.CHAR) - { - return java.lang.Character.TYPE; - } - else if (this == PrimitiveTypeWrapper.DOUBLE) - { - return java.lang.Double.TYPE; - } - else if (this == PrimitiveTypeWrapper.FLOAT) - { - return java.lang.Float.TYPE; - } - else if (this == PrimitiveTypeWrapper.INT) - { - return java.lang.Integer.TYPE; - } - else if (this == PrimitiveTypeWrapper.LONG) - { - return java.lang.Long.TYPE; - } - else if (this == PrimitiveTypeWrapper.SHORT) - { - return java.lang.Short.TYPE; - } - else if (this == PrimitiveTypeWrapper.BOOLEAN) - { - return java.lang.Boolean.TYPE; - } - else if (this == PrimitiveTypeWrapper.VOID) - { - return java.lang.Void.TYPE; - } - else - { - throw new InvalidOperationException(); - } - } -#endif - - private void LazyInitClass() - { - lock (this) - { - if (classObject == null) - { - // DynamicTypeWrapper should haved already had SetClassObject explicitly - Debug.Assert(!IsDynamic); -#if !FIRST_PASS - java.lang.Class clazz; - // note that this has to be the same check as in EmitClassLiteral - if (!this.IsFastClassLiteralSafe) - { - if (this.IsPrimitive) - { - clazz = GetPrimitiveClass(); - } - else - { - clazz = new java.lang.Class(null); - } - } - else - { - Type type = GetClassLiteralType(); - if (IsForbiddenTypeParameterType(type)) - { - clazz = new java.lang.Class(type); - } - else - { - clazz = (java.lang.Class)typeof(ikvm.@internal.ClassLiteral<>).MakeGenericType(type).GetField("Value").GetValue(null); - } - } -#if __MonoCS__ - SetTypeWrapperHack(clazz, this); -#else - clazz.typeWrapper = this; -#endif - // MONOBUG Interlocked.Exchange is broken on Mono, so we use CompareExchange - System.Threading.Interlocked.CompareExchange(ref classObject, clazz, null); -#endif - } - } - } - -#if __MonoCS__ - // MONOBUG this method is to work around an mcs bug - internal static void SetTypeWrapperHack(object clazz, TypeWrapper type) - { -#if !FIRST_PASS - typeof(java.lang.Class).GetField("typeWrapper", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(clazz, type); -#endif - } -#endif - -#if !FIRST_PASS - - private static void ResolvePrimitiveTypeWrapperClasses() - { - // note that we're evaluating all ClassObject properties for the side effect - // (to initialize and associate the ClassObject with the TypeWrapper) - if (PrimitiveTypeWrapper.BYTE.ClassObject == null - || PrimitiveTypeWrapper.CHAR.ClassObject == null - || PrimitiveTypeWrapper.DOUBLE.ClassObject == null - || PrimitiveTypeWrapper.FLOAT.ClassObject == null - || PrimitiveTypeWrapper.INT.ClassObject == null - || PrimitiveTypeWrapper.LONG.ClassObject == null - || PrimitiveTypeWrapper.SHORT.ClassObject == null - || PrimitiveTypeWrapper.BOOLEAN.ClassObject == null - || PrimitiveTypeWrapper.VOID.ClassObject == null) - { - throw new InvalidOperationException(); - } - } -#endif - - internal static TypeWrapper FromClass(java.lang.Class clazz) - { -#if FIRST_PASS - throw new NotImplementedException(); -#else - // MONOBUG redundant cast to workaround mcs bug - var tw = (TypeWrapper)(object)clazz.typeWrapper; - if (tw == null) - { - var type = clazz.type; - if (type == null) - { - ResolvePrimitiveTypeWrapperClasses(); - return FromClass(clazz); - } - - if (type == typeof(void) || type.IsPrimitive || ClassLoaderWrapper.IsRemappedType(type)) - tw = DotNetTypeWrapper.GetWrapperFromDotNetType(type); - else - tw = ClassLoaderWrapper.GetWrapperFromType(type); - -#if __MonoCS__ - SetTypeWrapperHack(clazz, tw); -#else - clazz.typeWrapper = tw; -#endif - } - - return tw; -#endif - } - -#endif // !STATIC_COMPILER && !STUB_GENERATOR - - public override string ToString() - { - return GetType().Name + "[" + name + "]"; - } - - // For UnloadableTypeWrapper it tries to load the type through the specified loader - // and if that fails it throw a NoClassDefFoundError (not a java.lang.NoClassDefFoundError), - // for all other types this is a no-op. - internal virtual TypeWrapper EnsureLoadable(ClassLoaderWrapper loader) - { - return this; - } - - private void SetTypeFlag(TypeFlags flag) - { - // we use a global lock object, since the chance of contention is very small - lock (flagsLock) - { - flags |= flag; - } - } - - internal bool HasIncompleteInterfaceImplementation - { - get - { - TypeWrapper baseWrapper = this.BaseTypeWrapper; - return (flags & TypeFlags.HasIncompleteInterfaceImplementation) != 0 || (baseWrapper != null && baseWrapper.HasIncompleteInterfaceImplementation); - } - } - - internal void SetHasIncompleteInterfaceImplementation() - { - SetTypeFlag(TypeFlags.HasIncompleteInterfaceImplementation); - } - - internal bool HasUnsupportedAbstractMethods - { - get - { - foreach (var iface in this.Interfaces) - if (iface.HasUnsupportedAbstractMethods) - return true; - - var baseWrapper = this.BaseTypeWrapper; - return (flags & TypeFlags.HasUnsupportedAbstractMethods) != 0 || (baseWrapper != null && baseWrapper.HasUnsupportedAbstractMethods); - } - } - - internal void SetHasUnsupportedAbstractMethods() - { - SetTypeFlag(TypeFlags.HasUnsupportedAbstractMethods); - } - - internal virtual bool HasStaticInitializer - { - get - { - return (flags & TypeFlags.HasStaticInitializer) != 0; - } - } - - internal void SetHasStaticInitializer() - { - SetTypeFlag(TypeFlags.HasStaticInitializer); - } - - internal bool HasVerifyError - { - get - { - return (flags & TypeFlags.VerifyError) != 0; - } - } - - internal void SetHasVerifyError() - { - SetTypeFlag(TypeFlags.VerifyError); - } - - internal bool HasClassFormatError - { - get - { - return (flags & TypeFlags.ClassFormatError) != 0; - } - } - - internal void SetHasClassFormatError() - { - SetTypeFlag(TypeFlags.ClassFormatError); - } - - internal virtual bool IsFakeTypeContainer - { - get - { - return false; - } - } - - internal virtual bool IsFakeNestedType - { - get - { - return false; - } - } - - // is this an anonymous class (in the sense of Unsafe.defineAnonymousClass(), not the JLS) - internal bool IsUnsafeAnonymous - { - get { return (flags & TypeFlags.Anonymous) != 0; } - } - - // a ghost is an interface that appears to be implemented by a .NET type - // (e.g. System.String (aka java.lang.String) appears to implement java.lang.CharSequence, - // so java.lang.CharSequence is a ghost) - internal virtual bool IsGhost - { - get - { - return false; - } - } - - // is this an array type of which the ultimate element type is a ghost? - internal bool IsGhostArray - { - get - { - return !IsUnloadable && IsArray && (ElementTypeWrapper.IsGhost || ElementTypeWrapper.IsGhostArray); - } - } - - internal virtual FieldInfo GhostRefField - { - get - { - throw new InvalidOperationException(); - } - } - - internal virtual bool IsRemapped - { - get - { - return false; - } - } - - internal bool IsArray - { - get - { - return name != null && name[0] == '['; - } - } - - // NOTE for non-array types this returns 0 - internal int ArrayRank - { - get - { - int i = 0; - if (name != null) - { - while (name[i] == '[') - { - i++; - } - } - return i; - } - } - - internal virtual TypeWrapper GetUltimateElementTypeWrapper() - { - throw new InvalidOperationException(); - } - - internal bool IsNonPrimitiveValueType - { - get - { - return this != VerifierTypeWrapper.Null && !IsPrimitive && !IsGhost && TypeAsTBD.IsValueType; - } - } - - internal bool IsPrimitive - { - get - { - return name == null; - } - } - - internal bool IsWidePrimitive - { - get - { - return this == PrimitiveTypeWrapper.LONG || this == PrimitiveTypeWrapper.DOUBLE; - } - } - - internal bool IsIntOnStackPrimitive - { - get - { - return name == null && - (this == PrimitiveTypeWrapper.BOOLEAN || - this == PrimitiveTypeWrapper.BYTE || - this == PrimitiveTypeWrapper.CHAR || - this == PrimitiveTypeWrapper.SHORT || - this == PrimitiveTypeWrapper.INT); - } - } - - private static bool IsJavaPrimitive(Type type) - { - return type == PrimitiveTypeWrapper.BOOLEAN.TypeAsTBD - || type == PrimitiveTypeWrapper.BYTE.TypeAsTBD - || type == PrimitiveTypeWrapper.CHAR.TypeAsTBD - || type == PrimitiveTypeWrapper.DOUBLE.TypeAsTBD - || type == PrimitiveTypeWrapper.FLOAT.TypeAsTBD - || type == PrimitiveTypeWrapper.INT.TypeAsTBD - || type == PrimitiveTypeWrapper.LONG.TypeAsTBD - || type == PrimitiveTypeWrapper.SHORT.TypeAsTBD - || type == PrimitiveTypeWrapper.VOID.TypeAsTBD; - } - - internal bool IsBoxedPrimitive - { - get - { - return !IsPrimitive && IsJavaPrimitive(TypeAsSignatureType); - } - } - - internal bool IsErasedOrBoxedPrimitiveOrRemapped - { - get - { - bool erased = IsUnloadable || IsGhostArray; - return erased || IsBoxedPrimitive || (IsRemapped && this is DotNetTypeWrapper); - } - } - - internal bool IsUnloadable - { - get - { - // NOTE we abuse modifiers to note unloadable classes - return modifiers == UnloadableModifiersHack; - } - } - - internal bool IsVerifierType - { - get - { - // NOTE we abuse modifiers to note verifier types - return modifiers == VerifierTypeModifiersHack; - } - } - - internal virtual bool IsMapUnsafeException - { - get - { - return false; - } - } - - internal Modifiers Modifiers - { - get - { - return modifiers; - } - } - - // since for inner classes, the modifiers returned by Class.getModifiers are different from the actual - // modifiers (as used by the VM access control mechanism), we have this additional property - internal virtual Modifiers ReflectiveModifiers - { - get - { - return modifiers; - } - } - - internal bool IsInternal - { - get - { - return (flags & TypeFlags.InternalAccess) != 0; - } - } - - internal bool IsPublic - { - get - { - return (modifiers & Modifiers.Public) != 0; - } - } - - internal bool IsAbstract - { - get - { - // interfaces don't need to marked abstract explicitly (and javac 1.1 didn't do it) - return (modifiers & (Modifiers.Abstract | Modifiers.Interface)) != 0; - } - } - - internal bool IsFinal - { - get - { - return (modifiers & Modifiers.Final) != 0; - } - } - - internal bool IsInterface - { - get - { - Debug.Assert(!IsUnloadable && !IsVerifierType); - return (modifiers & Modifiers.Interface) != 0; - } - } - - // this exists because interfaces and arrays of interfaces are treated specially - // by the verifier, interfaces don't have a common base (other than java.lang.Object) - // so any object reference or object array reference can be used where an interface - // or interface array reference is expected (the compiler will insert the required casts). - internal bool IsInterfaceOrInterfaceArray - { - get - { - TypeWrapper tw = this; - while (tw.IsArray) - { - tw = tw.ElementTypeWrapper; - } - return tw.IsInterface; - } - } - - internal abstract ClassLoaderWrapper GetClassLoader(); - - internal FieldWrapper GetFieldWrapper(string fieldName, string fieldSig) - { - foreach (FieldWrapper fw in GetFields()) - { - if (fw.Name == fieldName && fw.Signature == fieldSig) - { - return fw; - } - } - foreach (TypeWrapper iface in this.Interfaces) - { - FieldWrapper fw = iface.GetFieldWrapper(fieldName, fieldSig); - if (fw != null) - { - return fw; - } - } - TypeWrapper baseWrapper = this.BaseTypeWrapper; - if (baseWrapper != null) - { - return baseWrapper.GetFieldWrapper(fieldName, fieldSig); - } - return null; - } - - protected virtual void LazyPublishMembers() - { - if (methods == null) - { - methods = MethodWrapper.EmptyArray; - } - if (fields == null) - { - fields = FieldWrapper.EmptyArray; - } - } - - protected virtual void LazyPublishMethods() - { - LazyPublishMembers(); - } - - protected virtual void LazyPublishFields() - { - LazyPublishMembers(); - } - - internal MethodWrapper[] GetMethods() - { - if (methods == null) - { - lock (this) - { - if (methods == null) - { -#if STATIC_COMPILER - if (IsUnloadable || !CheckMissingBaseTypes(TypeAsBaseType)) - { - return methods = MethodWrapper.EmptyArray; - } -#endif - LazyPublishMethods(); - } - } - } - return methods; - } - - internal FieldWrapper[] GetFields() - { - if (fields == null) - { - lock (this) - { - if (fields == null) - { -#if STATIC_COMPILER - if (IsUnloadable || !CheckMissingBaseTypes(TypeAsBaseType)) - { - return fields = FieldWrapper.EmptyArray; - } -#endif - LazyPublishFields(); - } - } - } - return fields; - } - -#if STATIC_COMPILER - private static bool CheckMissingBaseTypes(Type type) - { - while (type != null) - { - if (type.__ContainsMissingType) - { - StaticCompiler.IssueMissingTypeMessage(type); - return false; - } - bool ok = true; - foreach (Type iface in type.__GetDeclaredInterfaces()) - { - ok &= CheckMissingBaseTypes(iface); - } - if (!ok) - { - return false; - } - type = type.BaseType; - } - return true; - } -#endif - - internal MethodWrapper GetMethodWrapper(string name, string sig, bool inherit) - { - // We need to get the methods before calling String.IsInterned, because getting them might cause the strings to be interned - MethodWrapper[] methods = GetMethods(); - // MemberWrapper interns the name and sig so we can use ref equality - // profiling has shown this to be more efficient - string _name = String.IsInterned(name); - string _sig = String.IsInterned(sig); - foreach (MethodWrapper mw in methods) - { - // NOTE we can use ref equality, because names and signatures are - // always interned by MemberWrapper - if (ReferenceEquals(mw.Name, _name) && ReferenceEquals(mw.Signature, _sig)) - { - return mw; - } - } - TypeWrapper baseWrapper = this.BaseTypeWrapper; - if (inherit && baseWrapper != null) - { - return baseWrapper.GetMethodWrapper(name, sig, inherit); - } - return null; - } - - internal MethodWrapper GetInterfaceMethod(string name, string sig) - { - MethodWrapper method = GetMethodWrapper(name, sig, false); - if (method != null) - { - return method; - } - TypeWrapper[] interfaces = Interfaces; - for (int i = 0; i < interfaces.Length; i++) - { - method = interfaces[i].GetInterfaceMethod(name, sig); - if (method != null) - { - return method; - } - } - return null; - } - - internal void SetMethods(MethodWrapper[] methods) - { - Debug.Assert(methods != null); - System.Threading.Thread.MemoryBarrier(); - this.methods = methods; - } - - internal void SetFields(FieldWrapper[] fields) - { - Debug.Assert(fields != null); - System.Threading.Thread.MemoryBarrier(); - this.fields = fields; - } - - internal string Name - { - get - { - return name; - } - } - - // the name of the type as it appears in a Java signature string (e.g. "Ljava.lang.Object;" or "I") - internal virtual string SigName - { - get - { - return "L" + this.Name + ";"; - } - } - - // returns true iff wrapper is allowed to access us - internal bool IsAccessibleFrom(TypeWrapper wrapper) - { - return IsPublic - || (IsInternal && InternalsVisibleTo(wrapper)) - || IsPackageAccessibleFrom(wrapper); - } - - internal bool InternalsVisibleTo(TypeWrapper wrapper) - { - return GetClassLoader().InternalsVisibleToImpl(this, wrapper); - } - - internal virtual bool IsPackageAccessibleFrom(TypeWrapper wrapper) - { - if (MatchingPackageNames(name, wrapper.name)) - { -#if STATIC_COMPILER - CompilerClassLoader ccl = GetClassLoader() as CompilerClassLoader; - if (ccl != null) - { - // this is a hack for multi target -sharedclassloader compilation - // (during compilation we have multiple CompilerClassLoader instances to represent the single shared runtime class loader) - return ccl.IsEquivalentTo(wrapper.GetClassLoader()); - } -#endif - return GetClassLoader() == wrapper.GetClassLoader(); - } - else - { - return false; - } - } - - private static bool MatchingPackageNames(string name1, string name2) - { - int index1 = name1.LastIndexOf('.'); - int index2 = name2.LastIndexOf('.'); - if (index1 == -1 && index2 == -1) - { - return true; - } - // for array types we need to skip the brackets - int skip1 = 0; - int skip2 = 0; - while (name1[skip1] == '[') - { - skip1++; - } - while (name2[skip2] == '[') - { - skip2++; - } - if (skip1 > 0) - { - // skip over the L that follows the brackets - skip1++; - } - if (skip2 > 0) - { - // skip over the L that follows the brackets - skip2++; - } - if ((index1 - skip1) != (index2 - skip2)) - { - return false; - } - return String.CompareOrdinal(name1, skip1, name2, skip2, index1 - skip1) == 0; - } - - internal abstract Type TypeAsTBD - { - get; - } - - internal Type TypeAsSignatureType - { - get - { - if (IsUnloadable) - { - return ((UnloadableTypeWrapper)this).MissingType ?? Types.Object; - } - if (IsGhostArray) - { - return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); - } - return TypeAsTBD; - } - } - - internal Type TypeAsPublicSignatureType - { - get - { - return (IsPublic ? this : GetPublicBaseTypeWrapper()).TypeAsSignatureType; - } - } - - internal virtual Type TypeAsBaseType - { - get - { - return TypeAsTBD; - } - } - - internal Type TypeAsLocalOrStackType - { - get - { - if (IsUnloadable || IsGhost) - { - return Types.Object; - } - if (IsNonPrimitiveValueType) - { - // return either System.ValueType or System.Enum - return TypeAsTBD.BaseType; - } - if (IsGhostArray) - { - return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); - } - return TypeAsTBD; - } - } - - /** Use this if the type is used as an array or array element */ - internal Type TypeAsArrayType - { - get - { - if (IsUnloadable || IsGhost) - { - return Types.Object; - } - if (IsGhostArray) - { - return ArrayTypeWrapper.MakeArrayType(Types.Object, ArrayRank); - } - return TypeAsTBD; - } - } - - internal Type TypeAsExceptionType - { - get - { - if (IsUnloadable) - { - return Types.Exception; - } - return TypeAsTBD; - } - } - - internal abstract TypeWrapper BaseTypeWrapper - { - get; - } - - internal TypeWrapper ElementTypeWrapper - { - get - { - Debug.Assert(!this.IsUnloadable); - Debug.Assert(this == VerifierTypeWrapper.Null || this.IsArray); - - if (this == VerifierTypeWrapper.Null) - { - return VerifierTypeWrapper.Null; - } - - // TODO consider caching the element type - switch (name[1]) - { - case '[': - // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load - // (because the ultimate element type was already loaded when this type was created) - return GetClassLoader().LoadClassByDottedNameFast(name.Substring(1)); - case 'L': - // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load - // (because the ultimate element type was already loaded when this type was created) - return GetClassLoader().LoadClassByDottedNameFast(name.Substring(2, name.Length - 3)); - case 'Z': - return PrimitiveTypeWrapper.BOOLEAN; - case 'B': - return PrimitiveTypeWrapper.BYTE; - case 'S': - return PrimitiveTypeWrapper.SHORT; - case 'C': - return PrimitiveTypeWrapper.CHAR; - case 'I': - return PrimitiveTypeWrapper.INT; - case 'J': - return PrimitiveTypeWrapper.LONG; - case 'F': - return PrimitiveTypeWrapper.FLOAT; - case 'D': - return PrimitiveTypeWrapper.DOUBLE; - default: - throw new InvalidOperationException(name); - } - } - } - - internal TypeWrapper MakeArrayType(int rank) - { - Debug.Assert(rank != 0); - // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load - return GetClassLoader().LoadClassByDottedNameFast(new String('[', rank) + this.SigName); - } - - internal bool ImplementsInterface(TypeWrapper interfaceWrapper) - { - TypeWrapper typeWrapper = this; - while (typeWrapper != null) - { - TypeWrapper[] interfaces = typeWrapper.Interfaces; - for (int i = 0; i < interfaces.Length; i++) - { - if (interfaces[i] == interfaceWrapper) - { - return true; - } - if (interfaces[i].ImplementsInterface(interfaceWrapper)) - { - return true; - } - } - typeWrapper = typeWrapper.BaseTypeWrapper; - } - return false; - } - - internal bool IsSubTypeOf(TypeWrapper baseType) - { - // make sure IsSubTypeOf isn't used on primitives - Debug.Assert(!this.IsPrimitive); - Debug.Assert(!baseType.IsPrimitive); - // can't be used on Unloadable - Debug.Assert(!this.IsUnloadable); - Debug.Assert(!baseType.IsUnloadable); - - if (baseType.IsInterface) - { - if (baseType == this) - { - return true; - } - return ImplementsInterface(baseType); - } - // NOTE this isn't just an optimization, it is also required when this is an interface - if (baseType == CoreClasses.java.lang.Object.Wrapper) - { - return true; - } - TypeWrapper subType = this; - while (subType != baseType) - { - subType = subType.BaseTypeWrapper; - if (subType == null) - { - return false; - } - } - return true; - } - - internal bool IsAssignableTo(TypeWrapper wrapper) - { - if (this == wrapper) - { - return true; - } - if (this.IsPrimitive || wrapper.IsPrimitive) - { - return false; - } - if (this == VerifierTypeWrapper.Null) - { - return true; - } - if (wrapper.IsInterface) - { - return ImplementsInterface(wrapper); - } - int rank1 = this.ArrayRank; - int rank2 = wrapper.ArrayRank; - if (rank1 > 0 && rank2 > 0) - { - rank1--; - rank2--; - TypeWrapper elem1 = this.ElementTypeWrapper; - TypeWrapper elem2 = wrapper.ElementTypeWrapper; - while (rank1 != 0 && rank2 != 0) - { - elem1 = elem1.ElementTypeWrapper; - elem2 = elem2.ElementTypeWrapper; - rank1--; - rank2--; - } - if (elem1.IsPrimitive || elem2.IsPrimitive) - { - return false; - } - return (!elem1.IsNonPrimitiveValueType && elem1.IsSubTypeOf(elem2)); - } - return this.IsSubTypeOf(wrapper); - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal bool IsInstance(object obj) - { - if (obj != null) - { - TypeWrapper thisWrapper = this; - TypeWrapper objWrapper = IKVM.Java.Externs.ikvm.runtime.Util.GetTypeWrapperFromObject(obj); - return objWrapper.IsAssignableTo(thisWrapper); - } - return false; - } -#endif - - internal virtual TypeWrapper[] Interfaces - { - get { return EmptyArray; } - } - - // NOTE this property can only be called for finished types! - internal virtual TypeWrapper[] InnerClasses - { - get { return EmptyArray; } - } - - // NOTE this property can only be called for finished types! - internal virtual TypeWrapper DeclaringTypeWrapper - { - get { return null; } - } - - internal virtual void Finish() - { - } - - internal void LinkAll() - { - if ((flags & TypeFlags.Linked) == 0) - { - TypeWrapper tw = BaseTypeWrapper; - if (tw != null) - { - tw.LinkAll(); - } - foreach (TypeWrapper iface in Interfaces) - { - iface.LinkAll(); - } - foreach (MethodWrapper mw in GetMethods()) - { - mw.Link(); - } - foreach (FieldWrapper fw in GetFields()) - { - fw.Link(); - } - SetTypeFlag(TypeFlags.Linked); - } - } - -#if !STATIC_COMPILER - [Conditional("DEBUG")] - internal static void AssertFinished(Type type) - { - if (type != null) - { - while (type.HasElementType) - { - type = type.GetElementType(); - } - Debug.Assert(!(type is TypeBuilder)); - } - } -#endif - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal void RunClassInit() - { - Type t = IsRemapped ? TypeAsBaseType : TypeAsTBD; - if (t != null) - { - System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(t.TypeHandle); - } - } -#endif - -#if EMITTERS - internal void EmitUnbox(CodeEmitter ilgen) - { - Debug.Assert(this.IsNonPrimitiveValueType); - - ilgen.EmitUnboxSpecial(this.TypeAsTBD); - } - - internal void EmitBox(CodeEmitter ilgen) - { - Debug.Assert(this.IsNonPrimitiveValueType); - - ilgen.Emit(OpCodes.Box, this.TypeAsTBD); - } - - internal void EmitConvSignatureTypeToStackType(CodeEmitter ilgen) - { - if (IsUnloadable) - { - } - else if (this == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Conv_I1); - } - else if (IsNonPrimitiveValueType) - { - EmitBox(ilgen); - } - else if (IsGhost) - { - CodeEmitterLocal local = ilgen.DeclareLocal(TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, local); - ilgen.Emit(OpCodes.Ldloca, local); - ilgen.Emit(OpCodes.Ldfld, GhostRefField); - } - } - - // NOTE sourceType is optional and only used for interfaces, - // it is *not* used to automatically downcast - internal void EmitConvStackTypeToSignatureType(CodeEmitter ilgen, TypeWrapper sourceType) - { - if (!IsUnloadable) - { - if (IsGhost) - { - CodeEmitterLocal local1 = ilgen.DeclareLocal(TypeAsLocalOrStackType); - ilgen.Emit(OpCodes.Stloc, local1); - CodeEmitterLocal local2 = ilgen.DeclareLocal(TypeAsSignatureType); - ilgen.Emit(OpCodes.Ldloca, local2); - ilgen.Emit(OpCodes.Ldloc, local1); - ilgen.Emit(OpCodes.Stfld, GhostRefField); - ilgen.Emit(OpCodes.Ldloca, local2); - ilgen.Emit(OpCodes.Ldobj, TypeAsSignatureType); - } - // because of the way interface merging works, any reference is valid - // for any interface reference - else if (IsInterfaceOrInterfaceArray && (sourceType == null || sourceType.IsUnloadable || !sourceType.IsAssignableTo(this))) - { - ilgen.EmitAssertType(TypeAsTBD); - Profiler.Count("InterfaceDownCast"); - } - else if (IsNonPrimitiveValueType) - { - EmitUnbox(ilgen); - } - else if (sourceType != null && sourceType.IsUnloadable) - { - ilgen.Emit(OpCodes.Castclass, TypeAsSignatureType); - } - } - } - - internal virtual void EmitCheckcast(CodeEmitter ilgen) - { - if (IsGhost) - { - ilgen.Emit(OpCodes.Dup); - // TODO make sure we get the right "Cast" method and cache it - // NOTE for dynamic ghosts we don't end up here because AotTypeWrapper overrides this method, - // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) - ilgen.Emit(OpCodes.Call, TypeAsTBD.GetMethod("Cast")); - ilgen.Emit(OpCodes.Pop); - } - else if (IsGhostArray) - { - ilgen.Emit(OpCodes.Dup); - // TODO make sure we get the right "CastArray" method and cache it - // NOTE for dynamic ghosts we don't end up here because AotTypeWrapper overrides this method, - // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) - TypeWrapper tw = this; - int rank = 0; - while (tw.IsArray) - { - rank++; - tw = tw.ElementTypeWrapper; - } - ilgen.EmitLdc_I4(rank); - ilgen.Emit(OpCodes.Call, tw.TypeAsTBD.GetMethod("CastArray")); - ilgen.Emit(OpCodes.Castclass, ArrayTypeWrapper.MakeArrayType(Types.Object, rank)); - } - else - { - ilgen.EmitCastclass(TypeAsTBD); - } - } - - internal virtual void EmitInstanceOf(CodeEmitter ilgen) - { - if (IsGhost) - { - // TODO make sure we get the right "IsInstance" method and cache it - // NOTE for dynamic ghosts we don't end up here because DynamicTypeWrapper overrides this method, - // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) - ilgen.Emit(OpCodes.Call, TypeAsTBD.GetMethod("IsInstance")); - } - else if (IsGhostArray) - { - // TODO make sure we get the right "IsInstanceArray" method and cache it - // NOTE for dynamic ghosts we don't end up here because DynamicTypeWrapper overrides this method, - // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) - TypeWrapper tw = this; - int rank = 0; - while (tw.IsArray) - { - rank++; - tw = tw.ElementTypeWrapper; - } - ilgen.EmitLdc_I4(rank); - ilgen.Emit(OpCodes.Call, tw.TypeAsTBD.GetMethod("IsInstanceArray")); - } - else - { - ilgen.Emit_instanceof(TypeAsTBD); - } - } -#endif // EMITTERS - - // NOTE don't call this method, call MethodWrapper.Link instead - internal virtual MethodBase LinkMethod(MethodWrapper mw) - { - return mw.GetMethod(); - } - - // NOTE don't call this method, call FieldWrapper.Link instead - internal virtual FieldInfo LinkField(FieldWrapper fw) - { - return fw.GetField(); - } - -#if EMITTERS - internal virtual void EmitRunClassConstructor(CodeEmitter ilgen) - { - } -#endif // EMITTERS - - internal virtual string GetGenericSignature() - { - return null; - } - - internal virtual string GetGenericMethodSignature(MethodWrapper mw) - { - return null; - } - - internal virtual string GetGenericFieldSignature(FieldWrapper fw) - { - return null; - } - - internal virtual MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) - { - return null; - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal virtual string[] GetEnclosingMethod() - { - return null; - } - - internal virtual object[] GetDeclaredAnnotations() - { - return null; - } - - internal virtual object[] GetMethodAnnotations(MethodWrapper mw) - { - return null; - } - - internal virtual object[][] GetParameterAnnotations(MethodWrapper mw) - { - return null; - } - - internal virtual object[] GetFieldAnnotations(FieldWrapper fw) - { - return null; - } - - internal virtual string GetSourceFileName() - { - return null; - } - - internal virtual int GetSourceLineNumber(MethodBase mb, int ilOffset) - { - return -1; - } - - internal virtual object GetAnnotationDefault(MethodWrapper mw) - { - MethodBase mb = mw.GetMethod(); - if (mb != null) - { - object[] attr = mb.GetCustomAttributes(typeof(AnnotationDefaultAttribute), false); - if (attr.Length == 1) - { - return JVM.NewAnnotationElementValue(mw.DeclaringType.GetClassLoader().GetJavaClassLoader(), mw.ReturnType.ClassObject, ((AnnotationDefaultAttribute)attr[0]).Value); - } - } - return null; - } -#endif // !STATIC_COMPILER && !STUB_GENERATOR - - internal virtual Annotation Annotation - { - get - { - return null; - } - } - - internal virtual Type EnumType - { - get - { - return null; - } - } - - private static Type[] GetInterfaces(Type type) - { -#if STATIC_COMPILER || STUB_GENERATOR - List list = new List(); - for (; type != null && !type.__IsMissing; type = type.BaseType) - { - AddInterfaces(list, type); - } - return list.ToArray(); -#else - return type.GetInterfaces(); -#endif - } - -#if STATIC_COMPILER || STUB_GENERATOR - private static void AddInterfaces(List list, Type type) - { - foreach (Type iface in type.__GetDeclaredInterfaces()) - { - if (!list.Contains(iface)) - { - list.Add(iface); - if (!iface.__IsMissing) - { - AddInterfaces(list, iface); - } - } - } - } -#endif - - protected static TypeWrapper[] GetImplementedInterfacesAsTypeWrappers(Type type) - { - Type[] interfaceTypes = GetInterfaces(type); - TypeWrapper[] interfaces = new TypeWrapper[interfaceTypes.Length]; - for (int i = 0; i < interfaceTypes.Length; i++) - { - Type decl = interfaceTypes[i].DeclaringType; - if (decl != null && AttributeHelper.IsGhostInterface(decl)) - { - // we have to return the declaring type for ghost interfaces - interfaces[i] = ClassLoaderWrapper.GetWrapperFromType(decl); - } - else - { - interfaces[i] = ClassLoaderWrapper.GetWrapperFromType(interfaceTypes[i]); - } - } - for (int i = 0; i < interfaceTypes.Length; i++) - { - if (interfaces[i].IsRemapped) - { - // for remapped interfaces, we also return the original interface (Java types will ignore it, if it isn't listed in the ImplementsAttribute) - TypeWrapper twRemapped = interfaces[i]; - TypeWrapper tw = DotNetTypeWrapper.GetWrapperFromDotNetType(interfaceTypes[i]); - interfaces[i] = tw; - if (Array.IndexOf(interfaces, twRemapped) == -1) - { - interfaces = ArrayUtil.Concat(interfaces, twRemapped); - } - } - } - return interfaces; - } - - internal TypeWrapper GetPublicBaseTypeWrapper() - { - Debug.Assert(!this.IsPublic); - if (this.IsUnloadable || this.IsInterface) - { - return CoreClasses.java.lang.Object.Wrapper; - } - for (TypeWrapper tw = this; ; tw = tw.BaseTypeWrapper) - { - if (tw.IsPublic) - { - return tw; - } - } - } - -#if !STUB_GENERATOR - // return the constructor used for automagic .NET serialization - internal virtual MethodBase GetSerializationConstructor() - { - return this.TypeAsBaseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { - JVM.Import(typeof(System.Runtime.Serialization.SerializationInfo)), JVM.Import(typeof(System.Runtime.Serialization.StreamingContext)) }, null); - } - - internal virtual MethodBase GetBaseSerializationConstructor() - { - return BaseTypeWrapper.GetSerializationConstructor(); - } -#endif - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal virtual object GhostWrap(object obj) - { - return obj; - } - - internal virtual object GhostUnwrap(object obj) - { - return obj; - } -#endif - - internal bool IsDynamic - { -#if STUB_GENERATOR - get { return false; } -#else - get { return this is DynamicTypeWrapper; } -#endif - } - - internal virtual object[] GetConstantPool() - { - return null; - } - - internal virtual byte[] GetRawTypeAnnotations() - { - return null; - } - - internal virtual byte[] GetMethodRawTypeAnnotations(MethodWrapper mw) - { - return null; - } - - internal virtual byte[] GetFieldRawTypeAnnotations(FieldWrapper fw) - { - return null; - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal virtual TypeWrapper Host - { - get { return null; } - } -#endif - } - - sealed class UnloadableTypeWrapper : TypeWrapper - { - - internal const string ContainerTypeName = "__"; - readonly Type missingType; - Type customModifier; - - internal UnloadableTypeWrapper(string name) - : base(TypeFlags.None, TypeWrapper.UnloadableModifiersHack, name) - { - - } - - internal UnloadableTypeWrapper(Type missingType) : - this(missingType.FullName) // TODO demangle and re-mangle appropriately - { - this.missingType = missingType; - } - - internal UnloadableTypeWrapper(string name, Type customModifier) : - this(name) - { - this.customModifier = customModifier; - } - - internal override TypeWrapper BaseTypeWrapper - { - get { return null; } - } - - internal override ClassLoaderWrapper GetClassLoader() - { - return null; - } - - internal override TypeWrapper EnsureLoadable(ClassLoaderWrapper loader) - { - var tw = loader.LoadClassByDottedNameFast(this.Name); - if (tw == null) - throw new NoClassDefFoundError(this.Name); - - return tw; - } - - internal override string SigName - { - get - { - var name = Name; - if (name.StartsWith("[")) - return name; - - return "L" + name + ";"; - } - } - - protected override void LazyPublishMembers() - { - throw new InvalidOperationException("LazyPublishMembers called on UnloadableTypeWrapper: " + Name); - } - - internal override Type TypeAsTBD - { - get - { - throw new InvalidOperationException("get_Type called on UnloadableTypeWrapper: " + Name); - } - } - - internal override TypeWrapper[] Interfaces - { - get - { -#if STATIC_COMPILER - if (missingType != null) - { - StaticCompiler.IssueMissingTypeMessage(missingType); - return TypeWrapper.EmptyArray; - } -#endif - - throw new InvalidOperationException("get_Interfaces called on UnloadableTypeWrapper: " + Name); - } - } - - internal override TypeWrapper[] InnerClasses - { - get - { - throw new InvalidOperationException("get_InnerClasses called on UnloadableTypeWrapper: " + Name); - } - } - - internal override TypeWrapper DeclaringTypeWrapper - { - get - { - throw new InvalidOperationException("get_DeclaringTypeWrapper called on UnloadableTypeWrapper: " + Name); - } - } - - internal override void Finish() - { - throw new InvalidOperationException("Finish called on UnloadableTypeWrapper: " + Name); - } - - internal Type MissingType - { - get { return missingType; } - } - - internal Type CustomModifier - { - get { return customModifier; } - } - - internal void SetCustomModifier(Type type) - { - this.customModifier = type; - } - -#if EMITTERS - - internal Type GetCustomModifier(TypeWrapperFactory context) - { - // we don't need to lock, because we're only supposed to be called while holding the finish lock - return customModifier ?? (customModifier = context.DefineUnloadable(this.Name)); - } - - internal override void EmitCheckcast(CodeEmitter ilgen) - { - throw new InvalidOperationException("EmitCheckcast called on UnloadableTypeWrapper: " + Name); - } - - internal override void EmitInstanceOf(CodeEmitter ilgen) - { - throw new InvalidOperationException("EmitInstanceOf called on UnloadableTypeWrapper: " + Name); - } - -#endif // EMITTERS - - } - - sealed class PrimitiveTypeWrapper : TypeWrapper - { - - internal static readonly PrimitiveTypeWrapper BYTE = new PrimitiveTypeWrapper(Types.Byte, "B"); - internal static readonly PrimitiveTypeWrapper CHAR = new PrimitiveTypeWrapper(Types.Char, "C"); - internal static readonly PrimitiveTypeWrapper DOUBLE = new PrimitiveTypeWrapper(Types.Double, "D"); - internal static readonly PrimitiveTypeWrapper FLOAT = new PrimitiveTypeWrapper(Types.Single, "F"); - internal static readonly PrimitiveTypeWrapper INT = new PrimitiveTypeWrapper(Types.Int32, "I"); - internal static readonly PrimitiveTypeWrapper LONG = new PrimitiveTypeWrapper(Types.Int64, "J"); - internal static readonly PrimitiveTypeWrapper SHORT = new PrimitiveTypeWrapper(Types.Int16, "S"); - internal static readonly PrimitiveTypeWrapper BOOLEAN = new PrimitiveTypeWrapper(Types.Boolean, "Z"); - internal static readonly PrimitiveTypeWrapper VOID = new PrimitiveTypeWrapper(Types.Void, "V"); - - readonly Type type; - readonly string sigName; - - /// - /// Initializes a new instance. - /// - /// - /// - private PrimitiveTypeWrapper(Type type, string sigName) : - base(TypeFlags.None, Modifiers.Public | Modifiers.Abstract | Modifiers.Final, null) - { - this.type = type; - this.sigName = sigName; - } - - internal override TypeWrapper BaseTypeWrapper => null; - - internal static bool IsPrimitiveType(Type type) - { - return type == BYTE.type - || type == CHAR.type - || type == DOUBLE.type - || type == FLOAT.type - || type == INT.type - || type == LONG.type - || type == SHORT.type - || type == BOOLEAN.type - || type == VOID.type; - } - - internal override string SigName => sigName; - - internal override ClassLoaderWrapper GetClassLoader() => ClassLoaderWrapper.GetBootstrapClassLoader(); - - internal override Type TypeAsTBD => type; - - public override string ToString() => "PrimitiveTypeWrapper[" + sigName + "]"; - - } - - class CompiledTypeWrapper : TypeWrapper - { - - readonly Type type; - - TypeWrapper baseTypeWrapper = VerifierTypeWrapper.Null; - volatile TypeWrapper[] interfaces; - MethodInfo clinitMethod; - volatile bool clinitMethodSet; - Modifiers reflectiveModifiers; - - internal static CompiledTypeWrapper newInstance(string name, Type type) - { - // TODO since ghost and remapped types can only exist in the core library assembly, we probably - // should be able to remove the Type.IsDefined() tests in most cases - if (type.IsValueType && AttributeHelper.IsGhostInterface(type)) - { - return new CompiledGhostTypeWrapper(name, type); - } - else if (AttributeHelper.IsRemappedType(type)) - { - return new CompiledRemappedTypeWrapper(name, type); - } - else - { - return new CompiledTypeWrapper(name, type); - } - } - - private sealed class CompiledRemappedTypeWrapper : CompiledTypeWrapper - { - private readonly Type remappedType; - - internal CompiledRemappedTypeWrapper(string name, Type type) - : base(name, type) - { - RemappedTypeAttribute attr = AttributeHelper.GetRemappedType(type); - if (attr == null) - { - throw new InvalidOperationException(); - } - remappedType = attr.Type; - } - - internal override Type TypeAsTBD - { - get - { - return remappedType; - } - } - - internal override bool IsRemapped - { - get - { - return true; - } - } - - protected override void LazyPublishMethods() - { - List list = new List(); - const BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; - foreach (ConstructorInfo ctor in type.GetConstructors(bindingFlags)) - { - AddMethod(list, ctor); - } - foreach (MethodInfo method in type.GetMethods(bindingFlags)) - { - AddMethod(list, method); - } - // if we're a remapped interface, we need to get the methods from the real interface - if (remappedType.IsInterface) - { - Type nestedHelper = type.GetNestedType("__Helper", BindingFlags.Public | BindingFlags.Static); - foreach (RemappedInterfaceMethodAttribute m in AttributeHelper.GetRemappedInterfaceMethods(type)) - { - MethodInfo method = remappedType.GetMethod(m.MappedTo); - MethodInfo mbHelper = method; - ExModifiers modifiers = AttributeHelper.GetModifiers(method, false); - string name; - string sig; - TypeWrapper retType; - TypeWrapper[] paramTypes; - MemberFlags flags = MemberFlags.None; - GetNameSigFromMethodBase(method, out name, out sig, out retType, out paramTypes, ref flags); - if (nestedHelper != null) - { - mbHelper = nestedHelper.GetMethod(m.Name); - if (mbHelper == null) - { - mbHelper = method; - } - } - MethodWrapper mw = new CompiledRemappedMethodWrapper(this, m.Name, sig, method, retType, paramTypes, modifiers, false, mbHelper, null); - mw.SetDeclaredExceptions(m.Throws); - list.Add(mw); - } - } - SetMethods(list.ToArray()); - } - - private void AddMethod(List list, MethodBase method) - { - HideFromJavaFlags flags = AttributeHelper.GetHideFromJavaFlags(method); - if ((flags & HideFromJavaFlags.Code) == 0 - && (remappedType.IsSealed || !method.Name.StartsWith("instancehelper_")) - && (!remappedType.IsSealed || method.IsStatic)) - { - list.Add(CreateRemappedMethodWrapper(method, flags)); - } - } - - protected override void LazyPublishFields() - { - List list = new List(); - FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(field); - if ((hideFromJavaFlags & HideFromJavaFlags.Code) == 0) - { - list.Add(CreateFieldWrapper(field, hideFromJavaFlags)); - } - } - SetFields(list.ToArray()); - } - - private MethodWrapper CreateRemappedMethodWrapper(MethodBase mb, HideFromJavaFlags hideFromJavaflags) - { - ExModifiers modifiers = AttributeHelper.GetModifiers(mb, false); - string name; - string sig; - TypeWrapper retType; - TypeWrapper[] paramTypes; - MemberFlags flags = MemberFlags.None; - GetNameSigFromMethodBase(mb, out name, out sig, out retType, out paramTypes, ref flags); - MethodInfo mbHelper = mb as MethodInfo; - bool hideFromReflection = mbHelper != null && (hideFromJavaflags & HideFromJavaFlags.Reflection) != 0; - MethodInfo mbNonvirtualHelper = null; - if (!mb.IsStatic && !mb.IsConstructor) - { - ParameterInfo[] parameters = mb.GetParameters(); - Type[] argTypes = new Type[parameters.Length + 1]; - argTypes[0] = remappedType; - for (int i = 0; i < parameters.Length; i++) - { - argTypes[i + 1] = parameters[i].ParameterType; - } - MethodInfo helper = type.GetMethod("instancehelper_" + mb.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, argTypes, null); - if (helper != null) - { - mbHelper = helper; - } - mbNonvirtualHelper = type.GetMethod("nonvirtualhelper/" + mb.Name, BindingFlags.NonPublic | BindingFlags.Static, null, argTypes, null); - } - return new CompiledRemappedMethodWrapper(this, name, sig, mb, retType, paramTypes, modifiers, hideFromReflection, mbHelper, mbNonvirtualHelper); - } - } - - private sealed class CompiledGhostTypeWrapper : CompiledTypeWrapper - { - private volatile FieldInfo ghostRefField; - private volatile Type typeAsBaseType; - - internal CompiledGhostTypeWrapper(string name, Type type) - : base(name, type) - { - } - - internal override Type TypeAsBaseType - { - get - { - if (typeAsBaseType == null) - { - typeAsBaseType = type.GetNestedType("__Interface"); - } - return typeAsBaseType; - } - } - - internal override FieldInfo GhostRefField - { - get - { - if (ghostRefField == null) - { - ghostRefField = type.GetField("__"); - } - return ghostRefField; - } - } - - internal override bool IsGhost - { - get - { - return true; - } - } - -#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS - internal override object GhostWrap(object obj) - { - return type.GetMethod("Cast").Invoke(null, new object[] { obj }); - } - - internal override object GhostUnwrap(object obj) - { - return type.GetMethod("ToObject").Invoke(obj, new object[0]); - } -#endif - } - - /// - /// Gets the Java type name of a Java type. - /// - /// - /// - /// - internal static JavaTypeName? GetName(Type type) - { - if (type.HasElementType) - return null; - if (type.IsGenericType) - return null; - if (AttributeHelper.IsJavaModule(type.Module) == false) - return null; - - // look for our custom attribute, that contains the real name of the type (for inner classes) - var attr = AttributeHelper.GetInnerClass(type); - if (attr != null) - { - var name = attr.InnerClassName; - if (name != null) - return name; - } - - // type is an inner type - if (type.DeclaringType != null) - return GetName(type.DeclaringType) + "$" + TypeNameUtil.Unescape(type.Name); - - return TypeNameUtil.Unescape(type.FullName); - } - - private static TypeWrapper GetBaseTypeWrapper(Type type) - { - if (type.IsInterface || AttributeHelper.IsGhostInterface(type)) - { - return null; - } - else if (type.BaseType == null) - { - // System.Object must appear to be derived from java.lang.Object - return CoreClasses.java.lang.Object.Wrapper; - } - else - { - RemappedTypeAttribute attr = AttributeHelper.GetRemappedType(type); - if (attr != null) - { - if (attr.Type == Types.Object) - { - return null; - } - else - { - return CoreClasses.java.lang.Object.Wrapper; - } - } - else if (ClassLoaderWrapper.IsRemappedType(type.BaseType)) - { - // if we directly extend System.Object or System.Exception, the base class must be cli.System.Object or cli.System.Exception - return DotNetTypeWrapper.GetWrapperFromDotNetType(type.BaseType); - } - TypeWrapper tw = null; - while (tw == null) - { - type = type.BaseType; - tw = ClassLoaderWrapper.GetWrapperFromType(type); - } - return tw; - } - } - - private CompiledTypeWrapper(ExModifiers exmod, string name) - : base(exmod.IsInternal ? TypeFlags.InternalAccess : TypeFlags.None, exmod.Modifiers, name) - { - } - - private CompiledTypeWrapper(string name, Type type) - : this(GetModifiers(type), name) - { - Debug.Assert(!(type is TypeBuilder)); - Debug.Assert(!type.Name.EndsWith("[]")); - - this.type = type; - } - - internal override TypeWrapper BaseTypeWrapper - { - get - { - if (baseTypeWrapper != VerifierTypeWrapper.Null) - { - return baseTypeWrapper; - } - return baseTypeWrapper = GetBaseTypeWrapper(type); - } - } - - internal override ClassLoaderWrapper GetClassLoader() - { - return AssemblyClassLoader.FromAssembly(type.Assembly); - } - - private static ExModifiers GetModifiers(Type type) - { - ModifiersAttribute attr = AttributeHelper.GetModifiersAttribute(type); - if (attr != null) - { - return new ExModifiers(attr.Modifiers, attr.IsInternal); - } - // only returns public, protected, private, final, static, abstract and interface (as per - // the documentation of Class.getModifiers()) - Modifiers modifiers = 0; - if (type.IsPublic || type.IsNestedPublic) - { - modifiers |= Modifiers.Public; - } - if (type.IsSealed) - { - modifiers |= Modifiers.Final; - } - if (type.IsAbstract) - { - modifiers |= Modifiers.Abstract; - } - if (type.IsInterface) - { - modifiers |= Modifiers.Interface; - } - else - { - modifiers |= Modifiers.Super; - } - return new ExModifiers(modifiers, false); - } - - internal override bool HasStaticInitializer - { - get - { - if (!clinitMethodSet) - { - try - { - clinitMethod = type.GetMethod("__", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - } -#if STATIC_COMPILER - catch (IKVM.Reflection.MissingMemberException) { } -#endif - finally { } - clinitMethodSet = true; - } - return clinitMethod != null; - } - } - - internal override TypeWrapper[] Interfaces - { - get - { - if (interfaces == null) - { - interfaces = GetInterfaces(); - } - return interfaces; - } - } - - private TypeWrapper[] GetInterfaces() - { - // NOTE instead of getting the interfaces list from Type, we use a custom - // attribute to list the implemented interfaces, because Java reflection only - // reports the interfaces *directly* implemented by the type, not the inherited - // interfaces. This is significant for serialVersionUID calculation (for example). - ImplementsAttribute attr = AttributeHelper.GetImplements(type); - if (attr == null) - { - if (BaseTypeWrapper == CoreClasses.java.lang.Object.Wrapper) - { - return GetImplementedInterfacesAsTypeWrappers(type); - } - return TypeWrapper.EmptyArray; - } - string[] interfaceNames = attr.Interfaces; - TypeWrapper[] interfaceWrappers = new TypeWrapper[interfaceNames.Length]; - if (this.IsRemapped) - { - for (int i = 0; i < interfaceWrappers.Length; i++) - { - interfaceWrappers[i] = ClassLoaderWrapper.LoadClassCritical(interfaceNames[i]); - } - } - else - { - TypeWrapper[] typeWrappers = GetImplementedInterfacesAsTypeWrappers(type); - for (int i = 0; i < interfaceWrappers.Length; i++) - { - for (int j = 0; j < typeWrappers.Length; j++) - { - if (typeWrappers[j].Name == interfaceNames[i]) - { - interfaceWrappers[i] = typeWrappers[j]; - break; - } - } - if (interfaceWrappers[i] == null) - { -#if STATIC_COMPILER - throw new FatalCompilerErrorException(Message.UnableToResolveInterface, interfaceNames[i], this); -#else - JVM.CriticalFailure("Unable to resolve interface " + interfaceNames[i] + " on type " + this, null); -#endif - } - } - } - return interfaceWrappers; - } - - private static bool IsNestedTypeAnonymousOrLocalClass(Type type) - { - switch (type.Attributes & (TypeAttributes.SpecialName | TypeAttributes.VisibilityMask)) - { - case TypeAttributes.SpecialName | TypeAttributes.NestedPublic: - case TypeAttributes.SpecialName | TypeAttributes.NestedAssembly: - return AttributeHelper.HasEnclosingMethodAttribute(type); - default: - return false; - } - } - - private static bool IsAnnotationAttribute(Type type) - { - return type.Name.EndsWith("Attribute", StringComparison.Ordinal) - && type.IsClass - && type.BaseType.FullName == "ikvm.internal.AnnotationAttributeBase"; - } - - internal override TypeWrapper[] InnerClasses - { - get - { - List wrappers = new List(); - foreach (Type nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) - { - if (IsAnnotationAttribute(nested)) - { - // HACK it's the custom attribute we generated for a corresponding annotation, so we shouldn't surface it as an inner classes - // (we can't put a HideFromJavaAttribute on it, because we do want the class to be visible as a $Proxy) - } - else if (IsNestedTypeAnonymousOrLocalClass(nested)) - { - // anonymous and local classes are not reported as inner classes - } - else if (AttributeHelper.IsHideFromJava(nested)) - { - // ignore - } - else - { - wrappers.Add(ClassLoaderWrapper.GetWrapperFromType(nested)); - } - } - foreach (string s in AttributeHelper.GetNonNestedInnerClasses(type)) - { - wrappers.Add(GetClassLoader().LoadClassByDottedName(s)); - } - return wrappers.ToArray(); - } - } - - internal override TypeWrapper DeclaringTypeWrapper - { - get - { - if (IsNestedTypeAnonymousOrLocalClass(type)) - { - return null; - } - Type declaringType = type.DeclaringType; - if (declaringType != null) - { - return ClassLoaderWrapper.GetWrapperFromType(declaringType); - } - string decl = AttributeHelper.GetNonNestedOuterClasses(type); - if (decl != null) - { - return GetClassLoader().LoadClassByDottedName(decl); - } - return null; - } - } - - // returns true iff name is of the form "...$" - private static bool IsAnonymousClassName(string name) - { - int index = name.LastIndexOf('$') + 1; - if (index > 1 && index < name.Length) - { - while (index < name.Length) - { - if ("0123456789".IndexOf(name[index++]) == -1) - { - return false; - } - } - return true; - } - return false; - } - - // This method uses some heuristics to predict the reflective modifiers and if the prediction matches - // we can avoid storing the InnerClassesAttribute to record the modifiers. - // The heuristics are based on javac from Java 7. - internal static Modifiers PredictReflectiveModifiers(TypeWrapper tw) - { - Modifiers modifiers = Modifiers.Static | (tw.Modifiers & (Modifiers.Public | Modifiers.Abstract | Modifiers.Interface)); - // javac marks anonymous classes as final, but the InnerClasses attribute access_flags does not have the ACC_FINAL flag set - if (tw.IsFinal && !IsAnonymousClassName(tw.Name)) - { - modifiers |= Modifiers.Final; - } - // javac uses the this$0 field to store the outer instance reference for non-static inner classes - foreach (FieldWrapper fw in tw.GetFields()) - { - if (fw.Name == "this$0") - { - modifiers &= ~Modifiers.Static; - break; - } - } - return modifiers; - } - - internal override Modifiers ReflectiveModifiers - { - get - { - if (reflectiveModifiers == 0) - { - Modifiers mods; - InnerClassAttribute attr = AttributeHelper.GetInnerClass(type); - if (attr != null) - { - // the mask comes from RECOGNIZED_INNER_CLASS_MODIFIERS in src/hotspot/share/vm/classfile/classFileParser.cpp - // (minus ACC_SUPER) - mods = attr.Modifiers & (Modifiers)0x761F; - } - else if (type.DeclaringType != null) - { - mods = PredictReflectiveModifiers(this); - } - else - { - // the mask comes from JVM_RECOGNIZED_CLASS_MODIFIERS in src/hotspot/share/vm/prims/jvm.h - // (minus ACC_SUPER) - mods = Modifiers & (Modifiers)0x7611; - } - if (IsInterface) - { - mods |= Modifiers.Abstract; - } - reflectiveModifiers = mods; - } - return reflectiveModifiers; - } - } - - internal override Type TypeAsBaseType - { - get - { - return type; - } - } - - private void SigTypePatchUp(string sigtype, ref TypeWrapper type) - { - if (sigtype != type.SigName) - { - // if type is an array, we know that it is a ghost array, because arrays of unloadable are compiled - // as object (not as arrays of object) - if (type.IsArray) - { - type = GetClassLoader().FieldTypeWrapperFromSig(sigtype, LoadMode.LoadOrThrow); - } - else if (type.IsPrimitive) - { - type = DotNetTypeWrapper.GetWrapperFromDotNetType(type.TypeAsTBD); - if (sigtype != type.SigName) - { - throw new InvalidOperationException(); - } - } - else if (type.IsNonPrimitiveValueType) - { - // this can't happen and even if it does happen we cannot return - // UnloadableTypeWrapper because that would result in incorrect code - // being generated - throw new InvalidOperationException(); - } - else - { - if (sigtype[0] == 'L') - { - sigtype = sigtype.Substring(1, sigtype.Length - 2); - } - try - { - TypeWrapper tw = GetClassLoader().LoadClassByDottedNameFast(sigtype); - if (tw != null && tw.IsRemapped) - { - type = tw; - return; - } - } - catch (RetargetableJavaException) - { - } - type = new UnloadableTypeWrapper(sigtype); - } - } - } - - private static void ParseSig(string sig, out string[] sigparam, out string sigret) - { - List list = new List(); - int pos = 1; - for (; ; ) - { - switch (sig[pos]) - { - case 'L': - { - int end = sig.IndexOf(';', pos) + 1; - list.Add(sig.Substring(pos, end - pos)); - pos = end; - break; - } - case '[': - { - int skip = 1; - while (sig[pos + skip] == '[') skip++; - if (sig[pos + skip] == 'L') - { - int end = sig.IndexOf(';', pos) + 1; - list.Add(sig.Substring(pos, end - pos)); - pos = end; - } - else - { - skip++; - list.Add(sig.Substring(pos, skip)); - pos += skip; - } - break; - } - case ')': - sigparam = list.ToArray(); - sigret = sig.Substring(pos + 1); - return; - default: - list.Add(sig.Substring(pos, 1)); - pos++; - break; - } - } - } - - private static bool IsCallerID(Type type) - { -#if STUB_GENERATOR - return type.FullName == "ikvm.internal.CallerID"; -#else - return type == CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType; -#endif - } - - private static bool IsCallerSensitive(MethodBase mb) - { -#if FIRST_PASS - return false; -#elif STATIC_COMPILER || STUB_GENERATOR - foreach (CustomAttributeData cad in mb.GetCustomAttributesData()) - { - if (cad.AttributeType.FullName == "sun.reflect.CallerSensitiveAttribute") - { - return true; - } - } - return false; -#else - return mb.IsDefined(typeof(global::sun.reflect.CallerSensitiveAttribute), false); -#endif - } - - private void GetNameSigFromMethodBase(MethodBase method, out string name, out string sig, out TypeWrapper retType, out TypeWrapper[] paramTypes, ref MemberFlags flags) - { - retType = method is ConstructorInfo ? PrimitiveTypeWrapper.VOID : GetParameterTypeWrapper(((MethodInfo)method).ReturnParameter); - ParameterInfo[] parameters = method.GetParameters(); - int len = parameters.Length; - if (len > 0 - && IsCallerID(parameters[len - 1].ParameterType) - && GetClassLoader() == ClassLoaderWrapper.GetBootstrapClassLoader() - && IsCallerSensitive(method)) - { - len--; - flags |= MemberFlags.CallerID; - } - paramTypes = new TypeWrapper[len]; - for (int i = 0; i < len; i++) - { - paramTypes[i] = GetParameterTypeWrapper(parameters[i]); - } - NameSigAttribute attr = AttributeHelper.GetNameSig(method); - if (attr != null) - { - name = attr.Name; - sig = attr.Sig; - string[] sigparams; - string sigret; - ParseSig(sig, out sigparams, out sigret); - // HACK newhelper methods have a return type, but it should be void - if (name == "") - { - retType = PrimitiveTypeWrapper.VOID; - } - SigTypePatchUp(sigret, ref retType); - // if we have a remapped method, the paramTypes array contains an additional entry for "this" so we have - // to remove that - if (paramTypes.Length == sigparams.Length + 1) - { - paramTypes = ArrayUtil.DropFirst(paramTypes); - } - Debug.Assert(sigparams.Length == paramTypes.Length); - for (int i = 0; i < sigparams.Length; i++) - { - SigTypePatchUp(sigparams[i], ref paramTypes[i]); - } - } - else - { - if (method is ConstructorInfo) - { - name = method.IsStatic ? "" : ""; - } - else - { - name = method.Name; - if (name.StartsWith(NamePrefix.Bridge, StringComparison.Ordinal)) - { - name = name.Substring(NamePrefix.Bridge.Length); - } - if (method.IsSpecialName) - { - name = UnicodeUtil.UnescapeInvalidSurrogates(name); - } - } - if (method.IsSpecialName && method.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal)) - { - paramTypes = ArrayUtil.DropFirst(paramTypes); - } - System.Text.StringBuilder sb = new System.Text.StringBuilder("("); - foreach (TypeWrapper tw in paramTypes) - { - sb.Append(tw.SigName); - } - sb.Append(")"); - sb.Append(retType.SigName); - sig = sb.ToString(); - } - } - - private sealed class DelegateConstructorMethodWrapper : MethodWrapper - { - private readonly ConstructorInfo constructor; - private MethodInfo invoke; - - private DelegateConstructorMethodWrapper(TypeWrapper tw, TypeWrapper iface, ExModifiers mods) - : base(tw, StringConstants.INIT, "(" + iface.SigName + ")V", null, PrimitiveTypeWrapper.VOID, new TypeWrapper[] { iface }, mods.Modifiers, mods.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None) - { - } - - internal DelegateConstructorMethodWrapper(TypeWrapper tw, MethodBase method) - : this(tw, tw.GetClassLoader().LoadClassByDottedName(tw.Name + DotNetTypeWrapper.DelegateInterfaceSuffix), AttributeHelper.GetModifiers(method, false)) - { - constructor = (ConstructorInfo)method; - } - - protected override void DoLinkMethod() - { - MethodWrapper mw = GetParameters()[0].GetMethods()[0]; - mw.Link(); - invoke = (MethodInfo)mw.GetMethod(); - } - -#if EMITTERS - internal override void EmitNewobj(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Ldvirtftn, invoke); - ilgen.Emit(OpCodes.Newobj, constructor); - } -#endif // EMITTERS - } - - protected override void LazyPublishMethods() - { - bool isDelegate = type.BaseType == Types.MulticastDelegate; - List methods = new List(); - const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; - foreach (ConstructorInfo ctor in type.GetConstructors(flags)) - { - HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(ctor); - if (isDelegate && !ctor.IsStatic && (hideFromJavaFlags & HideFromJavaFlags.Code) == 0) - { - methods.Add(new DelegateConstructorMethodWrapper(this, ctor)); - } - else - { - AddMethodOrConstructor(ctor, hideFromJavaFlags, methods); - } - } - AddMethods(type.GetMethods(flags), methods); - if (type.IsInterface && (type.IsPublic || type.IsNestedPublic)) - { - Type privateInterfaceMethods = type.GetNestedType(NestedTypeName.PrivateInterfaceMethods, BindingFlags.NonPublic); - if (privateInterfaceMethods != null) - { - AddMethods(privateInterfaceMethods.GetMethods(flags), methods); - } - } - SetMethods(methods.ToArray()); - } - - private void AddMethods(MethodInfo[] add, List methods) - { - foreach (MethodInfo method in add) - { - AddMethodOrConstructor(method, AttributeHelper.GetHideFromJavaFlags(method), methods); - } - } - - private void AddMethodOrConstructor(MethodBase method, HideFromJavaFlags hideFromJavaFlags, List methods) - { - if ((hideFromJavaFlags & HideFromJavaFlags.Code) != 0) - { - if (method.Name.StartsWith(NamePrefix.Incomplete, StringComparison.Ordinal)) - { - SetHasIncompleteInterfaceImplementation(); - } - } - else - { - if (method.IsSpecialName && (method.Name.StartsWith("__<", StringComparison.Ordinal) || method.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal))) - { - // skip - } - else - { - string name; - string sig; - TypeWrapper retType; - TypeWrapper[] paramTypes; - MethodInfo mi = method as MethodInfo; - bool hideFromReflection = mi != null && (hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0; - MemberFlags flags = hideFromReflection ? MemberFlags.HideFromReflection : MemberFlags.None; - GetNameSigFromMethodBase(method, out name, out sig, out retType, out paramTypes, ref flags); - ExModifiers mods = AttributeHelper.GetModifiers(method, false); - if (mods.IsInternal) - { - flags |= MemberFlags.InternalAccess; - } - if (hideFromReflection && name.StartsWith(NamePrefix.AccessStub, StringComparison.Ordinal)) - { - int id = Int32.Parse(name.Substring(NamePrefix.AccessStub.Length, name.IndexOf('|', NamePrefix.AccessStub.Length) - NamePrefix.AccessStub.Length)); - name = name.Substring(name.IndexOf('|', NamePrefix.AccessStub.Length) + 1); - flags |= MemberFlags.AccessStub; - MethodInfo nonvirt = type.GetMethod(NamePrefix.NonVirtual + id, BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance); - methods.Add(new AccessStubMethodWrapper(this, name, sig, mi, mi, nonvirt ?? mi, retType, paramTypes, mods.Modifiers & ~Modifiers.Final, flags)); - return; - } - MethodInfo impl; - MethodWrapper mw; - if (IsGhost && (mods.Modifiers & (Modifiers.Static | Modifiers.Private)) == 0) - { - Type[] types = new Type[paramTypes.Length]; - for (int i = 0; i < types.Length; i++) - { - types[i] = paramTypes[i].TypeAsSignatureType; - } - MethodInfo ifmethod = TypeAsBaseType.GetMethod(method.Name, types); - mw = new GhostMethodWrapper(this, name, sig, ifmethod, (MethodInfo)method, retType, paramTypes, mods.Modifiers, flags); - if (!mw.IsAbstract) - { - ((GhostMethodWrapper)mw).SetDefaultImpl(TypeAsSignatureType.GetMethod(NamePrefix.DefaultMethod + method.Name, types)); - } - } - else if (method.IsSpecialName && method.Name.StartsWith(NamePrefix.PrivateInterfaceInstanceMethod, StringComparison.Ordinal)) - { - mw = new PrivateInterfaceMethodWrapper(this, name, sig, method, retType, paramTypes, mods.Modifiers, flags); - } - else if (IsInterface && method.IsAbstract && (mods.Modifiers & Modifiers.Abstract) == 0 && (impl = GetDefaultInterfaceMethodImpl(mi, sig)) != null) - { - mw = new DefaultInterfaceMethodWrapper(this, name, sig, mi, impl, retType, paramTypes, mods.Modifiers, flags); - } - else - { - mw = new TypicalMethodWrapper(this, name, sig, method, retType, paramTypes, mods.Modifiers, flags); - } - if (mw.HasNonPublicTypeInSignature) - { - if (mi != null) - { - MethodInfo stubVirt; - MethodInfo stubNonVirt; - if (GetType2AccessStubs(name, sig, out stubVirt, out stubNonVirt)) - { - mw = new AccessStubMethodWrapper(this, name, sig, mi, stubVirt, stubNonVirt ?? stubVirt, retType, paramTypes, mw.Modifiers, flags); - } - } - else - { - ConstructorInfo stub; - if (GetType2AccessStub(sig, out stub)) - { - mw = new AccessStubConstructorMethodWrapper(this, sig, (ConstructorInfo)method, stub, paramTypes, mw.Modifiers, flags); - } - } - } - methods.Add(mw); - } - } - } - - private MethodInfo GetDefaultInterfaceMethodImpl(MethodInfo method, string expectedSig) - { - foreach (MethodInfo candidate in method.DeclaringType.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)) - { - if (candidate.IsSpecialName - && candidate.Name.StartsWith(NamePrefix.DefaultMethod, StringComparison.Ordinal) - && candidate.Name.Length == method.Name.Length + NamePrefix.DefaultMethod.Length - && candidate.Name.EndsWith(method.Name, StringComparison.Ordinal)) - { - string name; - string sig; - TypeWrapper retType; - TypeWrapper[] paramTypes; - MemberFlags flags = MemberFlags.None; - GetNameSigFromMethodBase(candidate, out name, out sig, out retType, out paramTypes, ref flags); - if (sig == expectedSig) - { - return candidate; - } - } - } - return null; - } + Debug.Assert(!this.IsUnloadable); + Debug.Assert(this == VerifierTypeWrapper.Null || this.IsArray); - private bool GetType2AccessStubs(string name, string sig, out MethodInfo stubVirt, out MethodInfo stubNonVirt) - { - stubVirt = null; - stubNonVirt = null; - const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; - foreach (MethodInfo method in type.GetMethods(flags)) - { - if (AttributeHelper.IsHideFromJava(method)) + if (this == VerifierTypeWrapper.Null) { - NameSigAttribute attr = AttributeHelper.GetNameSig(method); - if (attr != null && attr.Name == name && attr.Sig == sig) - { - if (method.Name.StartsWith(NamePrefix.NonVirtual, StringComparison.Ordinal)) - { - stubNonVirt = method; - } - else - { - stubVirt = method; - } - } + return VerifierTypeWrapper.Null; } - } - return stubVirt != null; - } - private bool GetType2AccessStub(string sig, out ConstructorInfo stub) - { - stub = null; - const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - foreach (ConstructorInfo ctor in type.GetConstructors(flags)) - { - if (AttributeHelper.IsHideFromJava(ctor)) + // TODO consider caching the element type + switch (name[1]) { - NameSigAttribute attr = AttributeHelper.GetNameSig(ctor); - if (attr != null && attr.Sig == sig) - { - stub = ctor; - } + case '[': + // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load + // (because the ultimate element type was already loaded when this type was created) + return GetClassLoader().LoadClassByDottedNameFast(name.Substring(1)); + case 'L': + // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load + // (because the ultimate element type was already loaded when this type was created) + return GetClassLoader().LoadClassByDottedNameFast(name.Substring(2, name.Length - 3)); + case 'Z': + return PrimitiveTypeWrapper.BOOLEAN; + case 'B': + return PrimitiveTypeWrapper.BYTE; + case 'S': + return PrimitiveTypeWrapper.SHORT; + case 'C': + return PrimitiveTypeWrapper.CHAR; + case 'I': + return PrimitiveTypeWrapper.INT; + case 'J': + return PrimitiveTypeWrapper.LONG; + case 'F': + return PrimitiveTypeWrapper.FLOAT; + case 'D': + return PrimitiveTypeWrapper.DOUBLE; + default: + throw new InvalidOperationException(name); } } - return stub != null; } - private static int SortFieldByToken(FieldInfo field1, FieldInfo field2) + internal TypeWrapper MakeArrayType(int rank) { - return field1.MetadataToken.CompareTo(field2.MetadataToken); + Debug.Assert(rank != 0); + // NOTE this call to LoadClassByDottedNameFast can never fail and will not trigger a class load + return GetClassLoader().LoadClassByDottedNameFast(new String('[', rank) + this.SigName); } - protected override void LazyPublishFields() + internal bool ImplementsInterface(TypeWrapper interfaceWrapper) { - List fields = new List(); - const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; - FieldInfo[] rawfields = type.GetFields(flags); - Array.Sort(rawfields, SortFieldByToken); - // FXBUG on .NET 3.5 and Mono Type.GetProperties() will not return "duplicate" properties (i.e. that have the same name and type, but differ in custom modifiers). - // .NET 4.0 works as expected. We don't have a workaround, because that would require name mangling again and this situation is very unlikely anyway. - PropertyInfo[] properties = type.GetProperties(flags); - foreach (FieldInfo field in rawfields) + TypeWrapper typeWrapper = this; + while (typeWrapper != null) { - HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(field); - if ((hideFromJavaFlags & HideFromJavaFlags.Code) != 0) - { - if (field.Name.StartsWith(NamePrefix.Type2AccessStubBackingField, StringComparison.Ordinal)) - { - TypeWrapper tw = GetFieldTypeWrapper(field); - string name = field.Name.Substring(NamePrefix.Type2AccessStubBackingField.Length); - for (int i = 0; i < properties.Length; i++) - { - if (properties[i] != null - && name == properties[i].Name - && MatchTypes(tw, GetPropertyTypeWrapper(properties[i]))) - { - fields.Add(new CompiledAccessStubFieldWrapper(this, properties[i], field, tw)); - properties[i] = null; - break; - } - } - } - } - else + TypeWrapper[] interfaces = typeWrapper.Interfaces; + for (int i = 0; i < interfaces.Length; i++) { - if (field.IsSpecialName && field.Name.StartsWith("__<", StringComparison.Ordinal)) + if (interfaces[i] == interfaceWrapper) { - // skip + return true; } - else + if (interfaces[i].ImplementsInterface(interfaceWrapper)) { - fields.Add(CreateFieldWrapper(field, hideFromJavaFlags)); + return true; } } + typeWrapper = typeWrapper.BaseTypeWrapper; } - foreach (PropertyInfo property in properties) - { - if (property != null) - { - AddPropertyFieldWrapper(fields, property, null); - } - } - SetFields(fields.ToArray()); - } - - private static bool MatchTypes(TypeWrapper tw1, TypeWrapper tw2) - { - return tw1 == tw2 || (tw1.IsUnloadable && tw2.IsUnloadable && tw1.Name == tw2.Name); - } - - private void AddPropertyFieldWrapper(List fields, PropertyInfo property, FieldInfo field) - { - // NOTE explictly defined properties (in map.xml) are decorated with HideFromJava, - // so we don't need to worry about them here - HideFromJavaFlags hideFromJavaFlags = AttributeHelper.GetHideFromJavaFlags(property); - if ((hideFromJavaFlags & HideFromJavaFlags.Code) == 0) - { - // is it a type 1 access stub? - if ((hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0) - { - fields.Add(new CompiledAccessStubFieldWrapper(this, property, GetPropertyTypeWrapper(property))); - } - else - { - // It must be an explicit property - // (defined in Java source by an @ikvm.lang.Property annotation) - ModifiersAttribute mods = AttributeHelper.GetModifiersAttribute(property); - fields.Add(new CompiledPropertyFieldWrapper(this, property, new ExModifiers(mods.Modifiers, mods.IsInternal))); - } - } + return false; } - private sealed class CompiledRemappedMethodWrapper : SmartMethodWrapper + internal bool IsSubTypeOf(TypeWrapper baseType) { - private readonly MethodInfo mbHelper; -#if !STATIC_COMPILER - private readonly MethodInfo mbNonvirtualHelper; -#endif - - internal CompiledRemappedMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, ExModifiers modifiers, bool hideFromReflection, MethodInfo mbHelper, MethodInfo mbNonvirtualHelper) - : base(declaringType, name, sig, method, returnType, parameterTypes, modifiers.Modifiers, - (modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None) | (hideFromReflection ? MemberFlags.HideFromReflection : MemberFlags.None)) - { - this.mbHelper = mbHelper; -#if !STATIC_COMPILER - this.mbNonvirtualHelper = mbNonvirtualHelper; -#endif - } - -#if EMITTERS - protected override void CallImpl(CodeEmitter ilgen) - { - MethodBase mb = GetMethod(); - MethodInfo mi = mb as MethodInfo; - if (mi != null) - { - if (!IsStatic && IsFinal) - { - // When calling a final instance method on a remapped type from a class derived from a .NET class (i.e. a cli.System.Object or cli.System.Exception derived base class) - // then we can't call the java.lang.Object or java.lang.Throwable methods and we have to go through the instancehelper_ method. Note that since the method - // is final, this won't affect the semantics. - CallvirtImpl(ilgen); - } - else - { - ilgen.Emit(OpCodes.Call, mi); - } - } - else - { - ilgen.Emit(OpCodes.Call, mb); - } - } - - protected override void CallvirtImpl(CodeEmitter ilgen) - { - Debug.Assert(!mbHelper.IsStatic || mbHelper.Name.StartsWith("instancehelper_") || mbHelper.DeclaringType.Name == "__Helper"); - if (mbHelper.IsPublic) - { - ilgen.Emit(mbHelper.IsStatic ? OpCodes.Call : OpCodes.Callvirt, mbHelper); - } - else - { - // HACK the helper is not public, this means that we're dealing with finalize or clone - ilgen.Emit(OpCodes.Callvirt, GetMethod()); - } - } - - protected override void NewobjImpl(CodeEmitter ilgen) - { - MethodBase mb = GetMethod(); - MethodInfo mi = mb as MethodInfo; - if (mi != null) - { - Debug.Assert(mi.Name == "newhelper"); - ilgen.Emit(OpCodes.Call, mi); - } - else - { - ilgen.Emit(OpCodes.Newobj, mb); - } - } -#endif // EMITTERS - -#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR - [HideFromJava] - internal override object Invoke(object obj, object[] args) - { - MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); - if (mb.IsStatic && !IsStatic) - { - args = ArrayUtil.Concat(obj, args); - obj = null; - } - return InvokeAndUnwrapException(mb, obj, args); - } - - [HideFromJava] - internal override object CreateInstance(object[] args) - { - MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); - if (mb.IsStatic) - { - return InvokeAndUnwrapException(mb, null, args); - } - return base.CreateInstance(args); - } - - [HideFromJava] - internal override object InvokeNonvirtualRemapped(object obj, object[] args) - { - MethodInfo mi = mbNonvirtualHelper; - if (mi == null) - { - mi = mbHelper; - } - return mi.Invoke(null, ArrayUtil.Concat(obj, args)); - } -#endif // !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR - -#if EMITTERS - internal override void EmitCallvirtReflect(CodeEmitter ilgen) - { - MethodBase mb = mbHelper != null ? mbHelper : GetMethod(); - ilgen.Emit(mb.IsStatic ? OpCodes.Call : OpCodes.Callvirt, mb); - } -#endif // EMITTERS - - internal string GetGenericSignature() - { - SignatureAttribute attr = AttributeHelper.GetSignature(mbHelper != null ? mbHelper : GetMethod()); - if (attr != null) - { - return attr.Signature; - } - return null; - } - } + // make sure IsSubTypeOf isn't used on primitives + Debug.Assert(!this.IsPrimitive); + Debug.Assert(!baseType.IsPrimitive); + // can't be used on Unloadable + Debug.Assert(!this.IsUnloadable); + Debug.Assert(!baseType.IsUnloadable); - private static TypeWrapper TypeWrapperFromModOpt(Type[] modopt) - { - int rank = 0; - TypeWrapper tw = null; - foreach (Type type in modopt) + if (baseType.IsInterface) { - if (type == JVM.LoadType(typeof(IKVM.Attributes.AccessStub))) - { - // ignore - } - else if (type == Types.Array) - { - rank++; - } - else if (type == Types.Void || type.IsPrimitive || ClassLoaderWrapper.IsRemappedType(type)) - { - tw = DotNetTypeWrapper.GetWrapperFromDotNetType(type); - } - else if (type.DeclaringType != null && type.DeclaringType.FullName == UnloadableTypeWrapper.ContainerTypeName) - { - tw = new UnloadableTypeWrapper(TypeNameUtil.UnmangleNestedTypeName(type.Name), type); - } - else + if (baseType == this) { - tw = ClassLoaderWrapper.GetWrapperFromType(type); + return true; } + return ImplementsInterface(baseType); } - if (rank != 0) - { - tw = tw.MakeArrayType(rank); - } - return tw; - } - - private static TypeWrapper GetPropertyTypeWrapper(PropertyInfo property) - { - return TypeWrapperFromModOpt(property.GetOptionalCustomModifiers()) - ?? ClassLoaderWrapper.GetWrapperFromType(property.PropertyType); - } - - internal static TypeWrapper GetFieldTypeWrapper(FieldInfo field) - { - return TypeWrapperFromModOpt(field.GetOptionalCustomModifiers()) - ?? ClassLoaderWrapper.GetWrapperFromType(field.FieldType); - } - - internal static TypeWrapper GetParameterTypeWrapper(ParameterInfo param) - { - TypeWrapper tw = TypeWrapperFromModOpt(param.GetOptionalCustomModifiers()); - if (tw != null) - { - return tw; - } - Type parameterType = param.ParameterType; - if (parameterType.IsByRef) - { - // we only support ByRef parameters for automatically generated delegate invoke stubs - parameterType = parameterType.GetElementType().MakeArrayType(); - } - return ClassLoaderWrapper.GetWrapperFromType(parameterType); - } - - private FieldWrapper CreateFieldWrapper(FieldInfo field, HideFromJavaFlags hideFromJavaFlags) - { - ExModifiers modifiers = AttributeHelper.GetModifiers(field, false); - TypeWrapper type = GetFieldTypeWrapper(field); - string name = field.Name; - - if (field.IsSpecialName) + // NOTE this isn't just an optimization, it is also required when this is an interface + if (baseType == CoreClasses.java.lang.Object.Wrapper) { - name = UnicodeUtil.UnescapeInvalidSurrogates(name); + return true; } - - if (field.IsLiteral) + TypeWrapper subType = this; + while (subType != baseType) { - MemberFlags flags = MemberFlags.None; - if ((hideFromJavaFlags & HideFromJavaFlags.Reflection) != 0) - { - flags |= MemberFlags.HideFromReflection; - } - if (modifiers.IsInternal) + subType = subType.BaseTypeWrapper; + if (subType == null) { - flags |= MemberFlags.InternalAccess; + return false; } - return new ConstantFieldWrapper(this, type, name, type.SigName, modifiers.Modifiers, field, null, flags); - } - else - { - return FieldWrapper.Create(this, type, field, name, type.SigName, modifiers); - } - } - - internal override Type TypeAsTBD - { - get - { - return type; - } - } - - internal override bool IsMapUnsafeException - { - get - { - return AttributeHelper.IsExceptionIsUnsafeForMapping(type); } + return true; } -#if EMITTERS - internal override void EmitRunClassConstructor(CodeEmitter ilgen) + internal bool IsAssignableTo(TypeWrapper wrapper) { - if (HasStaticInitializer) + if (this == wrapper) { - ilgen.Emit(OpCodes.Call, clinitMethod); + return true; } - } -#endif // EMITTERS - - internal override string GetGenericSignature() - { - SignatureAttribute attr = AttributeHelper.GetSignature(type); - if (attr != null) + if (this.IsPrimitive || wrapper.IsPrimitive) { - return attr.Signature; + return false; } - return null; - } - - internal override string GetGenericMethodSignature(MethodWrapper mw) - { - if (mw is CompiledRemappedMethodWrapper) + if (this == VerifierTypeWrapper.Null) { - return ((CompiledRemappedMethodWrapper)mw).GetGenericSignature(); + return true; } - MethodBase mb = mw.GetMethod(); - if (mb != null) + if (wrapper.IsInterface) + { + return ImplementsInterface(wrapper); + } + int rank1 = this.ArrayRank; + int rank2 = wrapper.ArrayRank; + if (rank1 > 0 && rank2 > 0) { - SignatureAttribute attr = AttributeHelper.GetSignature(mb); - if (attr != null) + rank1--; + rank2--; + TypeWrapper elem1 = this.ElementTypeWrapper; + TypeWrapper elem2 = wrapper.ElementTypeWrapper; + while (rank1 != 0 && rank2 != 0) + { + elem1 = elem1.ElementTypeWrapper; + elem2 = elem2.ElementTypeWrapper; + rank1--; + rank2--; + } + if (elem1.IsPrimitive || elem2.IsPrimitive) { - return attr.Signature; + return false; } + return (!elem1.IsNonPrimitiveValueType && elem1.IsSubTypeOf(elem2)); } - return null; + return this.IsSubTypeOf(wrapper); } - internal override string GetGenericFieldSignature(FieldWrapper fw) +#if !IMPORTER && !EXPORTER + internal bool IsInstance(object obj) { - FieldInfo fi = fw.GetField(); - if (fi != null) + if (obj != null) { - SignatureAttribute attr = AttributeHelper.GetSignature(fi); - if (attr != null) - { - return attr.Signature; - } + TypeWrapper thisWrapper = this; + TypeWrapper objWrapper = IKVM.Java.Externs.ikvm.runtime.Util.GetTypeWrapperFromObject(obj); + return objWrapper.IsAssignableTo(thisWrapper); } - return null; + return false; } +#endif - internal override MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) + internal virtual TypeWrapper[] Interfaces { - MethodBase mb = mw.GetMethod(); - if (mb == null) - { - // delegate constructor - return null; - } - MethodParametersAttribute attr = AttributeHelper.GetMethodParameters(mb); - if (attr == null) - { - return null; - } - if (attr.IsMalformed) - { - return MethodParametersEntry.Malformed; - } - ParameterInfo[] parameters = mb.GetParameters(); - MethodParametersEntry[] mp = new MethodParametersEntry[attr.Modifiers.Length]; - for (int i = 0; i < mp.Length; i++) - { - mp[i].name = i < parameters.Length ? parameters[i].Name : null; - mp[i].flags = (ushort)attr.Modifiers[i]; - } - return mp; + get { return EmptyArray; } } -#if !STATIC_COMPILER && !STUB_GENERATOR - internal override string[] GetEnclosingMethod() + // NOTE this property can only be called for finished types! + internal virtual TypeWrapper[] InnerClasses { - EnclosingMethodAttribute enc = AttributeHelper.GetEnclosingMethodAttribute(type); - if (enc != null) - { - return new string[] { enc.ClassName, enc.MethodName, enc.MethodSignature }; - } - return null; + get { return EmptyArray; } } - internal override object[] GetDeclaredAnnotations() + // NOTE this property can only be called for finished types! + internal virtual TypeWrapper DeclaringTypeWrapper { - return type.GetCustomAttributes(false); + get { return null; } } - internal override object[] GetMethodAnnotations(MethodWrapper mw) + internal virtual void Finish() { - MethodBase mb = mw.GetMethod(); - if (mb == null) - { - // delegate constructor - return null; - } - return mb.GetCustomAttributes(false); } - internal override object[][] GetParameterAnnotations(MethodWrapper mw) + internal void LinkAll() { - MethodBase mb = mw.GetMethod(); - if (mb == null) - { - // delegate constructor - return null; - } - ParameterInfo[] parameters = mb.GetParameters(); - int skip = 0; - if (mb.IsStatic && !mw.IsStatic && mw.Name != "") - { - skip = 1; - } - int skipEnd = 0; - if (mw.HasCallerID) - { - skipEnd = 1; - } - object[][] attribs = new object[parameters.Length - skip - skipEnd][]; - for (int i = skip; i < parameters.Length - skipEnd; i++) + if ((flags & TypeFlags.Linked) == 0) { - attribs[i - skip] = parameters[i].GetCustomAttributes(false); + TypeWrapper tw = BaseTypeWrapper; + if (tw != null) + { + tw.LinkAll(); + } + foreach (TypeWrapper iface in Interfaces) + { + iface.LinkAll(); + } + foreach (MethodWrapper mw in GetMethods()) + { + mw.Link(); + } + foreach (FieldWrapper fw in GetFields()) + { + fw.Link(); + } + SetTypeFlag(TypeFlags.Linked); } - return attribs; } - internal override object[] GetFieldAnnotations(FieldWrapper fw) +#if !IMPORTER + [Conditional("DEBUG")] + internal static void AssertFinished(Type type) { - FieldInfo field = fw.GetField(); - if (field != null) - { - return field.GetCustomAttributes(false); - } - CompiledPropertyFieldWrapper prop = fw as CompiledPropertyFieldWrapper; - if (prop != null) + if (type != null) { - return prop.GetProperty().GetCustomAttributes(false); + while (type.HasElementType) + { + type = type.GetElementType(); + } + Debug.Assert(!(type is TypeBuilder)); } - return new object[0]; } #endif - internal sealed class CompiledAnnotation : Annotation +#if !IMPORTER && !EXPORTER + internal void RunClassInit() { - private readonly ConstructorInfo constructor; - - internal CompiledAnnotation(Type type) + Type t = IsRemapped ? TypeAsBaseType : TypeAsTBD; + if (t != null) { - constructor = type.GetConstructor(new Type[] { JVM.Import(typeof(object[])) }); + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(t.TypeHandle); } + } +#endif - private CustomAttributeBuilder MakeCustomAttributeBuilder(ClassLoaderWrapper loader, object annotation) - { - return new CustomAttributeBuilder(constructor, new object[] { AnnotationDefaultAttribute.Escape(QualifyClassNames(loader, annotation)) }); - } +#if EMITTERS + internal void EmitUnbox(CodeEmitter ilgen) + { + Debug.Assert(this.IsNonPrimitiveValueType); - internal override void Apply(ClassLoaderWrapper loader, TypeBuilder tb, object annotation) - { - tb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + ilgen.EmitUnboxSpecial(this.TypeAsTBD); + } - internal override void Apply(ClassLoaderWrapper loader, MethodBuilder mb, object annotation) - { - mb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + internal void EmitBox(CodeEmitter ilgen) + { + Debug.Assert(this.IsNonPrimitiveValueType); - internal override void Apply(ClassLoaderWrapper loader, FieldBuilder fb, object annotation) - { - fb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); - } + ilgen.Emit(OpCodes.Box, this.TypeAsTBD); + } - internal override void Apply(ClassLoaderWrapper loader, ParameterBuilder pb, object annotation) + internal void EmitConvSignatureTypeToStackType(CodeEmitter ilgen) + { + if (IsUnloadable) { - pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); } - - internal override void Apply(ClassLoaderWrapper loader, AssemblyBuilder ab, object annotation) + else if (this == PrimitiveTypeWrapper.BYTE) { - ab.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + ilgen.Emit(OpCodes.Conv_I1); } - - internal override void Apply(ClassLoaderWrapper loader, PropertyBuilder pb, object annotation) + else if (IsNonPrimitiveValueType) { - pb.SetCustomAttribute(MakeCustomAttributeBuilder(loader, annotation)); + EmitBox(ilgen); } - - internal override bool IsCustomAttribute + else if (IsGhost) { - get { return false; } + CodeEmitterLocal local = ilgen.DeclareLocal(TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, local); + ilgen.Emit(OpCodes.Ldloca, local); + ilgen.Emit(OpCodes.Ldfld, GhostRefField); } } - internal override Annotation Annotation + // NOTE sourceType is optional and only used for interfaces, + // it is *not* used to automatically downcast + internal void EmitConvStackTypeToSignatureType(CodeEmitter ilgen, TypeWrapper sourceType) { - get + if (!IsUnloadable) { - string annotationAttribute = AttributeHelper.GetAnnotationAttributeType(type); - if (annotationAttribute != null) + if (IsGhost) { - return new CompiledAnnotation(type.Assembly.GetType(annotationAttribute, true)); + CodeEmitterLocal local1 = ilgen.DeclareLocal(TypeAsLocalOrStackType); + ilgen.Emit(OpCodes.Stloc, local1); + CodeEmitterLocal local2 = ilgen.DeclareLocal(TypeAsSignatureType); + ilgen.Emit(OpCodes.Ldloca, local2); + ilgen.Emit(OpCodes.Ldloc, local1); + ilgen.Emit(OpCodes.Stfld, GhostRefField); + ilgen.Emit(OpCodes.Ldloca, local2); + ilgen.Emit(OpCodes.Ldobj, TypeAsSignatureType); } - return null; - } - } - - internal override Type EnumType - { - get - { - if ((this.Modifiers & Modifiers.Enum) != 0) + // because of the way interface merging works, any reference is valid + // for any interface reference + else if (IsInterfaceOrInterfaceArray && (sourceType == null || sourceType.IsUnloadable || !sourceType.IsAssignableTo(this))) { - return type.GetNestedType("__Enum"); + ilgen.EmitAssertType(TypeAsTBD); + Profiler.Count("InterfaceDownCast"); + } + else if (IsNonPrimitiveValueType) + { + EmitUnbox(ilgen); + } + else if (sourceType != null && sourceType.IsUnloadable) + { + ilgen.Emit(OpCodes.Castclass, TypeAsSignatureType); } - return null; - } - } - -#if !STATIC_COMPILER && !STUB_GENERATOR - internal override string GetSourceFileName() - { - object[] attr = type.GetCustomAttributes(typeof(SourceFileAttribute), false); - if (attr.Length == 1) - { - return ((SourceFileAttribute)attr[0]).SourceFile; - } - if (DeclaringTypeWrapper != null) - { - return DeclaringTypeWrapper.GetSourceFileName(); - } - if (IsNestedTypeAnonymousOrLocalClass(type)) - { - return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).GetSourceFileName(); - } - if (type.Module.IsDefined(typeof(SourceFileAttribute), false)) - { - return type.Name + ".java"; } - return null; } - internal override int GetSourceLineNumber(MethodBase mb, int ilOffset) + internal virtual void EmitCheckcast(CodeEmitter ilgen) { - object[] attr = mb.GetCustomAttributes(typeof(LineNumberTableAttribute), false); - if (attr.Length == 1) + if (IsGhost) { - return ((LineNumberTableAttribute)attr[0]).GetLineNumber(ilOffset); + ilgen.Emit(OpCodes.Dup); + // TODO make sure we get the right "Cast" method and cache it + // NOTE for dynamic ghosts we don't end up here because AotTypeWrapper overrides this method, + // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) + ilgen.Emit(OpCodes.Call, TypeAsTBD.GetMethod("Cast")); + ilgen.Emit(OpCodes.Pop); } - return -1; - } -#endif - - internal override bool IsFastClassLiteralSafe - { - get { return true; } - } - - internal override object[] GetConstantPool() - { - return AttributeHelper.GetConstantPool(type); - } - - internal override byte[] GetRawTypeAnnotations() - { - return AttributeHelper.GetRuntimeVisibleTypeAnnotations(type); - } - - internal override byte[] GetMethodRawTypeAnnotations(MethodWrapper mw) - { - MethodBase mb = mw.GetMethod(); - return mb == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(mb); - } - - internal override byte[] GetFieldRawTypeAnnotations(FieldWrapper fw) - { - FieldInfo fi = fw.GetField(); - return fi == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(fi); - } - } - - sealed class ArrayTypeWrapper : TypeWrapper - { - private static volatile TypeWrapper[] interfaces; - private static volatile MethodInfo clone; - private readonly TypeWrapper ultimateElementTypeWrapper; - private Type arrayType; - private bool finished; - - internal ArrayTypeWrapper(TypeWrapper ultimateElementTypeWrapper, string name) - : base(ultimateElementTypeWrapper.IsInternal ? TypeFlags.InternalAccess : TypeFlags.None, - Modifiers.Final | Modifiers.Abstract | (ultimateElementTypeWrapper.Modifiers & Modifiers.Public), name) - { - Debug.Assert(!ultimateElementTypeWrapper.IsArray); - this.ultimateElementTypeWrapper = ultimateElementTypeWrapper; - } - - internal override TypeWrapper BaseTypeWrapper - { - get { return CoreClasses.java.lang.Object.Wrapper; } - } - - internal override ClassLoaderWrapper GetClassLoader() - { - return ultimateElementTypeWrapper.GetClassLoader(); - } - - internal static MethodInfo CloneMethod - { - get + else if (IsGhostArray) { - if (clone == null) + ilgen.Emit(OpCodes.Dup); + // TODO make sure we get the right "CastArray" method and cache it + // NOTE for dynamic ghosts we don't end up here because AotTypeWrapper overrides this method, + // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) + TypeWrapper tw = this; + int rank = 0; + while (tw.IsArray) { - clone = Types.Array.GetMethod("Clone", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); + rank++; + tw = tw.ElementTypeWrapper; } - return clone; - } - } - - protected override void LazyPublishMembers() - { - MethodWrapper mw = new SimpleCallMethodWrapper(this, "clone", "()Ljava.lang.Object;", CloneMethod, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, Modifiers.Public, MemberFlags.HideFromReflection, SimpleOpCode.Callvirt, SimpleOpCode.Callvirt); - mw.Link(); - SetMethods(new MethodWrapper[] { mw }); - SetFields(FieldWrapper.EmptyArray); - } - - internal override Modifiers ReflectiveModifiers - { - get - { - return Modifiers.Final | Modifiers.Abstract | (ultimateElementTypeWrapper.ReflectiveModifiers & Modifiers.AccessMask); + ilgen.EmitLdc_I4(rank); + ilgen.Emit(OpCodes.Call, tw.TypeAsTBD.GetMethod("CastArray")); + ilgen.Emit(OpCodes.Castclass, ArrayTypeWrapper.MakeArrayType(Types.Object, rank)); } - } - - internal override string SigName - { - get + else { - // for arrays the signature name is the same as the normal name - return Name; + ilgen.EmitCastclass(TypeAsTBD); } } - internal override TypeWrapper[] Interfaces + internal virtual void EmitInstanceOf(CodeEmitter ilgen) { - get + if (IsGhost) { - if (interfaces == null) - { - TypeWrapper[] tw = new TypeWrapper[2]; - tw[0] = CoreClasses.java.lang.Cloneable.Wrapper; - tw[1] = CoreClasses.java.io.Serializable.Wrapper; - interfaces = tw; - } - return interfaces; + // TODO make sure we get the right "IsInstance" method and cache it + // NOTE for dynamic ghosts we don't end up here because DynamicTypeWrapper overrides this method, + // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) + ilgen.Emit(OpCodes.Call, TypeAsTBD.GetMethod("IsInstance")); } - } - - internal override Type TypeAsTBD - { - get + else if (IsGhostArray) { - while (arrayType == null) + // TODO make sure we get the right "IsInstanceArray" method and cache it + // NOTE for dynamic ghosts we don't end up here because DynamicTypeWrapper overrides this method, + // so we're safe to call GetMethod on TypeAsTBD (because it has to be a compiled type, if we're here) + TypeWrapper tw = this; + int rank = 0; + while (tw.IsArray) { - bool prevFinished = finished; - Type type = MakeArrayType(ultimateElementTypeWrapper.TypeAsArrayType, this.ArrayRank); - if (prevFinished) - { - // We were already finished prior to the call to MakeArrayType, so we can safely - // set arrayType to the finished type. - // Note that this takes advantage of the fact that once we've been finished, - // we can never become unfinished. - arrayType = type; - } - else - { - lock (this) - { - // To prevent a race with Finish, we can only set arrayType in this case - // (inside the locked region) if we've not already finished. If we have - // finished, we need to rerun MakeArrayType on the now finished element type. - // Note that there is a benign race left, because it is possible that another - // thread finishes right after we've set arrayType and exited the locked - // region. This is not problem, because TypeAsTBD is only guaranteed to - // return a finished type *after* Finish has been called. - if (!finished) - { - arrayType = type; - } - } - } + rank++; + tw = tw.ElementTypeWrapper; } - return arrayType; + ilgen.EmitLdc_I4(rank); + ilgen.Emit(OpCodes.Call, tw.TypeAsTBD.GetMethod("IsInstanceArray")); } - } - - internal override void Finish() - { - if (!finished) + else { - ultimateElementTypeWrapper.Finish(); - lock (this) - { - // Now that we've finished the element type, we must clear arrayType, - // because it may still refer to a TypeBuilder. Note that we have to - // do this atomically with setting "finished", to prevent a race - // with TypeAsTBD. - finished = true; - arrayType = null; - } + ilgen.Emit_instanceof(TypeAsTBD); } } - internal override bool IsFastClassLiteralSafe - { - // here we have to deal with the somewhat strange fact that in Java you cannot represent primitive type class literals, - // but you can represent arrays of primitive types as a class literal - get { return ultimateElementTypeWrapper.IsFastClassLiteralSafe || ultimateElementTypeWrapper.IsPrimitive; } - } - - internal override TypeWrapper GetUltimateElementTypeWrapper() + /// + /// Emits the appropriate instruction to do an indirect load of a reference to this type. + /// + /// + internal virtual void EmitLdind(CodeEmitter il) { - return ultimateElementTypeWrapper; + if (this == PrimitiveTypeWrapper.BOOLEAN) + il.Emit(OpCodes.Ldind_U1); + else if (this == PrimitiveTypeWrapper.BYTE) + il.Emit(OpCodes.Ldind_U1); + else if (this == PrimitiveTypeWrapper.CHAR) + il.Emit(OpCodes.Ldind_U2); + else if (this == PrimitiveTypeWrapper.SHORT) + il.Emit(OpCodes.Ldind_I2); + else if (this == PrimitiveTypeWrapper.INT) + il.Emit(OpCodes.Ldind_I4); + else if (this == PrimitiveTypeWrapper.LONG) + il.Emit(OpCodes.Ldind_I8); + else if (this == PrimitiveTypeWrapper.FLOAT) + il.Emit(OpCodes.Ldind_R4); + else if (this == PrimitiveTypeWrapper.DOUBLE) + il.Emit(OpCodes.Ldind_R8); + else + il.Emit(OpCodes.Ldind_Ref); } - internal static Type MakeArrayType(Type type, int dims) + /// + /// Emits the appropriate instruction to do an indirect store of a reference to this type. + /// + /// + internal virtual void EmitStind(CodeEmitter il) { - // NOTE this is not just an optimization, but it is also required to - // make sure that ReflectionOnly types stay ReflectionOnly types - // (in particular instantiations of generic types from mscorlib that - // have ReflectionOnly type parameters). - for (int i = 0; i < dims; i++) - { - type = type.MakeArrayType(); - } - return type; + if (this == PrimitiveTypeWrapper.BOOLEAN) + il.Emit(OpCodes.Stind_I1); + else if (this == PrimitiveTypeWrapper.BYTE) + il.Emit(OpCodes.Stind_I1); + else if (this == PrimitiveTypeWrapper.CHAR) + il.Emit(OpCodes.Stind_I2); + else if (this == PrimitiveTypeWrapper.SHORT) + il.Emit(OpCodes.Stind_I2); + else if (this == PrimitiveTypeWrapper.INT) + il.Emit(OpCodes.Stind_I4); + else if (this == PrimitiveTypeWrapper.LONG) + il.Emit(OpCodes.Stind_I8); + else if (this == PrimitiveTypeWrapper.FLOAT) + il.Emit(OpCodes.Stind_R4); + else if (this == PrimitiveTypeWrapper.DOUBLE) + il.Emit(OpCodes.Stind_R8); + else + il.Emit(OpCodes.Stind_Ref); } - } - // this is a container for the special verifier TypeWrappers - sealed class VerifierTypeWrapper : TypeWrapper - { - // the TypeWrapper constructor interns the name, so we have to pre-intern here to make sure we have the same string object - // (if it has only been interned previously) - private static readonly string This = string.Intern("this"); - private static readonly string New = string.Intern("new"); - private static readonly string Fault = string.Intern(""); - internal static readonly TypeWrapper Invalid = null; - internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null, null); - internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null, null); - internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper(""); - internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("", 0, null, null); - internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("", 0, null, null); - - private readonly int index; - private readonly TypeWrapper underlyingType; - private readonly MethodAnalyzer methodAnalyzer; - -#if STUB_GENERATOR - internal class MethodAnalyzer - { - internal void ClearFaultBlockException(int dummy) { } - } #endif - public override string ToString() + // NOTE don't call this method, call MethodWrapper.Link instead + internal virtual MethodBase LinkMethod(MethodWrapper mw) { - return GetType().Name + "[" + Name + "," + index + "," + underlyingType + "]"; + return mw.GetMethod(); } - internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex) + // NOTE don't call this method, call FieldWrapper.Link instead + internal virtual FieldInfo LinkField(FieldWrapper fw) { - return new VerifierTypeWrapper(New, bytecodeIndex, type, null); + return fw.GetField(); } - internal static TypeWrapper MakeFaultBlockException(MethodAnalyzer ma, int handlerIndex) +#if EMITTERS + internal virtual void EmitRunClassConstructor(CodeEmitter ilgen) { - return new VerifierTypeWrapper(Fault, handlerIndex, null, ma); } +#endif // EMITTERS - // NOTE the "this" type is special, it can only exist in local[0] and on the stack - // as soon as the type on the stack is merged or popped it turns into its underlying type. - // It exists to capture the verification rules for non-virtual base class method invocation in .NET 2.0, - // which requires that the invocation is done on a "this" reference that was directly loaded onto the - // stack (using ldarg_0). - internal static TypeWrapper MakeThis(TypeWrapper type) + internal virtual string GetGenericSignature() { - return new VerifierTypeWrapper(This, 0, type, null); + return null; } - internal static bool IsNotPresentOnStack(TypeWrapper w) + internal virtual string GetGenericMethodSignature(MethodWrapper mw) { - return IsNew(w) || IsFaultBlockException(w); + return null; } - internal static bool IsNew(TypeWrapper w) + internal virtual string GetGenericFieldSignature(FieldWrapper fw) { - return w != null && w.IsVerifierType && ReferenceEquals(w.Name, New); + return null; } - internal static bool IsFaultBlockException(TypeWrapper w) + internal virtual MethodParametersEntry[] GetMethodParameters(MethodWrapper mw) { - return w != null && w.IsVerifierType && ReferenceEquals(w.Name, Fault); + return null; } - internal static bool IsNullOrUnloadable(TypeWrapper w) +#if !IMPORTER && !EXPORTER + internal virtual string[] GetEnclosingMethod() { - return w == Null || w.IsUnloadable; + return null; } - internal static bool IsThis(TypeWrapper w) + internal virtual object[] GetDeclaredAnnotations() { - return w != null && w.IsVerifierType && ReferenceEquals(w.Name, This); + return null; } - internal static void ClearFaultBlockException(TypeWrapper w) + internal virtual object[] GetMethodAnnotations(MethodWrapper mw) { - VerifierTypeWrapper vtw = (VerifierTypeWrapper)w; - vtw.methodAnalyzer.ClearFaultBlockException(vtw.Index); + return null; } - internal int Index + internal virtual object[][] GetParameterAnnotations(MethodWrapper mw) { - get - { - return index; - } + return null; } - internal TypeWrapper UnderlyingType + internal virtual object[] GetFieldAnnotations(FieldWrapper fw) { - get - { - return underlyingType; - } + return null; } - private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType, MethodAnalyzer methodAnalyzer) - : base(TypeFlags.None, TypeWrapper.VerifierTypeModifiersHack, name) + internal virtual string GetSourceFileName() { - this.index = index; - this.underlyingType = underlyingType; - this.methodAnalyzer = methodAnalyzer; + return null; } - internal override TypeWrapper BaseTypeWrapper + internal virtual int GetSourceLineNumber(MethodBase mb, int ilOffset) { - get { return null; } + return -1; } - internal override ClassLoaderWrapper GetClassLoader() + internal virtual object GetAnnotationDefault(MethodWrapper mw) { + MethodBase mb = mw.GetMethod(); + if (mb != null) + { + object[] attr = mb.GetCustomAttributes(typeof(AnnotationDefaultAttribute), false); + if (attr.Length == 1) + { + return JVM.NewAnnotationElementValue(mw.DeclaringType.GetClassLoader().GetJavaClassLoader(), mw.ReturnType.ClassObject, ((AnnotationDefaultAttribute)attr[0]).Value); + } + } return null; } +#endif // !IMPORTER && !EXPORTER - protected override void LazyPublishMembers() + internal virtual Annotation Annotation { - throw new InvalidOperationException("LazyPublishMembers called on " + this); + get + { + return null; + } } - internal override Type TypeAsTBD + internal virtual Type EnumType { get { - throw new InvalidOperationException("get_Type called on " + this); + return null; } } - internal override TypeWrapper[] Interfaces + private static Type[] GetInterfaces(Type type) { - get +#if IMPORTER || EXPORTER + List list = new List(); + for (; type != null && !type.__IsMissing; type = type.BaseType) { - throw new InvalidOperationException("get_Interfaces called on " + this); + AddInterfaces(list, type); } + return list.ToArray(); +#else + return type.GetInterfaces(); +#endif } - internal override TypeWrapper[] InnerClasses +#if IMPORTER || EXPORTER + private static void AddInterfaces(List list, Type type) { - get + foreach (Type iface in type.__GetDeclaredInterfaces()) { - throw new InvalidOperationException("get_InnerClasses called on " + this); + if (!list.Contains(iface)) + { + list.Add(iface); + if (!iface.__IsMissing) + { + AddInterfaces(list, iface); + } + } } } +#endif - internal override TypeWrapper DeclaringTypeWrapper + protected static TypeWrapper[] GetImplementedInterfacesAsTypeWrappers(Type type) { - get + Type[] interfaceTypes = GetInterfaces(type); + TypeWrapper[] interfaces = new TypeWrapper[interfaceTypes.Length]; + for (int i = 0; i < interfaceTypes.Length; i++) + { + Type decl = interfaceTypes[i].DeclaringType; + if (decl != null && AttributeHelper.IsGhostInterface(decl)) + { + // we have to return the declaring type for ghost interfaces + interfaces[i] = ClassLoaderWrapper.GetWrapperFromType(decl); + } + else + { + interfaces[i] = ClassLoaderWrapper.GetWrapperFromType(interfaceTypes[i]); + } + } + for (int i = 0; i < interfaceTypes.Length; i++) { - throw new InvalidOperationException("get_DeclaringTypeWrapper called on " + this); + if (interfaces[i].IsRemapped) + { + // for remapped interfaces, we also return the original interface (Java types will ignore it, if it isn't listed in the ImplementsAttribute) + TypeWrapper twRemapped = interfaces[i]; + TypeWrapper tw = DotNetTypeWrapper.GetWrapperFromDotNetType(interfaceTypes[i]); + interfaces[i] = tw; + if (Array.IndexOf(interfaces, twRemapped) == -1) + { + interfaces = ArrayUtil.Concat(interfaces, twRemapped); + } + } } + return interfaces; } - internal override void Finish() + internal TypeWrapper GetPublicBaseTypeWrapper() { - throw new InvalidOperationException("Finish called on " + this); + Debug.Assert(!this.IsPublic); + if (this.IsUnloadable || this.IsInterface) + { + return CoreClasses.java.lang.Object.Wrapper; + } + for (TypeWrapper tw = this; ; tw = tw.BaseTypeWrapper) + { + if (tw.IsPublic) + { + return tw; + } + } } - } -#if !STATIC_COMPILER && !STUB_GENERATOR - // this represents an intrinsified anonymous class (currently used only by LambdaMetafactory) - sealed class AnonymousTypeWrapper : TypeWrapper - { - private readonly Type type; +#if !EXPORTER + // return the constructor used for automagic .NET serialization + internal virtual MethodBase GetSerializationConstructor() + { + return this.TypeAsBaseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { + JVM.Import(typeof(System.Runtime.Serialization.SerializationInfo)), JVM.Import(typeof(System.Runtime.Serialization.StreamingContext)) }, null); + } - internal AnonymousTypeWrapper(Type type) - : base(TypeFlags.Anonymous, Modifiers.Final | Modifiers.Synthetic, GetName(type)) + internal virtual MethodBase GetBaseSerializationConstructor() { - this.type = type; + return BaseTypeWrapper.GetSerializationConstructor(); } +#endif - internal static bool IsAnonymous(Type type) +#if !IMPORTER && !EXPORTER + internal virtual object GhostWrap(object obj) { - return type.IsSpecialName - && type.Name.StartsWith(NestedTypeName.IntrinsifiedAnonymousClass, StringComparison.Ordinal) - && AttributeHelper.IsJavaModule(type.Module); + return obj; } - private static string GetName(Type type) + internal virtual object GhostUnwrap(object obj) { - return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).Name - + type.Name.Replace(NestedTypeName.IntrinsifiedAnonymousClass, "$$Lambda$"); + return obj; } +#endif - internal override ClassLoaderWrapper GetClassLoader() + internal bool IsDynamic { - return ClassLoaderWrapper.GetWrapperFromType(type.DeclaringType).GetClassLoader(); +#if EXPORTER + get { return false; } +#else + get { return this is DynamicTypeWrapper; } +#endif } - internal override Type TypeAsTBD + internal virtual object[] GetConstantPool() { - get { return type; } + return null; } - internal override TypeWrapper BaseTypeWrapper + internal virtual byte[] GetRawTypeAnnotations() { - get { return CoreClasses.java.lang.Object.Wrapper; } + return null; } - internal override TypeWrapper[] Interfaces + internal virtual byte[] GetMethodRawTypeAnnotations(MethodWrapper mw) { - get - { - TypeWrapper[] interfaces = GetImplementedInterfacesAsTypeWrappers(type); - if (type.IsSerializable) - { - // we have to remove the System.Runtime.Serialization.ISerializable interface - List list = new List(interfaces); - list.RemoveAll(Serialization.IsISerializable); - return list.ToArray(); - } - return interfaces; - } + return null; } - protected override void LazyPublishMembers() + internal virtual byte[] GetFieldRawTypeAnnotations(FieldWrapper fw) { - List methods = new List(); - foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) - { - if (mi.IsSpecialName) - { - // we use special name to hide default methods - } - else if (mi.IsPublic) - { - TypeWrapper returnType; - TypeWrapper[] parameterTypes; - string signature; - GetSig(mi, out returnType, out parameterTypes, out signature); - methods.Add(new TypicalMethodWrapper(this, mi.Name, signature, mi, returnType, parameterTypes, Modifiers.Public, MemberFlags.None)); - } - else if (mi.Name == "writeReplace") - { - methods.Add(new TypicalMethodWrapper(this, "writeReplace", "()Ljava.lang.Object;", mi, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, - Modifiers.Private | Modifiers.Final, MemberFlags.None)); - } - } - SetMethods(methods.ToArray()); - List fields = new List(); - foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) - { - TypeWrapper fieldType = CompiledTypeWrapper.GetFieldTypeWrapper(fi); - fields.Add(new SimpleFieldWrapper(this, fieldType, fi, fi.Name, fieldType.SigName, new ExModifiers(Modifiers.Private | Modifiers.Final, false))); - } - SetFields(fields.ToArray()); + return null; } - private void GetSig(MethodInfo mi, out TypeWrapper returnType, out TypeWrapper[] parameterTypes, out string signature) +#if !IMPORTER && !EXPORTER + internal virtual TypeWrapper Host { - returnType = CompiledTypeWrapper.GetParameterTypeWrapper(mi.ReturnParameter); - ParameterInfo[] parameters = mi.GetParameters(); - parameterTypes = new TypeWrapper[parameters.Length]; - System.Text.StringBuilder sb = new System.Text.StringBuilder("("); - for (int i = 0; i < parameters.Length; i++) - { - parameterTypes[i] = CompiledTypeWrapper.GetParameterTypeWrapper(parameters[i]); - sb.Append(parameterTypes[i].SigName); - } - sb.Append(')'); - sb.Append(returnType.SigName); - signature = sb.ToString(); + get { return null; } } +#endif } -#endif // !STATIC_COMPILER && !STUB_GENERATOR + } diff --git a/src/IKVM.Runtime/TypeWrapperFactory.cs b/src/IKVM.Runtime/TypeWrapperFactory.cs index 1d36e5ca80..dba010a90f 100644 --- a/src/IKVM.Runtime/TypeWrapperFactory.cs +++ b/src/IKVM.Runtime/TypeWrapperFactory.cs @@ -29,7 +29,7 @@ Jeroen Frijters using System.Runtime.Loader; #endif -#if STATIC_COMPILER || STUB_GENERATOR +#if IMPORTER || EXPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; @@ -44,7 +44,7 @@ Jeroen Frijters namespace IKVM.Internal { -#if !STUB_GENERATOR +#if !EXPORTER abstract class TypeWrapperFactory { diff --git a/src/IKVM.Runtime/Types.cs b/src/IKVM.Runtime/Types.cs index 735d89432c..7b2718bb11 100644 --- a/src/IKVM.Runtime/Types.cs +++ b/src/IKVM.Runtime/Types.cs @@ -22,7 +22,10 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER || STUB_GENERATOR + +using IKVM.Runtime; + +#if IMPORTER || EXPORTER using Type = IKVM.Reflection.Type; #endif @@ -60,7 +63,6 @@ static class Types internal static readonly Type Double = JVM.Import(typeof(System.Double)); internal static readonly Type IsVolatile = JVM.Import(typeof(System.Runtime.CompilerServices.IsVolatile)); - internal static readonly Type SecurityAttribute = JVM.Import(typeof(System.Security.Permissions.SecurityAttribute)); static Types() { diff --git a/src/IKVM.Runtime/TypicalMethodWrapper.cs b/src/IKVM.Runtime/TypicalMethodWrapper.cs new file mode 100644 index 0000000000..d1f96eab13 --- /dev/null +++ b/src/IKVM.Runtime/TypicalMethodWrapper.cs @@ -0,0 +1,83 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using IKVM.Attributes; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + sealed class TypicalMethodWrapper : SmartMethodWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal TypicalMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags) : + base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags) + { + + } + +#if EMITTERS + + protected override void CallImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, GetMethod()); + } + + protected override void CallvirtImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Callvirt, GetMethod()); + } + + protected override void NewobjImpl(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Newobj, GetMethod()); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/UnicodeUtil.cs b/src/IKVM.Runtime/UnicodeUtil.cs new file mode 100644 index 0000000000..df47087142 --- /dev/null +++ b/src/IKVM.Runtime/UnicodeUtil.cs @@ -0,0 +1,122 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + static class UnicodeUtil + { + // We use part of the Supplementary Private Use Area-B to encode + // invalid surrogates. If we encounter either of these two + // markers, we always encode the surrogate (single or pair) + private const char HighSurrogatePrefix = '\uDBFF'; + private const char LowSurrogatePrefix = '\uDBFE'; + + // Identifiers in ECMA CLI metadata and strings in custom attribute blobs are encoded + // using UTF-8 and don't allow partial surrogates, so we have to "complete" them to + // produce valid Unicode and reverse the process when we read back the names. + internal static string EscapeInvalidSurrogates(string str) + { + if (str != null) + { + for (int i = 0; i < str.Length; i++) + { + char c = str[i]; + if (Char.IsLowSurrogate(c)) + { + str = str.Substring(0, i) + LowSurrogatePrefix + c + str.Substring(i + 1); + i++; + } + else if (Char.IsHighSurrogate(c)) + { + i++; + // always escape the markers + if (c == HighSurrogatePrefix || c == LowSurrogatePrefix || i == str.Length || !Char.IsLowSurrogate(str[i])) + { + str = str.Substring(0, i - 1) + HighSurrogatePrefix + (char)(c + 0x400) + str.Substring(i); + } + } + } + } + return str; + } + + internal static string UnescapeInvalidSurrogates(string str) + { + if (str != null) + { + for (int i = 0; i < str.Length; i++) + { + switch (str[i]) + { + case HighSurrogatePrefix: + str = str.Substring(0, i) + (char)(str[i + 1] - 0x400) + str.Substring(i + 2); + break; + case LowSurrogatePrefix: + str = str.Substring(0, i) + str[i + 1] + str.Substring(i + 2); + break; + } + } + } + return str; + } + + internal static string[] EscapeInvalidSurrogates(string[] str) + { + if (str != null) + { + for (int i = 0; i < str.Length; i++) + { + str[i] = EscapeInvalidSurrogates(str[i]); + } + } + return str; + } + + internal static string[] UnescapeInvalidSurrogates(string[] str) + { + if (str != null) + { + for (int i = 0; i < str.Length; i++) + { + str[i] = UnescapeInvalidSurrogates(str[i]); + } + } + return str; + } + } + +} diff --git a/src/IKVM.Runtime/UnloadableTypeWrapper.cs b/src/IKVM.Runtime/UnloadableTypeWrapper.cs new file mode 100644 index 0000000000..1e9e7b8399 --- /dev/null +++ b/src/IKVM.Runtime/UnloadableTypeWrapper.cs @@ -0,0 +1,187 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + sealed class UnloadableTypeWrapper : TypeWrapper + { + + internal const string ContainerTypeName = "__"; + readonly Type missingType; + Type customModifier; + + /// + /// Initializes a new instance. + /// + /// + internal UnloadableTypeWrapper(string name) : + base(TypeFlags.None, TypeWrapper.UnloadableModifiersHack, name) + { + + } + + internal UnloadableTypeWrapper(Type missingType) : + this(missingType.FullName) // TODO demangle and re-mangle appropriately + { + this.missingType = missingType; + } + + internal UnloadableTypeWrapper(string name, Type customModifier) : + this(name) + { + this.customModifier = customModifier; + } + + internal override TypeWrapper BaseTypeWrapper + { + get { return null; } + } + + internal override ClassLoaderWrapper GetClassLoader() + { + return null; + } + + internal override TypeWrapper EnsureLoadable(ClassLoaderWrapper loader) + { + var tw = loader.LoadClassByDottedNameFast(this.Name); + if (tw == null) + throw new NoClassDefFoundError(this.Name); + + return tw; + } + + internal override string SigName + { + get + { + var name = Name; + if (name.StartsWith("[")) + return name; + + return "L" + name + ";"; + } + } + + protected override void LazyPublishMembers() + { + throw new InvalidOperationException("LazyPublishMembers called on UnloadableTypeWrapper: " + Name); + } + + internal override Type TypeAsTBD + { + get + { + throw new InvalidOperationException("get_Type called on UnloadableTypeWrapper: " + Name); + } + } + + internal override TypeWrapper[] Interfaces + { + get + { +#if IMPORTER + if (missingType != null) + { + StaticCompiler.IssueMissingTypeMessage(missingType); + return TypeWrapper.EmptyArray; + } +#endif + + throw new InvalidOperationException("get_Interfaces called on UnloadableTypeWrapper: " + Name); + } + } + + internal override TypeWrapper[] InnerClasses + { + get + { + throw new InvalidOperationException("get_InnerClasses called on UnloadableTypeWrapper: " + Name); + } + } + + internal override TypeWrapper DeclaringTypeWrapper + { + get + { + throw new InvalidOperationException("get_DeclaringTypeWrapper called on UnloadableTypeWrapper: " + Name); + } + } + + internal override void Finish() + { + throw new InvalidOperationException("Finish called on UnloadableTypeWrapper: " + Name); + } + + internal Type MissingType + { + get { return missingType; } + } + + internal Type CustomModifier + { + get { return customModifier; } + } + + internal void SetCustomModifier(Type type) + { + this.customModifier = type; + } + +#if EMITTERS + + internal Type GetCustomModifier(TypeWrapperFactory context) + { + // we don't need to lock, because we're only supposed to be called while holding the finish lock + return customModifier ?? (customModifier = context.DefineUnloadable(this.Name)); + } + + internal override void EmitCheckcast(CodeEmitter ilgen) + { + throw new InvalidOperationException("EmitCheckcast called on UnloadableTypeWrapper: " + Name); + } + + internal override void EmitInstanceOf(CodeEmitter ilgen) + { + throw new InvalidOperationException("EmitInstanceOf called on UnloadableTypeWrapper: " + Name); + } + +#endif // EMITTERS + + } + +} diff --git a/src/IKVM.Runtime/Java/Externs/java/net/InetAddressExtensions.cs b/src/IKVM.Runtime/Util/Java/Net/InetAddressExtensions.cs similarity index 57% rename from src/IKVM.Runtime/Java/Externs/java/net/InetAddressExtensions.cs rename to src/IKVM.Runtime/Util/Java/Net/InetAddressExtensions.cs index 6683610f87..b92dc60a27 100644 --- a/src/IKVM.Runtime/Java/Externs/java/net/InetAddressExtensions.cs +++ b/src/IKVM.Runtime/Util/Java/Net/InetAddressExtensions.cs @@ -1,6 +1,6 @@ using System.Net; -namespace IKVM.Runtime.Java.Externs.java.net +namespace IKVM.Runtime.Util.Java.Net { /// @@ -12,52 +12,52 @@ static class InetAddressExtensions #if !FIRST_PASS /// - /// Gets a for the given . + /// Gets a for the given . /// /// /// - public static IPAddress ToIPAddress(this global::java.net.InetAddress self) + public static IPAddress ToIPAddress(this java.net.InetAddress self) { if (self == null) return null; - else if (self is global::java.net.Inet6Address ip6) + else if (self is java.net.Inet6Address ip6) return new IPAddress(self.getAddress(), ip6.getScopeId()); else return new IPAddress(self.getAddress()); } /// - /// Gets a for the given . + /// Gets a for the given . /// /// /// - public static global::java.net.InetAddress ToInetAddress(this IPAddress address) + public static java.net.InetAddress ToInetAddress(this IPAddress address) { return address != null ? address.ToInetAddress(address.ToString()) : null; } /// - /// Gets a for the given . + /// Gets a for the given . /// /// /// /// - public static global::java.net.InetAddress ToInetAddress(this IPAddress address, string hostname) + public static java.net.InetAddress ToInetAddress(this IPAddress address, string hostname) { if (address == null) return null; else if (address.IsIPv6LinkLocal || address.IsIPv6SiteLocal) - return global::java.net.Inet6Address.getByAddress(hostname, address.GetAddressBytes(), (int)address.ScopeId); + return java.net.Inet6Address.getByAddress(hostname, address.GetAddressBytes(), (int)address.ScopeId); else - return global::java.net.InetAddress.getByAddress(hostname, address.GetAddressBytes()); + return java.net.InetAddress.getByAddress(hostname, address.GetAddressBytes()); } /// - /// Gets a for the given . + /// Gets a for the given . /// /// /// - public static global::java.net.InetAddress ToInetAddress(this IPEndPoint self) + public static java.net.InetAddress ToInetAddress(this IPEndPoint self) { return self != null ? self.Address.ToInetAddress() : null; } diff --git a/src/IKVM.Runtime/Util/Java/Net/InetSocketAddressExtensions.cs b/src/IKVM.Runtime/Util/Java/Net/InetSocketAddressExtensions.cs new file mode 100644 index 0000000000..c71905f652 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Net/InetSocketAddressExtensions.cs @@ -0,0 +1,41 @@ +using System.Net; + +namespace IKVM.Runtime.Util.Java.Net +{ + + /// + /// Extension methods for working with Internet Socket Addresses. + /// + static class InetSocketAddressExtensions + { + +#if FIRST_PASS == false + + /// + /// Gets a for the given . + /// + /// + /// + public static IPEndPoint ToIPEndpoint(this global::java.net.InetSocketAddress self) + { + if (self == null) + return null; + else + return new IPEndPoint(self.getAddress().ToIPAddress(), self.getPort()); + } + + /// + /// Gets a for the given . + /// + /// + /// + public static global::java.net.InetSocketAddress ToInetSocketAddress(this IPEndPoint endpoint) + { + return endpoint != null ? new java.net.InetSocketAddress(endpoint.Address.ToInetAddress(endpoint.Address.ToString()), endpoint.Port) : null; + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Util/Java/Net/SocketExceptionExtensions.cs b/src/IKVM.Runtime/Util/Java/Net/SocketExceptionExtensions.cs index f60b7ee03e..643df6d966 100644 --- a/src/IKVM.Runtime/Util/Java/Net/SocketExceptionExtensions.cs +++ b/src/IKVM.Runtime/Util/Java/Net/SocketExceptionExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Net.Sockets; -namespace IKVM.Java.Externs.java.net +namespace IKVM.Runtime.Util.Java.Net { /// diff --git a/src/IKVM.Runtime/Util/Java/Nio/DirectBufferMemoryManager.cs b/src/IKVM.Runtime/Util/Java/Nio/DirectBufferMemoryManager.cs new file mode 100644 index 0000000000..41e48b57ce --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Nio/DirectBufferMemoryManager.cs @@ -0,0 +1,62 @@ +using System; +using System.Buffers; + +using IKVM.Internal; +using IKVM.Runtime.Accessors.Java.Lang; + +namespace IKVM.Runtime.Util.Java.Nio +{ + +#if FIRST_PASS == false + + /// + /// Provides a implementation backed by a Java DirectBuffer. This is a bit backward + /// because ultimatly Java owns the memory, and we just use this manager to obtain a + /// reference to it. Ideally, we would override DirectBuffer in the JDK to allocate from the runtime MemoryPool. + /// + class DirectBufferMemoryManager : MemoryManager + { + + static BufferAccessor bufferAccessor; + static BufferAccessor BufferAccessor => JVM.BaseAccessors.Get(ref bufferAccessor); + + readonly object buffer; + + /// + /// Initializes a new instance. + /// + public DirectBufferMemoryManager(object buffer) + { + this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + /// + public override unsafe Span GetSpan() => new Span((byte*)(IntPtr)BufferAccessor.GetAddress(buffer), BufferAccessor.GetCapacity(buffer)); + + /// + public override unsafe MemoryHandle Pin(int elementIndex = 0) + { + if (elementIndex < 0 || elementIndex >= bufferAccessor.GetCapacity(buffer)) + throw new ArgumentOutOfRangeException(nameof(elementIndex)); + + return new MemoryHandle((byte*)(IntPtr)(bufferAccessor.GetAddress(buffer)) + elementIndex); + } + + /// + public override void Unpin() + { + + } + + /// + protected override void Dispose(bool disposing) + { + // Java deals with this through the use of a Cleaner + // TODO I think we should own this + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedAction.cs b/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedAction.cs new file mode 100644 index 0000000000..5141286e9b --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedAction.cs @@ -0,0 +1,37 @@ +using System; + +namespace IKVM.Runtime.Util.Java.Security +{ + +#if FIRST_PASS == false + + /// + /// Implementation of that invokes a delegate. + /// + class ActionPrivilegedAction : global::java.security.PrivilegedAction + { + + public static implicit operator ActionPrivilegedAction(Action action) => new ActionPrivilegedAction(action); + + readonly Action action; + + /// + /// Initializes a new instance. + /// + /// + public ActionPrivilegedAction(Action action) + { + this.action = action ?? throw new ArgumentNullException(nameof(action)); + } + + public object run() + { + action(); + return null; + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedExceptionAction.cs b/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedExceptionAction.cs new file mode 100644 index 0000000000..b3e7a40a08 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Security/ActionPrivilegedExceptionAction.cs @@ -0,0 +1,35 @@ +using System; + +namespace IKVM.Runtime.Util.Java.Security +{ + +#if FIRST_PASS == false + + /// + /// Implementation of that invokes a delegate. + /// + class ActionPrivilegedExceptionAction : global::java.security.PrivilegedExceptionAction + { + + readonly Action action; + + /// + /// Initializes a new instance. + /// + /// + public ActionPrivilegedExceptionAction(Action action) + { + this.action = action ?? throw new ArgumentNullException(nameof(action)); + } + + public object run() + { + action(); + return null; + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Util/Java/Security/FuncPrivilegedAction.cs b/src/IKVM.Runtime/Util/Java/Security/FuncPrivilegedAction.cs new file mode 100644 index 0000000000..d9a4c0ad6d --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Security/FuncPrivilegedAction.cs @@ -0,0 +1,34 @@ +using System; + +namespace IKVM.Runtime.Util.Java.Security +{ + +#if FIRST_PASS == false + + /// + /// Implementation of that invokes a delegate. + /// + class FuncPrivilegedAction : global::java.security.PrivilegedAction + { + + readonly Func func; + + /// + /// Initializes a new instance. + /// + /// + public FuncPrivilegedAction(Func func) + { + this.func = func ?? throw new ArgumentNullException(nameof(func)); + } + + public object run() + { + return func(); + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorExtensions.cs b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorExtensions.cs new file mode 100644 index 0000000000..ae91b763e5 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorExtensions.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; + +#if FIRST_PASS == false + +using java.util.concurrent; + +namespace IKVM.Runtime.Util.Java.Util.Concurrent +{ + + static class ExecutorExtensions + { + + /// + /// Gets a implementation that dispatches to the given . + /// + /// + /// + public static SynchronizationContext ToSynchronizationContext(this Executor self) + { + return new ExecutorSynchronizationContext(self); + } + + /// + /// Gets a implementation that dispatches to the given . + /// + /// + /// + public static TaskScheduler ToTaskScheduler(this Executor self) + { + return new ExecutorTaskScheduler(self); + } + + } + +} + +#endif diff --git a/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorSynchronizationContext.cs b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorSynchronizationContext.cs new file mode 100644 index 0000000000..0be1ebd12e --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorSynchronizationContext.cs @@ -0,0 +1,81 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +#if FIRST_PASS == false + +using java.lang; +using java.util.concurrent; + +namespace IKVM.Runtime.Util.Java.Util.Concurrent +{ + + /// + /// implementation that executes tasks on a . + /// + class ExecutorSynchronizationContext : SynchronizationContext + { + + /// + /// Runnable implementation that encapsulates the execution of a task. + /// + class TaskRunnable : Runnable + { + + readonly ExecutorSynchronizationContext self; + readonly SendOrPostCallback post; + readonly object state; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + public TaskRunnable(ExecutorSynchronizationContext self, SendOrPostCallback post, object state) + { + this.self = self ?? throw new ArgumentNullException(nameof(self)); + this.post = post ?? throw new ArgumentNullException(nameof(post)); + this.state = state; + } + + public void run() + { + var save = Current; + + try + { + SetSynchronizationContext(self); + post(state); + } + finally + { + SetSynchronizationContext(save); + } + } + + } + + readonly Executor executor; + + /// + /// Initializes a new instance. + /// + /// + /// + public ExecutorSynchronizationContext(Executor executor) + { + this.executor = executor ?? throw new ArgumentNullException(nameof(executor)); + } + + public override void Post(SendOrPostCallback d, object state) + { + executor.execute(new TaskRunnable(this, d, state)); + } + + } + +} + +#endif \ No newline at end of file diff --git a/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorTaskScheduler.cs b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorTaskScheduler.cs new file mode 100644 index 0000000000..abda2cff83 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/Concurrent/ExecutorTaskScheduler.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +#if FIRST_PASS == false + +using java.lang; +using java.util.concurrent; + +namespace IKVM.Runtime.Util.Java.Util.Concurrent +{ + + /// + /// implementation that executes tasks on a . + /// + class ExecutorTaskScheduler : TaskScheduler + { + + /// + /// Runnable implementation that encapsulates the execution of a task. + /// + class TaskRunnable : Runnable + { + + readonly ExecutorTaskScheduler scheduler; + readonly Task task; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public TaskRunnable(ExecutorTaskScheduler scheduler, Task task) + { + this.scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); + this.task = task ?? throw new ArgumentNullException(nameof(task)); + } + + public void run() + { + scheduler.TryExecuteTask(task); + } + + } + + readonly Executor executor; + + /// + /// Initializes a new instance. + /// + /// + /// + public ExecutorTaskScheduler(Executor executor) + { + this.executor = executor ?? throw new ArgumentNullException(nameof(executor)); + } + + protected override void QueueTask(Task task) + { + executor.execute(new TaskRunnable(this, task)); + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + } + + protected override IEnumerable GetScheduledTasks() + { + return Array.Empty(); + } + + } + +} + +#endif \ No newline at end of file diff --git a/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskExtensions.cs b/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskExtensions.cs new file mode 100644 index 0000000000..a94514a998 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskExtensions.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +#if FIRST_PASS == false + +using java.util.concurrent; + +#endif + +namespace IKVM.Runtime.Util.Java.Util.Concurrent +{ + + static class TaskExtensions + { + +#if FIRST_PASS == false + + public static Future ToFuture(this Task task, Func cancellation) + { + return new TaskFuture(task, cancellation); + } + + public static Future ToFuture(this Task task, CancellationTokenSource cancellationTokenSource) + { + return new TaskFuture(task, () => { cancellationTokenSource.Cancel(); return true; }); + } + + public static Future ToFuture(this Task task) + { + return new TaskFuture(task); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskFuture.cs b/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskFuture.cs new file mode 100644 index 0000000000..2230e7dd66 --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/Concurrent/TaskFuture.cs @@ -0,0 +1,92 @@ +using System; +using System.Threading.Tasks; + +#if FIRST_PASS == false + +using java.util.concurrent; + +#endif + +namespace IKVM.Runtime.Util.Java.Util.Concurrent +{ + +#if FIRST_PASS == false + + /// + /// Implements the Java Future interface around a .NET Task. + /// + class TaskFuture : Future + { + + readonly Task task; + readonly Func cancellation; + + /// + /// Initializes a new instance. + /// + /// + /// + /// + public TaskFuture(Task task, Func cancellation) + { + this.task = task ?? throw new ArgumentNullException(nameof(task)); + this.cancellation = cancellation ?? throw new ArgumentNullException(nameof(cancellation)); + } + + /// + /// Initializes a new instance. + /// + /// + /// + public TaskFuture(Task task) + { + this.task = task ?? throw new ArgumentNullException(nameof(task)); + } + + public bool cancel(bool mayInterruptIfRunning) + { + return cancellation != null && mayInterruptIfRunning != false && cancellation(); + } + + public bool isCancelled() + { + return task.IsCanceled; + } + + public bool isDone() + { + return task.IsCompleted; + } + + public object get() + { + try + { + return task.GetAwaiter().GetResult(); + } + catch (OperationCanceledException e) + { + throw new CancellationException(e.Message); + } + } + + public object get(long timeout, TimeUnit unit) + { + try + { + if (task.Wait(TimeSpan.FromMilliseconds(unit.toMillis(timeout))) == false) + throw new global::java.util.concurrent.TimeoutException(); + + return get(); + } + catch (OperationCanceledException e) + { + throw new CancellationException(e.Message); + } + } + + } + +#endif + +} diff --git a/src/IKVM.Runtime/Util/Java/Util/MapWrapper.cs b/src/IKVM.Runtime/Util/Java/Util/MapWrapper.cs new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/IKVM.Runtime/Util/Java/Util/MapWrapper.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/IKVM.Runtime/Util/Sun/Nio/Ch/OperationBase.cs b/src/IKVM.Runtime/Util/Sun/Nio/Ch/OperationBase.cs deleted file mode 100644 index fc2c4aafe8..0000000000 --- a/src/IKVM.Runtime/Util/Sun/Nio/Ch/OperationBase.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright (C) 2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System; - -using System.Net.Sockets; - -namespace IKVM.Runtime.Util.Sun.Nio.Ch -{ - -#if !FIRST_PASS - - abstract class OperationBase - { - - static readonly AsyncCallback callback = CallbackProc; - Socket socket; - global::sun.nio.ch.Iocp.ResultHandler handler; - int result; - Exception exception; - - internal int Do(Socket socket, TInput input, object handler) - { - try - { - this.socket = socket; - this.handler = (global::sun.nio.ch.Iocp.ResultHandler)handler; - var ar = Begin(socket, input, callback, this); - if (ar.CompletedSynchronously) - { - if (exception != null) - throw exception; - - return result; - } - else - { - return global::sun.nio.ch.IOStatus.UNAVAILABLE; - } - } - catch (SocketException e) - { - throw java.net.SocketUtil.convertSocketExceptionToIOException(e); - } - catch (ObjectDisposedException) - { - throw new java.nio.channels.ClosedChannelException(); - } - } - - static void CallbackProc(IAsyncResult ar) - { - var obj = (OperationBase)ar.AsyncState; - - try - { - var result = obj.End(obj.socket, ar); - if (ar.CompletedSynchronously) - obj.result = result; - else - obj.handler.completed(result, false); - } - catch (SocketException e) - { - if (ar.CompletedSynchronously) - { - obj.exception = e; - } - else - { - obj.handler.failed((int)e.SocketErrorCode, global::java.net.SocketUtil.convertSocketExceptionToIOException(e)); - } - } - catch (ObjectDisposedException e) - { - if (ar.CompletedSynchronously) - { - obj.exception = e; - } - else - { - obj.handler.failed(0, new global::java.nio.channels.ClosedChannelException()); - } - } - } - - protected abstract IAsyncResult Begin(Socket socket, TInput input, AsyncCallback callback, object state); - - protected abstract int End(Socket socket, IAsyncResult ar); - - } - -#endif - -} diff --git a/src/IKVM.Runtime/VerifierTypeWrapper.cs b/src/IKVM.Runtime/VerifierTypeWrapper.cs new file mode 100644 index 0000000000..57a57c32ff --- /dev/null +++ b/src/IKVM.Runtime/VerifierTypeWrapper.cs @@ -0,0 +1,202 @@ +/* + Copyright (C) 2002-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + // this is a container for the special verifier TypeWrappers + sealed class VerifierTypeWrapper : TypeWrapper + { + + // the TypeWrapper constructor interns the name, so we have to pre-intern here to make sure we have the same string object + // (if it has only been interned previously) + static readonly string This = string.Intern("this"); + static readonly string New = string.Intern("new"); + static readonly string Fault = string.Intern(""); + internal static readonly TypeWrapper Invalid = null; + internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null, null); + internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null, null); + internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper(""); + internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("", 0, null, null); + internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("", 0, null, null); + + readonly int index; + readonly TypeWrapper underlyingType; + readonly MethodAnalyzer methodAnalyzer; + +#if EXPORTER + + internal class MethodAnalyzer + { + internal void ClearFaultBlockException(int dummy) { } + } + +#endif + + public override string ToString() + { + return GetType().Name + "[" + Name + "," + index + "," + underlyingType + "]"; + } + + internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex) + { + return new VerifierTypeWrapper(New, bytecodeIndex, type, null); + } + + internal static TypeWrapper MakeFaultBlockException(MethodAnalyzer ma, int handlerIndex) + { + return new VerifierTypeWrapper(Fault, handlerIndex, null, ma); + } + + // NOTE the "this" type is special, it can only exist in local[0] and on the stack + // as soon as the type on the stack is merged or popped it turns into its underlying type. + // It exists to capture the verification rules for non-virtual base class method invocation in .NET 2.0, + // which requires that the invocation is done on a "this" reference that was directly loaded onto the + // stack (using ldarg_0). + internal static TypeWrapper MakeThis(TypeWrapper type) + { + return new VerifierTypeWrapper(This, 0, type, null); + } + + internal static bool IsNotPresentOnStack(TypeWrapper w) + { + return IsNew(w) || IsFaultBlockException(w); + } + + internal static bool IsNew(TypeWrapper w) + { + return w != null && w.IsVerifierType && ReferenceEquals(w.Name, New); + } + + internal static bool IsFaultBlockException(TypeWrapper w) + { + return w != null && w.IsVerifierType && ReferenceEquals(w.Name, Fault); + } + + internal static bool IsNullOrUnloadable(TypeWrapper w) + { + return w == Null || w.IsUnloadable; + } + + internal static bool IsThis(TypeWrapper w) + { + return w != null && w.IsVerifierType && ReferenceEquals(w.Name, This); + } + + internal static void ClearFaultBlockException(TypeWrapper w) + { + VerifierTypeWrapper vtw = (VerifierTypeWrapper)w; + vtw.methodAnalyzer.ClearFaultBlockException(vtw.Index); + } + + internal int Index + { + get + { + return index; + } + } + + internal TypeWrapper UnderlyingType + { + get + { + return underlyingType; + } + } + + private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType, MethodAnalyzer methodAnalyzer) + : base(TypeFlags.None, TypeWrapper.VerifierTypeModifiersHack, name) + { + this.index = index; + this.underlyingType = underlyingType; + this.methodAnalyzer = methodAnalyzer; + } + + internal override TypeWrapper BaseTypeWrapper + { + get { return null; } + } + + internal override ClassLoaderWrapper GetClassLoader() + { + return null; + } + + protected override void LazyPublishMembers() + { + throw new InvalidOperationException("LazyPublishMembers called on " + this); + } + + internal override Type TypeAsTBD + { + get + { + throw new InvalidOperationException("get_Type called on " + this); + } + } + + internal override TypeWrapper[] Interfaces + { + get + { + throw new InvalidOperationException("get_Interfaces called on " + this); + } + } + + internal override TypeWrapper[] InnerClasses + { + get + { + throw new InvalidOperationException("get_InnerClasses called on " + this); + } + } + + internal override TypeWrapper DeclaringTypeWrapper + { + get + { + throw new InvalidOperationException("get_DeclaringTypeWrapper called on " + this); + } + } + + internal override void Finish() + { + throw new InvalidOperationException("Finish called on " + this); + } + } + +} diff --git a/src/IKVM.Runtime/Vfs/VfsAssemblyClassDirectory.cs b/src/IKVM.Runtime/Vfs/VfsAssemblyClassDirectory.cs index 5b8b0e9302..5cbfb6337b 100644 --- a/src/IKVM.Runtime/Vfs/VfsAssemblyClassDirectory.cs +++ b/src/IKVM.Runtime/Vfs/VfsAssemblyClassDirectory.cs @@ -1,27 +1,4 @@ -/* - Copyright (C) 2007-2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -36,7 +13,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents a virtual directory for Java classes form an assembly. /// - sealed class VfsAssemblyClassDirectory : VfsDirectory + internal sealed class VfsAssemblyClassDirectory : VfsDirectory { readonly Assembly assembly; @@ -87,6 +64,9 @@ VfsEntry CreateEntry(string name) /// TypeWrapper TryLoadType(JavaTypeName className) { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else var acl = AssemblyClassLoader.FromAssembly(assembly); try @@ -99,6 +79,7 @@ TypeWrapper TryLoadType(JavaTypeName className) } return null; +#endif } /// @@ -140,7 +121,7 @@ IEnumerable GetAssemblyTypes() /// VfsEntry GetPackageEntry(JavaPackageName packageName) { -#if FIRST_PASS +#if FIRST_PASS || IMPORTER || EXPORTER throw new PlatformNotSupportedException(); #else var acl = AssemblyClassLoader.FromAssembly(assembly); @@ -176,7 +157,7 @@ VfsEntry GetPackageEntry(JavaPackageName packageName) /// public override string[] List() { -#if FIRST_PASS +#if FIRST_PASS || IMPORTER || EXPORTER throw new PlatformNotSupportedException(); #else var lst = new HashSet(); @@ -198,10 +179,10 @@ public override string[] List() continue; // found a type that is within the package - // its package 'ssimple name is a directory + // its package's simple name is a directory if (name.Value.IsMemberOf(package)) { - lst.Add(name.Value.SimpleName.ToString() + ".class"); + lst.Add(name.Value.UnqualifiedName.ToString() + ".class"); continue; } diff --git a/src/IKVM.Runtime/Vfs/VfsAssemblyClassFile.cs b/src/IKVM.Runtime/Vfs/VfsAssemblyClassFile.cs index 85d9cc77b2..fae0a0dd20 100644 --- a/src/IKVM.Runtime/Vfs/VfsAssemblyClassFile.cs +++ b/src/IKVM.Runtime/Vfs/VfsAssemblyClassFile.cs @@ -1,30 +1,8 @@ -/* - Copyright (C) 2007-2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; +using System; using System.IO; using IKVM.Internal; +using IKVM.Runtime.Accessors.Java.Lang; namespace IKVM.Runtime.Vfs { @@ -32,7 +10,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents an assembly class file within the virtual file system. /// - sealed class VfsAssemblyClassFile : VfsFile + internal sealed class VfsAssemblyClassFile : VfsFile { readonly TypeWrapper type; @@ -56,12 +34,11 @@ internal VfsAssemblyClassFile(VfsContext context, TypeWrapper type) : /// byte[] GenerateClassFile() { -#if FIRST_PASS - throw new PlatformNotSupportedException(); +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); #else var stream = new MemoryStream(); - var includeNonPublicInterfaces = bool.TryParse(java.lang.Props.props.getProperty("ikvm.stubgen.skipNonPublicInterfaces"), out var b) && b; - IKVM.StubGen.StubGenerator.WriteClass(stream, type, includeNonPublicInterfaces, false, false, true); + IKVM.StubGen.StubGenerator.WriteClass(stream, type, true, true, true, true, false); return stream.ToArray(); #endif } diff --git a/src/IKVM.Runtime/Vfs/VfsAssemblyDirectory.cs b/src/IKVM.Runtime/Vfs/VfsAssemblyDirectory.cs index 73a47be76f..a39c415236 100644 --- a/src/IKVM.Runtime/Vfs/VfsAssemblyDirectory.cs +++ b/src/IKVM.Runtime/Vfs/VfsAssemblyDirectory.cs @@ -33,7 +33,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents a directory containing all of the known assembies. /// - sealed class VfsAssemblyDirectory : VfsDirectory + internal sealed class VfsAssemblyDirectory : VfsDirectory { readonly ConcurrentDictionary directories = new ConcurrentDictionary(); diff --git a/src/IKVM.Runtime/Vfs/VfsAssemblyResourceDirectory.cs b/src/IKVM.Runtime/Vfs/VfsAssemblyResourceDirectory.cs index aa92cf8b5c..eaea0d1ae1 100644 --- a/src/IKVM.Runtime/Vfs/VfsAssemblyResourceDirectory.cs +++ b/src/IKVM.Runtime/Vfs/VfsAssemblyResourceDirectory.cs @@ -1,27 +1,4 @@ -/* - Copyright (C) 2007-2011 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; +using System; using System.Reflection; namespace IKVM.Runtime.Vfs @@ -30,7 +7,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents a virtual directory for assembly resources. /// - sealed class VfsAssemblyResourceDirectory : VfsDirectory + internal sealed class VfsAssemblyResourceDirectory : VfsDirectory { readonly Assembly assembly; diff --git a/src/IKVM.Runtime/Vfs/VfsAssemblyResourceFile.cs b/src/IKVM.Runtime/Vfs/VfsAssemblyResourceFile.cs index c16ea8b00e..43b8d8c7d6 100644 --- a/src/IKVM.Runtime/Vfs/VfsAssemblyResourceFile.cs +++ b/src/IKVM.Runtime/Vfs/VfsAssemblyResourceFile.cs @@ -31,7 +31,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents an assembly resource file within the virtual file system. /// - sealed class VfsAssemblyResourceFile : VfsFile + internal sealed class VfsAssemblyResourceFile : VfsFile { readonly Assembly asm; diff --git a/src/IKVM.Runtime/Vfs/VfsCacertsFile.cs b/src/IKVM.Runtime/Vfs/VfsCacertsFile.cs index a7a811c29a..aab046aeba 100644 --- a/src/IKVM.Runtime/Vfs/VfsCacertsFile.cs +++ b/src/IKVM.Runtime/Vfs/VfsCacertsFile.cs @@ -33,7 +33,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents a fake cacerts file. /// - sealed class VfsCacertsFile : VfsFile + internal sealed class VfsCacertsFile : VfsFile { readonly Lazy buff; @@ -54,7 +54,7 @@ internal VfsCacertsFile(VfsContext context) : /// byte[] GenerateCacertsFile() { -#if FIRST_PASS +#if FIRST_PASS || IMPORTER || EXPORTER throw new NotImplementedException(); #else var jstore = java.security.KeyStore.getInstance("jks"); diff --git a/src/IKVM.Runtime/Vfs/VfsContext.cs b/src/IKVM.Runtime/Vfs/VfsContext.cs index 2938f50455..ad1f5e0908 100644 --- a/src/IKVM.Runtime/Vfs/VfsContext.cs +++ b/src/IKVM.Runtime/Vfs/VfsContext.cs @@ -7,7 +7,7 @@ namespace IKVM.Runtime.Vfs /// /// Provides information to the VFS about the environment. /// - abstract class VfsContext + internal abstract class VfsContext { /// diff --git a/src/IKVM.Runtime/Vfs/VfsDirectory.cs b/src/IKVM.Runtime/Vfs/VfsDirectory.cs index 1051539b69..1f659a5388 100644 --- a/src/IKVM.Runtime/Vfs/VfsDirectory.cs +++ b/src/IKVM.Runtime/Vfs/VfsDirectory.cs @@ -4,7 +4,7 @@ /// /// Represents a directory entry in the virtual file system. /// - abstract class VfsDirectory : VfsEntry + internal abstract class VfsDirectory : VfsEntry { /// diff --git a/src/IKVM.Runtime/Vfs/VfsEntry.cs b/src/IKVM.Runtime/Vfs/VfsEntry.cs index eeb19abb0b..b3d3701e68 100644 --- a/src/IKVM.Runtime/Vfs/VfsEntry.cs +++ b/src/IKVM.Runtime/Vfs/VfsEntry.cs @@ -31,7 +31,7 @@ namespace IKVM.Runtime.Vfs /// /// Base type for entries within a virtual file system. /// - abstract class VfsEntry + internal abstract class VfsEntry { readonly VfsContext context; diff --git a/src/IKVM.Runtime/Vfs/VfsEntryDirectory.cs b/src/IKVM.Runtime/Vfs/VfsEntryDirectory.cs index 9409769831..c10cf6d14c 100644 --- a/src/IKVM.Runtime/Vfs/VfsEntryDirectory.cs +++ b/src/IKVM.Runtime/Vfs/VfsEntryDirectory.cs @@ -8,7 +8,7 @@ namespace IKVM.Runtime.Vfs /// /// Describes a directory that has entries added to it statically. /// - sealed class VfsEntryDirectory : VfsDirectory + internal sealed class VfsEntryDirectory : VfsDirectory { readonly ConcurrentDictionary entries = new(); diff --git a/src/IKVM.Runtime/Vfs/VfsFile.cs b/src/IKVM.Runtime/Vfs/VfsFile.cs index 97968a1f7a..f5241e246c 100644 --- a/src/IKVM.Runtime/Vfs/VfsFile.cs +++ b/src/IKVM.Runtime/Vfs/VfsFile.cs @@ -31,7 +31,7 @@ namespace IKVM.Runtime.Vfs /// /// Represents a file within the virtual file system. /// - abstract class VfsFile : VfsEntry + internal abstract class VfsFile : VfsEntry { /// @@ -68,7 +68,7 @@ public virtual bool CanOpen(FileMode mode, FileAccess access) /// public virtual Stream Open(FileMode mode, FileAccess access) { - return CanOpen(mode, access) ? OpenRead() : throw new UnauthorizedAccessException("Access to the file was denied. The IKVM VFS currently only support read operations."); + return CanOpen(mode, access) ? OpenRead() : throw new UnauthorizedAccessException("Access to the file was denied. The IKVM VFS currently only supports read operations."); } /// diff --git a/src/IKVM.Runtime/Vfs/VfsMount.cs b/src/IKVM.Runtime/Vfs/VfsMount.cs index c0f72c02e5..5b3d480356 100644 --- a/src/IKVM.Runtime/Vfs/VfsMount.cs +++ b/src/IKVM.Runtime/Vfs/VfsMount.cs @@ -31,58 +31,58 @@ namespace IKVM.Runtime.Vfs /// /// Implements a virtual file system available to Java libraries. /// - partial class VfsMount + internal partial class VfsMount { - readonly string rootPath; - readonly VfsDirectory root; + readonly string path; + readonly VfsEntry item; /// /// Initializes a new instance. /// - /// - /// - public VfsMount(string rootPath, VfsDirectory root) + /// + /// + public VfsMount(string path, VfsEntry item) { - if (rootPath is null) - throw new ArgumentNullException(nameof(rootPath)); - if (root is null) - throw new ArgumentNullException(nameof(root)); + if (path is null) + throw new ArgumentNullException(nameof(path)); + if (item is null) + throw new ArgumentNullException(nameof(item)); - this.rootPath = PathExtensions.EnsureEndingDirectorySeparator(rootPath); - this.root = root; + this.path = item is VfsDirectory ? PathExtensions.EnsureEndingDirectorySeparator(path) : path.TrimEnd(System.IO.Path.DirectorySeparatorChar); + this.item = item; } /// /// Gets the path of this virtual file system. /// - public string RootPath => rootPath; + public string Path => path; /// - /// Gets the root directory of this virtual file system. + /// Gets the root item of this virtual file system. /// - public VfsDirectory Root => root; + public VfsEntry Item => item; /// /// Returns true if the given path is within the virtual file system mount. /// /// /// - public bool IsPath(string path) => path.StartsWith(rootPath, StringComparison.Ordinal); + public bool IsPath(string path) => path.StartsWith(this.path, StringComparison.Ordinal); /// - /// Gets the entry for he given path. + /// Gets the entry for the given path. /// /// /// /// - public VfsEntry GetPath(string path) + public VfsEntry GetEntry(string path) { if (path is null) throw new ArgumentNullException(nameof(path)); var p = path.Split(PathExtensions.DirectorySeparatorChars, StringSplitOptions.RemoveEmptyEntries); - var c = (VfsEntry)Root; + var c = Item; for (int i = 0; i < p.Length; i++) { // can only recurse into directory diff --git a/src/IKVM.Runtime/Vfs/VfsRuntimeContext.cs b/src/IKVM.Runtime/Vfs/VfsRuntimeContext.cs index 8a7b39e4e1..aae554c0c9 100644 --- a/src/IKVM.Runtime/Vfs/VfsRuntimeContext.cs +++ b/src/IKVM.Runtime/Vfs/VfsRuntimeContext.cs @@ -15,7 +15,7 @@ namespace IKVM.Runtime.Vfs /// /// Provides information to the VFS based on the current runtime environment. /// - class VfsRuntimeContext : VfsContext + internal class VfsRuntimeContext : VfsContext { /// diff --git a/src/IKVM.Runtime/Vfs/VfsTable.cs b/src/IKVM.Runtime/Vfs/VfsTable.cs index cf867e0765..32d9d34125 100644 --- a/src/IKVM.Runtime/Vfs/VfsTable.cs +++ b/src/IKVM.Runtime/Vfs/VfsTable.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; -using IKVM.Internal; using IKVM.Runtime.Extensions; namespace IKVM.Runtime.Vfs @@ -15,14 +13,9 @@ namespace IKVM.Runtime.Vfs /// /// Collection of mounted virtual file systems. /// - class VfsTable + internal class VfsTable { - /// - /// Gets the default mount path of the global file system. - /// - public readonly static string RootPath = JVM.IsUnix ? "/mnt/.ikvm/" : @"\\.ikvm\"; - /// /// Gets the default mount table. /// @@ -34,29 +27,21 @@ class VfsTable /// static VfsTable BuildDefaultTable(VfsContext context) { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else if (context is null) throw new ArgumentNullException(nameof(context)); + var ikvmHome = JVM.Properties.HomePath; + if (Directory.Exists(ikvmHome) == false) + throw new DirectoryNotFoundException("Could not locate ikvm.home when establishing VFS."); + var table = new VfsTable(context); - table.AddMount(RootPath, BuildIkvmHomeRoot(context)); + table.AddMount(Path.Combine(ikvmHome, "assembly"), new VfsAssemblyDirectory(context)); + table.AddMount(Path.Combine(ikvmHome, "lib", "security", "cacerts"), new VfsCacertsFile(context)); return table; - } - - /// - /// Builds the default mount directory. - /// - /// - /// - /// - static VfsDirectory BuildIkvmHomeRoot(VfsContext context) - { - if (context is null) - throw new ArgumentNullException(nameof(context)); - - var home = new VfsEntryDirectory(context); - home.AddEntry("assembly", new VfsAssemblyDirectory(context)); - home.AddEntry("cacerts", new VfsCacertsFile(context)); - return home; +#endif } readonly VfsContext context; @@ -81,11 +66,11 @@ public VfsTable(VfsContext context) /// Adds the a new mount to the table. /// /// - /// + /// /// - public VfsMount AddMount(string path, VfsDirectory root) + public VfsMount AddMount(string path, VfsEntry item) { - return mounts.AddFirst(new VfsMount(path, root)).Value; + return mounts.AddFirst(new VfsMount(path, item)).Value; } /// @@ -94,7 +79,7 @@ public VfsMount AddMount(string path, VfsDirectory root) /// public void RemoveMount(string path) { - var mount = mounts.FirstOrDefault(i => i.RootPath == path); + var mount = mounts.FirstOrDefault(i => i.Path == path); if (mount != null) mounts.Remove(mount); } @@ -118,14 +103,14 @@ public void RemoveMount(string path) /// /// /// - public VfsEntry GetPath(string path) => GetMount(path) is VfsMount mount ? mount.GetPath(path.Substring(mount.RootPath.Length)) : null; + public VfsEntry GetEntry(string path) => GetMount(path) is VfsMount mount ? mount.GetEntry(path.Substring(mount.Path.Length)) : null; /// /// Gets the names of the entries within the directory. /// /// /// - public string[] List(string path) => GetPath(path) is VfsDirectory directory ? directory.List() : null; + public string[] List(string path) => GetEntry(path) is VfsDirectory directory ? directory.List() : null; /// /// Opens a file at the specified path. @@ -143,7 +128,7 @@ public Stream Open(string path, FileMode mode, FileAccess access) throw new UnauthorizedAccessException("Virtual file system is read-only."); // search for the entry in the file system and open it - return GetPath(path) switch + return GetEntry(path) switch { VfsFile file => file.Open(mode, access), VfsDirectory => throw new UnauthorizedAccessException($"Access to '{path}' was denied."), @@ -158,7 +143,7 @@ public Stream Open(string path, FileMode mode, FileAccess access) /// public long GetLength(string path) { - return GetPath(path) is VfsFile entry ? entry.Size : 0; + return GetEntry(path) is VfsFile entry ? entry.Size : 0; } /// @@ -172,7 +157,7 @@ public int GetBooleanAttributes(string path) const int BA_REGULAR = 0x02; const int BA_DIRECTORY = 0x04; - return GetPath(path) switch + return GetEntry(path) switch { VfsDirectory => BA_EXISTS | BA_DIRECTORY, VfsFile => BA_EXISTS | BA_REGULAR, @@ -187,10 +172,18 @@ public int GetBooleanAttributes(string path) /// public string GetAssemblyClassesPath(Assembly assembly) { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else if (assembly is null) throw new ArgumentNullException(nameof(assembly)); - return PathExtensions.EnsureEndingDirectorySeparator(Path.Combine(mounts.First.Value.RootPath, "assembly", GetAssemblyDirectoryName(assembly), "classes")); + var ikvmHome = JVM.Properties.HomePath; + if (Directory.Exists(ikvmHome) == false) + throw new DirectoryNotFoundException("Could not locate IkvmHome when finding VFS."); + + return PathExtensions.EnsureEndingDirectorySeparator(Path.Combine(ikvmHome, "assembly", GetAssemblyDirectoryName(assembly), "classes")); +#endif } /// @@ -200,10 +193,18 @@ public string GetAssemblyClassesPath(Assembly assembly) /// public string GetAssemblyResourcesPath(Assembly assembly) { +#if FIRST_PASS || IMPORTER || EXPORTER + throw new NotImplementedException(); +#else if (assembly is null) throw new ArgumentNullException(nameof(assembly)); - return PathExtensions.EnsureEndingDirectorySeparator(Path.Combine(mounts.First.Value.RootPath, "assembly", GetAssemblyDirectoryName(assembly), "resources")); + var ikvmHome = JVM.Properties.HomePath; + if (Directory.Exists(ikvmHome) == false) + throw new DirectoryNotFoundException("Could not locate IkvmHome when finding VFS."); + + return PathExtensions.EnsureEndingDirectorySeparator(Path.Combine(ikvmHome, "assembly", GetAssemblyDirectoryName(assembly), "resources")); +#endif } /// @@ -216,8 +217,10 @@ bool IsLoadableAssembly(Assembly assembly) if (assembly.ReflectionOnly) return false; +#if NETFRAMEWORK if (assembly.GlobalAssemblyCache) return true; +#endif if (assembly.IsDynamic || assembly.Location == "") return false; diff --git a/src/IKVM.Runtime/VolatileLongDoubleFieldWrapper.cs b/src/IKVM.Runtime/VolatileLongDoubleFieldWrapper.cs new file mode 100644 index 0000000000..db5cdb1228 --- /dev/null +++ b/src/IKVM.Runtime/VolatileLongDoubleFieldWrapper.cs @@ -0,0 +1,257 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +using IKVM.Runtime; + +#if IMPORTER || EXPORTER +using IKVM.Reflection; +using IKVM.Reflection.Emit; + +using Type = IKVM.Reflection.Type; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +#if IMPORTER +using IKVM.Tools.Importer; +#endif + +namespace IKVM.Internal +{ + + /// + /// Field wrapper for a field of type 'volatile long' or 'volatile double'. + /// + sealed class VolatileLongDoubleFieldWrapper : FieldWrapper + { + + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// + /// + /// + internal VolatileLongDoubleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers) : + base(declaringType, fieldType, name, sig, modifiers, fi) + { + if (sig != "J" && sig != "D") + throw new ArgumentException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double signature.", nameof(sig)); + if (IsVolatile == false) + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} requires volatile type."); + } + +#if EMITTERS + + protected override void EmitGetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (fi.IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + + il.Emit(OpCodes.Ldflda, fi); + } + + if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadDouble); + } + else + { + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double."); + } + } + + protected override void EmitSetImpl(CodeEmitter il) + { + var fi = GetField(); + + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); + il.Emit(OpCodes.Stloc, value); + + if (fi.IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + + il.Emit(OpCodes.Ldflda, fi); + } + + il.Emit(OpCodes.Ldloc, value); + + if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteDouble); + } + else + { + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double."); + } + } + + protected override void EmitUnsafeVolatileGetImpl(CodeEmitter il) + { + var fi = GetField(); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + + il.Emit(OpCodes.Ldflda, fi); + } + + if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileReadDouble); + } + else + { + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double."); + } + } + + protected override void EmitUnsafeVolatileSetImpl(CodeEmitter il) + { + var fi = GetField(); + + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); + il.Emit(OpCodes.Stloc, value); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + + il.Emit(OpCodes.Ldflda, fi); + } + + il.Emit(OpCodes.Ldloc, value); + + if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.VolatileWriteDouble); + } + else + { + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double."); + } + } + + protected override void EmitUnsafeCompareAndSwapImpl(CodeEmitter il) + { + var fi = GetField(); + + var value = il.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); + il.Emit(OpCodes.Stloc, value); + var expect = il.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType); + il.Emit(OpCodes.Stloc, expect); + + if (IsStatic) + { + il.Emit(OpCodes.Ldsflda, fi); + } + else + { + if (DeclaringType.IsNonPrimitiveValueType) + il.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD); + + il.Emit(OpCodes.Ldflda, fi); + } + + il.Emit(OpCodes.Ldloc, expect); + il.Emit(OpCodes.Ldloc, value); + + if (FieldTypeWrapper == PrimitiveTypeWrapper.LONG) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapLong); + } + else if (FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE) + { + il.Emit(OpCodes.Call, ByteCodeHelperMethods.CompareAndSwapDouble); + } + else + { + throw new InternalException($"{nameof(VolatileLongDoubleFieldWrapper)} expects long or double."); + } + } + +#endif + +#if !IMPORTER && !EXPORTER && !FIRST_PASS + + internal override object GetValue(object obj) + { + throw new InvalidOperationException(); + } + + internal override void SetValue(object obj, object value) + { + throw new InvalidOperationException(); + } + +#endif + + } + +} diff --git a/src/IKVM.Runtime/atomic.cs b/src/IKVM.Runtime/atomic.cs index f00c8a2e6b..5005d7e6e5 100644 --- a/src/IKVM.Runtime/atomic.cs +++ b/src/IKVM.Runtime/atomic.cs @@ -23,16 +23,17 @@ Jeroen Frijters */ using System; -#if STATIC_COMPILER +using IKVM.Internal; +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; #else using System.Reflection; using System.Reflection.Emit; - #endif -using IKVM.Internal; using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags; @@ -128,6 +129,7 @@ private static void EmitSet(string name, TypeBuilder tb, FieldInfo field) static class InterlockedMethods { + internal static readonly MethodInfo AddInt32; internal static readonly MethodInfo CompareExchangeInt32; internal static readonly MethodInfo CompareExchangeInt64; @@ -155,5 +157,7 @@ static InterlockedMethods() } } } + } + } diff --git a/src/IKVM.Runtime/compiler.cs b/src/IKVM.Runtime/compiler.cs index ccdc3353d2..ad2a930615 100644 --- a/src/IKVM.Runtime/compiler.cs +++ b/src/IKVM.Runtime/compiler.cs @@ -23,3554 +23,3445 @@ Jeroen Frijters */ using System; using System.Collections.Generic; -#if STATIC_COMPILER +using System.Diagnostics; +using System.Diagnostics.SymbolStore; + +using IKVM.Attributes; +using IKVM.Internal; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; #else using System.Reflection; using System.Reflection.Emit; #endif -using System.Diagnostics; -using System.Diagnostics.SymbolStore; -using IKVM.Attributes; -using IKVM.Internal; using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry; using LocalVariableTableEntry = IKVM.Internal.ClassFile.Method.LocalVariableTableEntry; using Instruction = IKVM.Internal.ClassFile.Method.Instruction; using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags; -static class ByteCodeHelperMethods -{ - internal static readonly MethodInfo multianewarray; - internal static readonly MethodInfo multianewarray_ghost; - internal static readonly MethodInfo anewarray_ghost; - internal static readonly MethodInfo f2i; - internal static readonly MethodInfo d2i; - internal static readonly MethodInfo f2l; - internal static readonly MethodInfo d2l; - internal static readonly MethodInfo arraycopy_fast; - internal static readonly MethodInfo arraycopy_primitive_8; - internal static readonly MethodInfo arraycopy_primitive_4; - internal static readonly MethodInfo arraycopy_primitive_2; - internal static readonly MethodInfo arraycopy_primitive_1; - internal static readonly MethodInfo arraycopy; - internal static readonly MethodInfo DynamicCast; - internal static readonly MethodInfo DynamicAaload; - internal static readonly MethodInfo DynamicAastore; - internal static readonly MethodInfo DynamicClassLiteral; - internal static readonly MethodInfo DynamicMultianewarray; - internal static readonly MethodInfo DynamicNewarray; - internal static readonly MethodInfo DynamicNewCheckOnly; - internal static readonly MethodInfo DynamicCreateDelegate; - internal static readonly MethodInfo DynamicLoadMethodType; - internal static readonly MethodInfo DynamicLoadMethodHandle; - internal static readonly MethodInfo DynamicBinderMemberLookup; - internal static readonly MethodInfo DynamicMapException; - internal static readonly MethodInfo DynamicCallerID; - internal static readonly MethodInfo DynamicLinkIndyCallSite; - internal static readonly MethodInfo DynamicEraseInvokeExact; - internal static readonly MethodInfo VerboseCastFailure; - internal static readonly MethodInfo SkipFinalizer; - internal static readonly MethodInfo DynamicInstanceOf; - internal static readonly MethodInfo volatileReadDouble; - internal static readonly MethodInfo volatileReadLong; - internal static readonly MethodInfo volatileWriteDouble; - internal static readonly MethodInfo volatileWriteLong; - internal static readonly MethodInfo mapException; - internal static readonly MethodInfo GetDelegateForInvokeExact; - internal static readonly MethodInfo GetDelegateForInvoke; - internal static readonly MethodInfo GetDelegateForInvokeBasic; - internal static readonly MethodInfo LoadMethodType; - internal static readonly MethodInfo LinkIndyCallSite; - - static ByteCodeHelperMethods() - { -#if STATIC_COMPILER - Type typeofByteCodeHelper = StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper"); -#else - Type typeofByteCodeHelper = typeof(IKVM.Runtime.ByteCodeHelper); -#endif - multianewarray = GetHelper(typeofByteCodeHelper, "multianewarray"); - multianewarray_ghost = GetHelper(typeofByteCodeHelper, "multianewarray_ghost"); - anewarray_ghost = GetHelper(typeofByteCodeHelper, "anewarray_ghost"); - f2i = GetHelper(typeofByteCodeHelper, "f2i"); - d2i = GetHelper(typeofByteCodeHelper, "d2i"); - f2l = GetHelper(typeofByteCodeHelper, "f2l"); - d2l = GetHelper(typeofByteCodeHelper, "d2l"); - arraycopy_fast = GetHelper(typeofByteCodeHelper, "arraycopy_fast"); - arraycopy_primitive_8 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_8"); - arraycopy_primitive_4 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_4"); - arraycopy_primitive_2 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_2"); - arraycopy_primitive_1 = GetHelper(typeofByteCodeHelper, "arraycopy_primitive_1"); - arraycopy = GetHelper(typeofByteCodeHelper, "arraycopy"); - DynamicCast = GetHelper(typeofByteCodeHelper, "DynamicCast"); - DynamicAaload = GetHelper(typeofByteCodeHelper, "DynamicAaload"); - DynamicAastore = GetHelper(typeofByteCodeHelper, "DynamicAastore"); - DynamicClassLiteral = GetHelper(typeofByteCodeHelper, "DynamicClassLiteral"); - DynamicMultianewarray = GetHelper(typeofByteCodeHelper, "DynamicMultianewarray"); - DynamicNewarray = GetHelper(typeofByteCodeHelper, "DynamicNewarray"); - DynamicNewCheckOnly = GetHelper(typeofByteCodeHelper, "DynamicNewCheckOnly"); - DynamicCreateDelegate = GetHelper(typeofByteCodeHelper, "DynamicCreateDelegate"); - DynamicLoadMethodType = GetHelper(typeofByteCodeHelper, "DynamicLoadMethodType"); - DynamicLoadMethodHandle = GetHelper(typeofByteCodeHelper, "DynamicLoadMethodHandle"); - DynamicBinderMemberLookup = GetHelper(typeofByteCodeHelper, "DynamicBinderMemberLookup"); - DynamicMapException = GetHelper(typeofByteCodeHelper, "DynamicMapException"); - DynamicCallerID = GetHelper(typeofByteCodeHelper, "DynamicCallerID"); - DynamicLinkIndyCallSite = GetHelper(typeofByteCodeHelper, "DynamicLinkIndyCallSite"); - DynamicEraseInvokeExact = GetHelper(typeofByteCodeHelper, "DynamicEraseInvokeExact"); - VerboseCastFailure = GetHelper(typeofByteCodeHelper, "VerboseCastFailure"); - SkipFinalizer = GetHelper(typeofByteCodeHelper, "SkipFinalizer"); - DynamicInstanceOf = GetHelper(typeofByteCodeHelper, "DynamicInstanceOf"); - volatileReadDouble = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Double.MakeByRefType() }); - volatileReadLong = GetHelper(typeofByteCodeHelper, "VolatileRead", new Type[] { Types.Int64.MakeByRefType() }); - volatileWriteDouble = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Double.MakeByRefType(), Types.Double }); - volatileWriteLong = GetHelper(typeofByteCodeHelper, "VolatileWrite", new Type[] { Types.Int64.MakeByRefType(), Types.Int64 }); - mapException = GetHelper(typeofByteCodeHelper, "MapException"); - GetDelegateForInvokeExact = GetHelper(typeofByteCodeHelper, "GetDelegateForInvokeExact"); - GetDelegateForInvoke = GetHelper(typeofByteCodeHelper, "GetDelegateForInvoke"); - GetDelegateForInvokeBasic = GetHelper(typeofByteCodeHelper, "GetDelegateForInvokeBasic"); - LoadMethodType = GetHelper(typeofByteCodeHelper, "LoadMethodType"); - LinkIndyCallSite = GetHelper(typeofByteCodeHelper, "LinkIndyCallSite"); - } - - private static MethodInfo GetHelper(Type type, string method) - { - return GetHelper(type, method, null); - } - - private static MethodInfo GetHelper(Type type, string method, Type[] parameters) - { - MethodInfo mi = parameters == null ? type.GetMethod(method) : type.GetMethod(method, parameters); -#if STATIC_COMPILER - if (mi == null) - { - throw new FatalCompilerErrorException(Message.RuntimeMethodMissing, method); - } -#endif - return mi; - } -} - -struct MethodKey : IEquatable -{ - private readonly string className; - private readonly string methodName; - private readonly string methodSig; - - internal MethodKey(string className, string methodName, string methodSig) - { - this.className = className; - this.methodName = methodName; - this.methodSig = methodSig; - } - - public bool Equals(MethodKey other) - { - return className == other.className && methodName == other.methodName && methodSig == other.methodSig; - } - - public override int GetHashCode() - { - return className.GetHashCode() ^ methodName.GetHashCode() ^ methodSig.GetHashCode(); - } -} +using IKVM.ByteCode; namespace IKVM.Runtime { - sealed class Compiler - { - internal static readonly MethodInfo unmapExceptionMethod; - private static readonly MethodInfo fixateExceptionMethod; - private static readonly MethodInfo suppressFillInStackTraceMethod; - internal static readonly MethodInfo getTypeFromHandleMethod; - internal static readonly MethodInfo getTypeMethod; - private static readonly MethodInfo keepAliveMethod; - internal static readonly MethodWrapper getClassFromTypeHandle; - internal static readonly MethodWrapper getClassFromTypeHandle2; - private readonly DynamicTypeWrapper.FinishContext context; - private readonly DynamicTypeWrapper clazz; - private readonly MethodWrapper mw; - private readonly ClassFile classFile; - private readonly ClassFile.Method m; - private readonly CodeEmitter ilGenerator; - private readonly CodeInfo ma; - private readonly UntangledExceptionTable exceptions; - private readonly List harderrors; - private readonly LocalVarInfo localVars; - private bool nonleaf; - private readonly bool debug; - private readonly bool keepAlive; - private readonly bool strictfp; - private readonly bool emitLineNumbers; - private int[] scopeBegin; - private int[] scopeClose; -#if STATIC_COMPILER - private readonly MethodWrapper[] replacedMethodWrappers; -#endif - - static Compiler() - { - getTypeFromHandleMethod = Types.Type.GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public, null, new Type[] { Types.RuntimeTypeHandle }, null); - getTypeMethod = Types.Object.GetMethod("GetType", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); - keepAliveMethod = JVM.Import(typeof(System.GC)).GetMethod("KeepAlive", BindingFlags.Static | BindingFlags.Public, null, new Type[] { Types.Object }, null); - // HACK we need to special case core compilation, because the __ methods are HideFromJava - if (CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType is TypeBuilder) - { - MethodWrapper mw; - mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "()V", false); - mw.Link(); - suppressFillInStackTraceMethod = (MethodInfo)mw.GetMethod(); - mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false); - mw.Link(); - unmapExceptionMethod = (MethodInfo)mw.GetMethod(); - mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false); - mw.Link(); - fixateExceptionMethod = (MethodInfo)mw.GetMethod(); - } - else - { - suppressFillInStackTraceMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", Type.EmptyTypes); - unmapExceptionMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", new Type[] { Types.Exception }); - fixateExceptionMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", new Type[] { Types.Exception }); - } - getClassFromTypeHandle = ClassLoaderWrapper.LoadClassCritical("ikvm.runtime.Util").GetMethodWrapper("getClassFromTypeHandle", "(Lcli.System.RuntimeTypeHandle;)Ljava.lang.Class;", false); - getClassFromTypeHandle.Link(); - getClassFromTypeHandle2 = ClassLoaderWrapper.LoadClassCritical("ikvm.runtime.Util").GetMethodWrapper("getClassFromTypeHandle", "(Lcli.System.RuntimeTypeHandle;I)Ljava.lang.Class;", false); - getClassFromTypeHandle2.Link(); - } - - private Compiler(DynamicTypeWrapper.FinishContext context, TypeWrapper host, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ClassLoaderWrapper classLoader) - { - this.context = context; - this.clazz = clazz; - this.mw = mw; - this.classFile = classFile; - this.m = m; - this.ilGenerator = ilGenerator; - this.debug = classLoader.EmitDebugInfo; - this.strictfp = m.IsStrictfp; - if (mw.IsConstructor) - { - MethodWrapper finalize = clazz.GetMethodWrapper(StringConstants.FINALIZE, StringConstants.SIG_VOID, true); - keepAlive = finalize != null && finalize.DeclaringType != CoreClasses.java.lang.Object.Wrapper && finalize.DeclaringType != CoreClasses.cli.System.Object.Wrapper && finalize.DeclaringType != CoreClasses.java.lang.Throwable.Wrapper && finalize.DeclaringType != CoreClasses.cli.System.Exception.Wrapper; - } -#if STATIC_COMPILER - replacedMethodWrappers = clazz.GetReplacedMethodsFor(mw); -#endif - - TypeWrapper[] args = mw.GetParameters(); - for (int i = 0; i < args.Length; i++) - { - if (args[i].IsUnloadable) - { - ilGenerator.EmitLdarg(i + (m.IsStatic ? 0 : 1)); - EmitDynamicCast(args[i]); - ilGenerator.Emit(OpCodes.Pop); - } - } - - Profiler.Enter("MethodAnalyzer"); - try - { - if (classFile.MajorVersion < 51 && m.HasJsr) - { - JsrInliner.InlineJsrs(classLoader, mw, classFile, m); - } - MethodAnalyzer verifier = new MethodAnalyzer(host, clazz, mw, classFile, m, classLoader); - exceptions = MethodAnalyzer.UntangleExceptionBlocks(classFile, m); - ma = verifier.GetCodeInfoAndErrors(exceptions, out harderrors); - localVars = new LocalVarInfo(ma, classFile, m, exceptions, mw, classLoader); - } - finally - { - Profiler.Leave("MethodAnalyzer"); - } - - if (m.LineNumberTableAttribute != null) - { - if (classLoader.EmitDebugInfo) - { - emitLineNumbers = true; - } - else if (classLoader.EmitStackTraceInfo) - { - InstructionFlags[] flags = ComputePartialReachability(0, false); - for (int i = 0; i < m.Instructions.Length; i++) - { - if ((flags[i] & InstructionFlags.Reachable) == 0) - { - // skip unreachable instructions - } - else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getfield - && VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 0))) - { - // loading a field from the current object cannot throw - } - else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putfield - && VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 1))) - { - // storing a field in the current object cannot throw - } - else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic - && classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz) - { - // loading a field from the current class cannot throw - } - else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic - && classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz) - { - // storing a field to the current class cannot throw - } - else if (ByteCodeMetaData.CanThrowException(m.Instructions[i].NormalizedOpCode)) - { - emitLineNumbers = true; - break; - } - } - } - } - - LocalVar[] locals = localVars.GetAllLocalVars(); - foreach (LocalVar v in locals) - { - if (v.isArg) - { - int arg = m.ArgMap[v.local]; - TypeWrapper tw; - if (m.IsStatic) - { - tw = args[arg]; - } - else if (arg == 0) - { - continue; - } - else - { - tw = args[arg - 1]; - } - if (!tw.IsUnloadable && - (v.type != tw || tw.TypeAsLocalOrStackType != tw.TypeAsSignatureType)) - { - v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); - if (debug && v.name != null) - { - v.builder.SetLocalSymInfo(v.name); - } - v.isArg = false; - ilGenerator.EmitLdarg(arg); - tw.EmitConvSignatureTypeToStackType(ilGenerator); - ilGenerator.Emit(OpCodes.Stloc, v.builder); - } - } - } - - // if we're emitting debugging information, we need to use scopes for local variables - if (debug) - { - SetupLocalVariableScopes(); - } - - Workaroundx64JitBug(args); - } - - // workaround for x64 JIT bug - // https://connect.microsoft.com/VisualStudio/feedback/details/636466/variable-is-not-incrementing-in-c-release-x64#details - // (see also https://sourceforge.net/mailarchive/message.php?msg_id=28250469) - private void Workaroundx64JitBug(TypeWrapper[] args) - { - if (args.Length > (m.IsStatic ? 4 : 3) && m.ExceptionTable.Length != 0) - { - bool[] workarounds = null; - InstructionFlags[] flags = ComputePartialReachability(0, false); - for (int i = 0; i < m.Instructions.Length; i++) - { - if ((flags[i] & InstructionFlags.Reachable) == 0) - { - // skip unreachable instructions - } - else - { - switch (m.Instructions[i].NormalizedOpCode) - { - case NormalizedByteCode.__iinc: - case NormalizedByteCode.__astore: - case NormalizedByteCode.__istore: - case NormalizedByteCode.__lstore: - case NormalizedByteCode.__fstore: - case NormalizedByteCode.__dstore: - int arg = m.IsStatic ? m.Instructions[i].Arg1 : m.Instructions[i].Arg1 - 1; - if (arg >= 3 && arg < args.Length) - { - if (workarounds == null) - { - workarounds = new bool[args.Length + 1]; - } - workarounds[m.Instructions[i].Arg1] = true; - } - break; - } - } - } - if (workarounds != null) - { - for (int i = 0; i < workarounds.Length; i++) - { - if (workarounds[i]) - { - // TODO prevent this from getting optimized away - ilGenerator.EmitLdarga(i); - ilGenerator.Emit(OpCodes.Pop); - } - } - } - } - } - - private void SetupLocalVariableScopes() - { - LocalVariableTableEntry[] lvt = m.LocalVariableTableAttribute; - if (lvt != null) - { - scopeBegin = new int[m.Instructions.Length]; - scopeClose = new int[m.Instructions.Length]; - - // FXBUG make sure we always have an outer scope - // (otherwise LocalBuilder.SetLocalSymInfo() might throw an IndexOutOfRangeException) - // fix for bug 2881954. - scopeBegin[0]++; - scopeClose[m.Instructions.Length - 1]++; - - for (int i = 0; i < lvt.Length; i++) - { - int startIndex = SafeFindPcIndex(lvt[i].start_pc); - int endIndex = SafeFindPcIndex(lvt[i].start_pc + lvt[i].length); - if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) - { - if (startIndex > 0) - { - // NOTE javac (correctly) sets start_pc of the LVT entry to the instruction - // following the store that first initializes the local, so we have to - // detect that case and adjust our local scope (because we'll be creating - // the local when we encounter the first store). - LocalVar v = localVars.GetLocalVar(startIndex - 1); - if (v != null && v.local == lvt[i].index) - { - startIndex--; - } - } - scopeBegin[startIndex]++; - scopeClose[endIndex]++; - } - } - } - } - - private int SafeFindPcIndex(int pc) - { - for (int i = 0; i < m.Instructions.Length; i++) - { - if (m.Instructions[i].PC >= pc) - { - return i; - } - } - return -1; - } - - private sealed class ReturnCookie - { - private readonly CodeEmitterLabel stub; - private readonly CodeEmitterLocal local; - - internal ReturnCookie(CodeEmitterLabel stub, CodeEmitterLocal local) - { - this.stub = stub; - this.local = local; - } - - internal void EmitRet(CodeEmitter ilgen) - { - ilgen.MarkLabel(stub); - if (local != null) - { - ilgen.Emit(OpCodes.Ldloc, local); - } - ilgen.Emit(OpCodes.Ret); - } - } - - private sealed class BranchCookie - { - // NOTE Stub gets used for both the push stub (inside the exception block) as well as the pop stub (outside the block) - internal CodeEmitterLabel Stub; - internal CodeEmitterLabel TargetLabel; - internal bool ContentOnStack; - internal readonly int TargetIndex; - internal DupHelper dh; - - internal BranchCookie(Compiler compiler, int stackHeight, int targetIndex) - { - this.Stub = compiler.ilGenerator.DefineLabel(); - this.TargetIndex = targetIndex; - this.dh = new DupHelper(compiler, stackHeight); - } - - internal BranchCookie(CodeEmitterLabel label, int targetIndex) - { - this.Stub = label; - this.TargetIndex = targetIndex; - } - } - - private struct DupHelper - { - private enum StackType : byte - { - Null, - New, - This, - UnitializedThis, - FaultBlockException, - Other - } - private readonly Compiler compiler; - private readonly StackType[] types; - private readonly CodeEmitterLocal[] locals; - - internal DupHelper(Compiler compiler, int count) - { - this.compiler = compiler; - types = new StackType[count]; - locals = new CodeEmitterLocal[count]; - } - - internal void Release() - { - foreach (CodeEmitterLocal lb in locals) - { - if (lb != null) - { - compiler.ilGenerator.ReleaseTempLocal(lb); - } - } - } - - internal int Count - { - get - { - return types.Length; - } - } - - internal void SetType(int i, TypeWrapper type) - { - if (type == VerifierTypeWrapper.Null) - { - types[i] = StackType.Null; - } - else if (VerifierTypeWrapper.IsNew(type)) - { - // new objects aren't really there on the stack - types[i] = StackType.New; - } - else if (VerifierTypeWrapper.IsThis(type)) - { - types[i] = StackType.This; - } - else if (type == VerifierTypeWrapper.UninitializedThis) - { - // uninitialized references cannot be stored in a local, but we can reload them - types[i] = StackType.UnitializedThis; - } - else if (VerifierTypeWrapper.IsFaultBlockException(type)) - { - types[i] = StackType.FaultBlockException; - } - else - { - types[i] = StackType.Other; - locals[i] = compiler.ilGenerator.AllocTempLocal(compiler.GetLocalBuilderType(type)); - } - } - - internal void Load(int i) - { - switch (types[i]) - { - case StackType.Null: - compiler.ilGenerator.Emit(OpCodes.Ldnull); - break; - case StackType.New: - case StackType.FaultBlockException: - // objects aren't really there on the stack - break; - case StackType.This: - case StackType.UnitializedThis: - compiler.ilGenerator.Emit(OpCodes.Ldarg_0); - break; - case StackType.Other: - compiler.ilGenerator.Emit(OpCodes.Ldloc, locals[i]); - break; - default: - throw new InvalidOperationException(); - } - } - - internal void Store(int i) - { - switch (types[i]) - { - case StackType.Null: - case StackType.This: - case StackType.UnitializedThis: - compiler.ilGenerator.Emit(OpCodes.Pop); - break; - case StackType.New: - case StackType.FaultBlockException: - // objects aren't really there on the stack - break; - case StackType.Other: - compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]); - break; - default: - throw new InvalidOperationException(); - } - } - } - - internal static void Compile(DynamicTypeWrapper.FinishContext context, TypeWrapper host, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ref bool nonleaf) - { - ClassLoaderWrapper classLoader = clazz.GetClassLoader(); - if (classLoader.EmitDebugInfo) - { - if (classFile.SourcePath != null) - { - ilGenerator.DefineSymbolDocument(classLoader.GetTypeWrapperFactory().ModuleBuilder, classFile.SourcePath, SymLanguageType.Java, Guid.Empty, SymDocumentType.Text); - // the very first instruction in the method must have an associated line number, to be able - // to step into the method in Visual Studio .NET - ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute; - if (table != null) - { - int firstPC = int.MaxValue; - int firstLine = -1; - for (int i = 0; i < table.Length; i++) - { - if (table[i].start_pc < firstPC && table[i].line_number != 0) - { - firstLine = table[i].line_number; - firstPC = table[i].start_pc; - } - } - if (firstLine > 0) - { - ilGenerator.SetLineNumber((ushort)firstLine); - } - } - } - } - Compiler c; - try - { - Profiler.Enter("new Compiler"); - try - { - c = new Compiler(context, host, clazz, mw, classFile, m, ilGenerator, classLoader); - } - finally - { - Profiler.Leave("new Compiler"); - } - } - catch (VerifyError x) - { -#if STATIC_COMPILER - classLoader.IssueMessage(Message.EmittedVerificationError, classFile.Name + "." + m.Name + m.Signature, x.Message); -#endif - Tracer.Error(Tracer.Verifier, x.ToString()); - clazz.SetHasVerifyError(); - // because in Java the method is only verified if it is actually called, - // we generate code here to throw the VerificationError - ilGenerator.EmitThrow("java.lang.VerifyError", x.Message); - return; - } - catch (ClassFormatError x) - { -#if STATIC_COMPILER - classLoader.IssueMessage(Message.EmittedClassFormatError, classFile.Name + "." + m.Name + m.Signature, x.Message); -#endif - Tracer.Error(Tracer.Verifier, x.ToString()); - clazz.SetHasClassFormatError(); - ilGenerator.EmitThrow("java.lang.ClassFormatError", x.Message); - return; - } - Profiler.Enter("Compile"); - try - { - if (m.IsSynchronized && m.IsStatic) - { - clazz.EmitClassLiteral(ilGenerator); - ilGenerator.Emit(OpCodes.Dup); - CodeEmitterLocal monitor = ilGenerator.DeclareLocal(Types.Object); - ilGenerator.Emit(OpCodes.Stloc, monitor); - ilGenerator.EmitMonitorEnter(); - ilGenerator.BeginExceptionBlock(); - Block b = new Block(c, 0, int.MaxValue, -1, new List(), true); - c.Compile(b, 0); - b.Leave(); - ilGenerator.BeginFinallyBlock(); - ilGenerator.Emit(OpCodes.Ldloc, monitor); - ilGenerator.EmitMonitorExit(); - ilGenerator.Emit(OpCodes.Endfinally); - ilGenerator.EndExceptionBlock(); - b.LeaveStubs(new Block(c, 0, int.MaxValue, -1, null, false)); - } - else - { - Block b = new Block(c, 0, int.MaxValue, -1, null, false); - c.Compile(b, 0); - b.Leave(); - } - nonleaf = c.nonleaf; - } - finally - { - Profiler.Leave("Compile"); - } - } - - private sealed class Block - { - private readonly Compiler compiler; - private readonly CodeEmitter ilgen; - private readonly int beginIndex; - private readonly int endIndex; - private readonly int exceptionIndex; - private List exits; - private readonly bool nested; - private readonly object[] labels; - - internal Block(Compiler compiler, int beginIndex, int endIndex, int exceptionIndex, List exits, bool nested) - { - this.compiler = compiler; - this.ilgen = compiler.ilGenerator; - this.beginIndex = beginIndex; - this.endIndex = endIndex; - this.exceptionIndex = exceptionIndex; - this.exits = exits; - this.nested = nested; - labels = new object[compiler.m.Instructions.Length]; - } - - internal int EndIndex - { - get - { - return endIndex; - } - } - - internal int ExceptionIndex - { - get - { - return exceptionIndex; - } - } - - internal void SetBackwardBranchLabel(int instructionIndex, BranchCookie bc) - { - // NOTE we're overwriting the label that is already there - labels[instructionIndex] = bc.Stub; - if (exits == null) - { - exits = new List(); - } - exits.Add(bc); - } - - internal CodeEmitterLabel GetLabel(int targetIndex) - { - if (IsInRange(targetIndex)) - { - CodeEmitterLabel l = (CodeEmitterLabel)labels[targetIndex]; - if (l == null) - { - l = ilgen.DefineLabel(); - labels[targetIndex] = l; - } - return l; - } - else - { - BranchCookie l = (BranchCookie)labels[targetIndex]; - if (l == null) - { - // if we're branching out of the current exception block, we need to indirect this thru a stub - // that saves the stack and uses leave to leave the exception block (to another stub that recovers - // the stack) - int stackHeight = compiler.ma.GetStackHeight(targetIndex); - BranchCookie bc = new BranchCookie(compiler, stackHeight, targetIndex); - bc.ContentOnStack = true; - for (int i = 0; i < stackHeight; i++) - { - bc.dh.SetType(i, compiler.ma.GetRawStackTypeWrapper(targetIndex, i)); - } - exits.Add(bc); - l = bc; - labels[targetIndex] = l; - } - return l.Stub; - } - } - - internal bool HasLabel(int instructionIndex) - { - return labels[instructionIndex] != null; - } - - internal void MarkLabel(int instructionIndex) - { - object label = labels[instructionIndex]; - if (label == null) - { - CodeEmitterLabel l = ilgen.DefineLabel(); - ilgen.MarkLabel(l); - labels[instructionIndex] = l; - } - else - { - ilgen.MarkLabel((CodeEmitterLabel)label); - } - } - - internal bool IsInRange(int index) - { - return beginIndex <= index && index < endIndex; - } - - internal void Leave() - { - if (exits != null) - { - for (int i = 0; i < exits.Count; i++) - { - object exit = exits[i]; - BranchCookie bc = exit as BranchCookie; - if (bc != null && bc.ContentOnStack) - { - bc.ContentOnStack = false; - int stack = bc.dh.Count; - // HACK this is unreachable code, but we make sure that - // forward pass verification always yields a valid stack - // (this is required for unreachable leave stubs that are - // generated for unreachable code that follows an - // embedded exception emitted by the compiler for invalid - // code (e.g. NoSuchFieldError)) - for (int n = stack - 1; n >= 0; n--) - { - bc.dh.Load(n); - } - ilgen.MarkLabel(bc.Stub); - for (int n = 0; n < stack; n++) - { - bc.dh.Store(n); - } - if (bc.TargetIndex == -1) - { - ilgen.EmitBr(bc.TargetLabel); - } - else - { - bc.Stub = ilgen.DefineLabel(); - ilgen.EmitLeave(bc.Stub); - } - } - } - } - } - - internal void LeaveStubs(Block newBlock) - { - if (exits != null) - { - for (int i = 0; i < exits.Count; i++) - { - object exit = exits[i]; - ReturnCookie rc = exit as ReturnCookie; - if (rc != null) - { - if (newBlock.IsNested) - { - newBlock.exits.Add(rc); - } - else - { - rc.EmitRet(ilgen); - } - } - else - { - BranchCookie bc = exit as BranchCookie; - if (bc != null && bc.TargetIndex != -1) - { - Debug.Assert(!bc.ContentOnStack); - // if the target is within the new block, we handle it, otherwise we - // defer the cookie to our caller - if (newBlock.IsInRange(bc.TargetIndex)) - { - bc.ContentOnStack = true; - ilgen.MarkLabel(bc.Stub); - int stack = bc.dh.Count; - for (int n = stack - 1; n >= 0; n--) - { - bc.dh.Load(n); - } - ilgen.EmitBr(newBlock.GetLabel(bc.TargetIndex)); - } - else - { - newBlock.exits.Add(bc); - } - } - } - } - } - } - - internal void AddExitHack(object bc) - { - exits.Add(bc); - } - - internal bool IsNested - { - get - { - return nested; - } - } - } - - private void Compile(Block block, int startIndex) - { - InstructionFlags[] flags = ComputePartialReachability(startIndex, true); - ExceptionTableEntry[] exceptions = GetExceptionTableFor(flags); - int exceptionIndex = 0; - Instruction[] code = m.Instructions; - Stack blockStack = new Stack(); - bool instructionIsForwardReachable = true; - if (startIndex != 0) - { - for (int i = 0; i < flags.Length; i++) - { - if ((flags[i] & InstructionFlags.Reachable) != 0) - { - if (i < startIndex) - { - instructionIsForwardReachable = false; - ilGenerator.EmitBr(block.GetLabel(startIndex)); - } - break; - } - } - } - for (int i = 0; i < code.Length; i++) - { - Instruction instr = code[i]; - - if (scopeBegin != null) - { - for (int j = scopeClose[i]; j > 0; j--) - { - ilGenerator.EndScope(); - } - for (int j = scopeBegin[i]; j > 0; j--) - { - ilGenerator.BeginScope(); - } - } - - // if we've left the current exception block, do the exit processing - while (block.EndIndex == i) - { - block.Leave(); - - ExceptionTableEntry exc = exceptions[block.ExceptionIndex]; - - Block prevBlock = block; - block = blockStack.Pop(); - - exceptionIndex = block.ExceptionIndex + 1; - // skip over exception handlers that are no longer relevant - for (; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].endIndex <= i; exceptionIndex++) - { - } - - int handlerIndex = exc.handlerIndex; - - if (exc.catch_type == 0 && VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(handlerIndex, 0))) - { - if (exc.isFinally) - { - ilGenerator.BeginFinallyBlock(); - } - else - { - ilGenerator.BeginFaultBlock(); - } - Block b = new Block(this, 0, block.EndIndex, -1, null, false); - Compile(b, handlerIndex); - b.Leave(); - ilGenerator.EndExceptionBlock(); - } - else - { - TypeWrapper exceptionTypeWrapper; - bool remap; - if (exc.catch_type == 0) - { - exceptionTypeWrapper = CoreClasses.java.lang.Throwable.Wrapper; - remap = true; - } - else - { - exceptionTypeWrapper = classFile.GetConstantPoolClassType(exc.catch_type); - remap = exceptionTypeWrapper.IsUnloadable || !exceptionTypeWrapper.IsSubTypeOf(CoreClasses.cli.System.Exception.Wrapper); - } - Type excType = exceptionTypeWrapper.TypeAsExceptionType; - bool mapSafe = !exceptionTypeWrapper.IsUnloadable && !exceptionTypeWrapper.IsMapUnsafeException && !exceptionTypeWrapper.IsRemapped; - if (mapSafe) - { - ilGenerator.BeginCatchBlock(excType); - } - else - { - ilGenerator.BeginCatchBlock(Types.Exception); - } - BranchCookie bc = new BranchCookie(this, 1, exc.handlerIndex); - prevBlock.AddExitHack(bc); - Instruction handlerInstr = code[handlerIndex]; - bool unusedException = (handlerInstr.NormalizedOpCode == NormalizedByteCode.__pop || - (handlerInstr.NormalizedOpCode == NormalizedByteCode.__astore && - localVars.GetLocalVar(handlerIndex) == null)); - int mapFlags = unusedException ? 2 : 0; - if (mapSafe && unusedException) - { - // we don't need to do anything with the exception - } - else if (mapSafe) - { - ilGenerator.EmitLdc_I4(mapFlags | 1); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(excType)); - } - else if (exceptionTypeWrapper == CoreClasses.java.lang.Throwable.Wrapper) - { - ilGenerator.EmitLdc_I4(mapFlags); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); - } - else - { - ilGenerator.EmitLdc_I4(mapFlags | (remap ? 0 : 1)); - if (exceptionTypeWrapper.IsUnloadable) - { - Profiler.Count("EmitDynamicExceptionHandler"); - EmitDynamicClassLiteral(exceptionTypeWrapper); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicMapException); - } - else - { - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(excType)); - } - if (!unusedException) - { - ilGenerator.Emit(OpCodes.Dup); - } - CodeEmitterLabel leave = ilGenerator.DefineLabel(); - ilGenerator.EmitBrtrue(leave); - ilGenerator.Emit(OpCodes.Rethrow); - ilGenerator.MarkLabel(leave); - } - if (unusedException) - { - // we must still have an item on the stack, even though it isn't used! - bc.dh.SetType(0, VerifierTypeWrapper.Null); - } - else - { - bc.dh.SetType(0, exceptionTypeWrapper); - bc.dh.Store(0); - } - ilGenerator.EmitLeave(bc.Stub); - ilGenerator.EndExceptionBlock(); - } - prevBlock.LeaveStubs(block); - } - - if ((flags[i] & InstructionFlags.Reachable) == 0) - { - // skip any unreachable instructions - continue; - } - - // if there was a forward branch to this instruction, it is forward reachable - instructionIsForwardReachable |= block.HasLabel(i); - - if (block.HasLabel(i) || (flags[i] & InstructionFlags.BranchTarget) != 0) - { - block.MarkLabel(i); - } - - // if the instruction is only backward reachable, ECMA says it must have an empty stack, - // so we move the stack to locals - if (!instructionIsForwardReachable) - { - int stackHeight = ma.GetStackHeight(i); - if (stackHeight != 0) - { - BranchCookie bc = new BranchCookie(this, stackHeight, -1); - bc.ContentOnStack = true; - bc.TargetLabel = ilGenerator.DefineLabel(); - ilGenerator.MarkLabel(bc.TargetLabel); - for (int j = 0; j < stackHeight; j++) - { - bc.dh.SetType(j, ma.GetRawStackTypeWrapper(i, j)); - } - for (int j = stackHeight - 1; j >= 0; j--) - { - bc.dh.Load(j); - } - block.SetBackwardBranchLabel(i, bc); - } - } - - // if we're entering an exception block, we need to setup the exception block and - // transfer the stack into it - // Note that an exception block that *starts* at an unreachable instruction, - // is completely unreachable, because it is impossible to branch into an exception block. - for (; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].startIndex == i; exceptionIndex++) - { - int stackHeight = ma.GetStackHeight(i); - if (stackHeight != 0) - { - DupHelper dh = new DupHelper(this, stackHeight); - for (int k = 0; k < stackHeight; k++) - { - dh.SetType(k, ma.GetRawStackTypeWrapper(i, k)); - dh.Store(k); - } - ilGenerator.BeginExceptionBlock(); - for (int k = stackHeight - 1; k >= 0; k--) - { - dh.Load(k); - } - dh.Release(); - } - else - { - ilGenerator.BeginExceptionBlock(); - } - blockStack.Push(block); - block = new Block(this, exceptions[exceptionIndex].startIndex, exceptions[exceptionIndex].endIndex, exceptionIndex, new List(), true); - block.MarkLabel(i); - } - - if (emitLineNumbers) - { - ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute; - for (int j = 0; j < table.Length; j++) - { - if (table[j].start_pc == code[i].PC && table[j].line_number != 0) - { - ilGenerator.SetLineNumber(table[j].line_number); - break; - } - } - } - - if (keepAlive) - { - // JSR 133 specifies that a finalizer cannot run while the constructor is still in progress. - // This code attempts to implement that by adding calls to GC.KeepAlive(this) before return, - // backward branches and throw instructions. I don't think it is perfect, you may be able to - // fool it by calling a trivial method that loops forever which the CLR JIT will then inline - // and see that control flow doesn't continue and hence the lifetime of "this" will be - // shorter than the constructor. - switch (ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode)) - { - case ByteCodeFlowControl.Return: - ilGenerator.Emit(OpCodes.Ldarg_0); - ilGenerator.Emit(OpCodes.Call, keepAliveMethod); - break; - case ByteCodeFlowControl.Branch: - case ByteCodeFlowControl.CondBranch: - if (instr.TargetIndex <= i) - { - ilGenerator.Emit(OpCodes.Ldarg_0); - ilGenerator.Emit(OpCodes.Call, keepAliveMethod); - } - break; - case ByteCodeFlowControl.Throw: - case ByteCodeFlowControl.Switch: - if (ma.GetLocalTypeWrapper(i, 0) != VerifierTypeWrapper.UninitializedThis) - { - ilGenerator.Emit(OpCodes.Ldarg_0); - ilGenerator.Emit(OpCodes.Call, keepAliveMethod); - } - break; - } - } - - switch (instr.NormalizedOpCode) - { - case NormalizedByteCode.__getstatic: - { - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); - if (cpi.GetClassType() != clazz) - { - // we may trigger a static initializer, which is equivalent to a call - nonleaf = true; - } - FieldWrapper field = cpi.GetField(); - field.EmitGet(ilGenerator); - field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator); - break; - } - case NormalizedByteCode.__getfield: - { - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); - FieldWrapper field = cpi.GetField(); - if (ma.GetStackTypeWrapper(i, 0).IsUnloadable) - { - if (field.IsProtected) - { - // downcast receiver to our type - clazz.EmitCheckcast(ilGenerator); - } - else - { - // downcast receiver to field declaring type - field.DeclaringType.EmitCheckcast(ilGenerator); - } - } - field.EmitGet(ilGenerator); - field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator); - break; - } - case NormalizedByteCode.__putstatic: - { - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); - if (cpi.GetClassType() != clazz) - { - // we may trigger a static initializer, which is equivalent to a call - nonleaf = true; - } - FieldWrapper field = cpi.GetField(); - TypeWrapper tw = field.FieldTypeWrapper; - tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); - if (strictfp) - { - // no need to convert - } - else if (tw == PrimitiveTypeWrapper.DOUBLE) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - field.EmitSet(ilGenerator); - break; - } - case NormalizedByteCode.__putfield: - { - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); - FieldWrapper field = cpi.GetField(); - TypeWrapper tw = field.FieldTypeWrapper; - if (ma.GetStackTypeWrapper(i, 1).IsUnloadable) - { - CodeEmitterLocal temp = ilGenerator.UnsafeAllocTempLocal(tw.TypeAsLocalOrStackType); - ilGenerator.Emit(OpCodes.Stloc, temp); - if (field.IsProtected) - { - // downcast receiver to our type - clazz.EmitCheckcast(ilGenerator); - } - else - { - // downcast receiver to field declaring type - field.DeclaringType.EmitCheckcast(ilGenerator); - } - ilGenerator.Emit(OpCodes.Ldloc, temp); - } - tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); - if (strictfp) - { - // no need to convert - } - else if (tw == PrimitiveTypeWrapper.DOUBLE) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - field.EmitSet(ilGenerator); - break; - } - case NormalizedByteCode.__dynamic_getstatic: - case NormalizedByteCode.__dynamic_putstatic: - case NormalizedByteCode.__dynamic_getfield: - case NormalizedByteCode.__dynamic_putfield: - nonleaf = true; - DynamicGetPutField(instr, i); - break; - case NormalizedByteCode.__aconst_null: - ilGenerator.Emit(OpCodes.Ldnull); - break; - case NormalizedByteCode.__iconst: - ilGenerator.EmitLdc_I4(instr.NormalizedArg1); - break; - case NormalizedByteCode.__lconst_0: - ilGenerator.EmitLdc_I8(0L); - break; - case NormalizedByteCode.__lconst_1: - ilGenerator.EmitLdc_I8(1L); - break; - case NormalizedByteCode.__fconst_0: - case NormalizedByteCode.__dconst_0: - // floats are stored as native size on the stack, so both R4 and R8 are the same - ilGenerator.EmitLdc_R4(0.0f); - break; - case NormalizedByteCode.__fconst_1: - case NormalizedByteCode.__dconst_1: - // floats are stored as native size on the stack, so both R4 and R8 are the same - ilGenerator.EmitLdc_R4(1.0f); - break; - case NormalizedByteCode.__fconst_2: - ilGenerator.EmitLdc_R4(2.0f); - break; - case NormalizedByteCode.__ldc_nothrow: - case NormalizedByteCode.__ldc: - EmitLoadConstant(ilGenerator, instr.Arg1); - break; - case NormalizedByteCode.__invokedynamic: - { - ClassFile.ConstantPoolItemInvokeDynamic cpi = classFile.GetInvokeDynamic(instr.Arg1); - CastInterfaceArgs(null, cpi.GetArgTypes(), i, false); - if (!LambdaMetafactory.Emit(context, classFile, instr.Arg1, cpi, ilGenerator)) - { - EmitInvokeDynamic(cpi); - EmitReturnTypeConversion(cpi.GetRetType()); - } - nonleaf = true; - break; - } - case NormalizedByteCode.__dynamic_invokestatic: - case NormalizedByteCode.__privileged_invokestatic: - case NormalizedByteCode.__invokestatic: - case NormalizedByteCode.__methodhandle_link: - { - MethodWrapper method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); - if (method.IsIntrinsic && method.EmitIntrinsic(new EmitIntrinsicContext(method, context, ilGenerator, ma, i, mw, classFile, code, flags))) - { - break; - } - // if the stack values don't match the argument types (for interface argument types) - // we must emit code to cast the stack value to the interface type - CastInterfaceArgs(method.DeclaringType, method.GetParameters(), i, false); - if (method.HasCallerID) - { - context.EmitCallerID(ilGenerator, m.IsLambdaFormCompiled); - } - method.EmitCall(ilGenerator); - EmitReturnTypeConversion(method.ReturnType); - nonleaf = true; - break; - } - case NormalizedByteCode.__dynamic_invokeinterface: - case NormalizedByteCode.__dynamic_invokevirtual: - case NormalizedByteCode.__dynamic_invokespecial: - case NormalizedByteCode.__privileged_invokevirtual: - case NormalizedByteCode.__privileged_invokespecial: - case NormalizedByteCode.__invokevirtual: - case NormalizedByteCode.__invokeinterface: - case NormalizedByteCode.__invokespecial: - case NormalizedByteCode.__methodhandle_invoke: - { - bool isinvokespecial = instr.NormalizedOpCode == NormalizedByteCode.__invokespecial - || instr.NormalizedOpCode == NormalizedByteCode.__dynamic_invokespecial - || instr.NormalizedOpCode == NormalizedByteCode.__privileged_invokespecial; - MethodWrapper method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); - int argcount = method.GetParameters().Length; - TypeWrapper type = ma.GetRawStackTypeWrapper(i, argcount); - TypeWrapper thisType = ComputeThisType(type, method, instr.NormalizedOpCode); - - EmitIntrinsicContext eic = new EmitIntrinsicContext(method, context, ilGenerator, ma, i, mw, classFile, code, flags); - if (method.IsIntrinsic && method.EmitIntrinsic(eic)) - { - nonleaf |= eic.NonLeaf; - break; - } - - nonleaf = true; - - // HACK this code is duplicated in java.lang.invoke.cs - if (method.IsFinalizeOrClone) - { - // HACK we may need to redirect finalize or clone from java.lang.Object/Throwable - // to a more specific base type. - if (thisType.IsAssignableTo(CoreClasses.cli.System.Object.Wrapper)) - { - method = CoreClasses.cli.System.Object.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); - } - else if (thisType.IsAssignableTo(CoreClasses.cli.System.Exception.Wrapper)) - { - method = CoreClasses.cli.System.Exception.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); - } - else if (thisType.IsAssignableTo(CoreClasses.java.lang.Throwable.Wrapper)) - { - method = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); - } - } - - // if the stack values don't match the argument types (for interface argument types) - // we must emit code to cast the stack value to the interface type - if (isinvokespecial && method.IsConstructor && VerifierTypeWrapper.IsNew(type)) - { - CastInterfaceArgs(method.DeclaringType, method.GetParameters(), i, false); - } - else - { - // the this reference is included in the argument list because it may also need to be cast - TypeWrapper[] methodArgs = method.GetParameters(); - TypeWrapper[] args = new TypeWrapper[methodArgs.Length + 1]; - methodArgs.CopyTo(args, 1); - if (instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface) - { - args[0] = method.DeclaringType; - } - else - { - args[0] = thisType; - } - CastInterfaceArgs(method.DeclaringType, args, i, true); - } - - if (isinvokespecial && method.IsConstructor) - { - if (VerifierTypeWrapper.IsNew(type)) - { - // we have to construct a list of all the unitialized references to the object - // we're about to create on the stack, so that we can reconstruct the stack after - // the "newobj" instruction - int trivcount = 0; - bool nontrivial = false; - bool[] stackfix = new bool[ma.GetStackHeight(i) - (argcount + 1)]; - for (int j = 0; j < stackfix.Length; j++) - { - if (ma.GetRawStackTypeWrapper(i, argcount + 1 + j) == type) - { - stackfix[j] = true; - if (trivcount == j) - { - trivcount++; - } - else - { - // if there is other stuff on the stack between the new object - // references, we need to do more work to construct the proper stack - // layout after the newobj instruction - nontrivial = true; - } - } - } - for (int j = 0; !nontrivial && j < m.MaxLocals; j++) - { - if (ma.GetLocalTypeWrapper(i, j) == type) - { - nontrivial = true; - } - } - if (!thisType.IsUnloadable && thisType.IsSubTypeOf(CoreClasses.java.lang.Throwable.Wrapper)) - { - // if the next instruction is an athrow and the exception type - // doesn't override fillInStackTrace, we can suppress the call - // to fillInStackTrace from the constructor (and this is - // a huge perf win) - // NOTE we also can't call suppressFillInStackTrace for non-Java - // exceptions (because then the suppress flag won't be cleared), - // but this case is handled by the "is fillInStackTrace overridden?" - // test, because cli.System.Exception overrides fillInStackTrace. - if (code[i + 1].NormalizedOpCode == NormalizedByteCode.__athrow) - { - if (thisType.GetMethodWrapper("fillInStackTrace", "()Ljava.lang.Throwable;", true).DeclaringType == CoreClasses.java.lang.Throwable.Wrapper) - { - ilGenerator.Emit(OpCodes.Call, suppressFillInStackTraceMethod); - } - if ((flags[i + 1] & InstructionFlags.BranchTarget) == 0) - { - code[i + 1].PatchOpCode(NormalizedByteCode.__athrow_no_unmap); - } - } - } - method.EmitNewobj(ilGenerator); - if (!thisType.IsUnloadable && thisType.IsSubTypeOf(CoreClasses.cli.System.Exception.Wrapper)) - { - // we call Throwable.__() to disable remapping the exception - ilGenerator.Emit(OpCodes.Call, fixateExceptionMethod); - } - if (nontrivial) - { - // this could be done a little more efficiently, but since in practice this - // code never runs (for code compiled from Java source) it doesn't - // really matter - CodeEmitterLocal newobj = ilGenerator.DeclareLocal(GetLocalBuilderType(thisType)); - ilGenerator.Emit(OpCodes.Stloc, newobj); - CodeEmitterLocal[] tempstack = new CodeEmitterLocal[stackfix.Length]; - for (int j = 0; j < stackfix.Length; j++) - { - if (!stackfix[j]) - { - TypeWrapper stacktype = ma.GetStackTypeWrapper(i, argcount + 1 + j); - // it could be another new object reference (not from current invokespecial - // instruction) - if (stacktype == VerifierTypeWrapper.Null) - { - // NOTE we abuse the newobj local as a cookie to signal null! - tempstack[j] = newobj; - ilGenerator.Emit(OpCodes.Pop); - } - else if (!VerifierTypeWrapper.IsNotPresentOnStack(stacktype)) - { - CodeEmitterLocal lb = ilGenerator.DeclareLocal(GetLocalBuilderType(stacktype)); - ilGenerator.Emit(OpCodes.Stloc, lb); - tempstack[j] = lb; - } - } - } - for (int j = stackfix.Length - 1; j >= 0; j--) - { - if (stackfix[j]) - { - ilGenerator.Emit(OpCodes.Ldloc, newobj); - } - else if (tempstack[j] != null) - { - // NOTE we abuse the newobj local as a cookie to signal null! - if (tempstack[j] == newobj) - { - ilGenerator.Emit(OpCodes.Ldnull); - } - else - { - ilGenerator.Emit(OpCodes.Ldloc, tempstack[j]); - } - } - } - LocalVar[] locals = localVars.GetLocalVarsForInvokeSpecial(i); - for (int j = 0; j < locals.Length; j++) - { - if (locals[j] != null) - { - if (locals[j].builder == null) - { - // for invokespecial the resulting type can never be null - locals[j].builder = ilGenerator.DeclareLocal(GetLocalBuilderType(locals[j].type)); - } - ilGenerator.Emit(OpCodes.Ldloc, newobj); - ilGenerator.Emit(OpCodes.Stloc, locals[j].builder); - } - } - } - else - { - if (trivcount == 0) - { - ilGenerator.Emit(OpCodes.Pop); - } - else - { - for (int j = 1; j < trivcount; j++) - { - ilGenerator.Emit(OpCodes.Dup); - } - } - } - } - else - { - Debug.Assert(type == VerifierTypeWrapper.UninitializedThis); - method.EmitCall(ilGenerator); - LocalVar[] locals = localVars.GetLocalVarsForInvokeSpecial(i); - for (int j = 0; j < locals.Length; j++) - { - if (locals[j] != null) - { - if (locals[j].builder == null) - { - // for invokespecial the resulting type can never be null - locals[j].builder = ilGenerator.DeclareLocal(GetLocalBuilderType(locals[j].type)); - } - ilGenerator.Emit(OpCodes.Ldarg_0); - ilGenerator.Emit(OpCodes.Stloc, locals[j].builder); - } - } - } - } - else - { - if (method.HasCallerID) - { - context.EmitCallerID(ilGenerator, m.IsLambdaFormCompiled); - } - - if (isinvokespecial) - { - if (VerifierTypeWrapper.IsThis(type)) - { - method.EmitCall(ilGenerator); - } - else if (method.IsPrivate) - { - // if the method is private, we can get away with a callvirt (and not generate the stub) - method.EmitCallvirt(ilGenerator); - } - else if (instr.NormalizedOpCode == NormalizedByteCode.__privileged_invokespecial) - { - method.EmitCall(ilGenerator); - } - else - { - ilGenerator.Emit(OpCodes.Callvirt, context.GetInvokeSpecialStub(method)); - } - } - else - { - // NOTE this check is written somewhat pessimistically, because we need to - // take remapped types into account. For example, Throwable.getClass() "overrides" - // the final Object.getClass() method and we don't want to call Object.getClass() - // on a Throwable instance, because that would yield unverifiable code (java.lang.Throwable - // extends System.Exception instead of java.lang.Object in the .NET type system). - if (VerifierTypeWrapper.IsThis(type) - && (method.IsFinal || clazz.IsFinal) - && clazz.GetMethodWrapper(method.Name, method.Signature, true) == method) - { - // we're calling a method on our own instance that can't possibly be overriden, - // so we don't need to use callvirt - method.EmitCall(ilGenerator); - } - else - { - method.EmitCallvirt(ilGenerator); - } - } - EmitReturnTypeConversion(method.ReturnType); - } - break; - } - case NormalizedByteCode.__clone_array: - ilGenerator.Emit(OpCodes.Callvirt, ArrayTypeWrapper.CloneMethod); - break; - case NormalizedByteCode.__return: - case NormalizedByteCode.__areturn: - case NormalizedByteCode.__ireturn: - case NormalizedByteCode.__lreturn: - case NormalizedByteCode.__freturn: - case NormalizedByteCode.__dreturn: - { - if (block.IsNested) - { - // if we're inside an exception block, copy TOS to local, emit "leave" and push item onto our "todo" list - CodeEmitterLocal local = null; - if (instr.NormalizedOpCode != NormalizedByteCode.__return) - { - TypeWrapper retTypeWrapper = mw.ReturnType; - retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); - local = ilGenerator.UnsafeAllocTempLocal(retTypeWrapper.TypeAsSignatureType); - ilGenerator.Emit(OpCodes.Stloc, local); - } - CodeEmitterLabel label = ilGenerator.DefineLabel(); - // NOTE leave automatically discards any junk that may be on the stack - ilGenerator.EmitLeave(label); - block.AddExitHack(new ReturnCookie(label, local)); - } - else - { - // HACK the x64 JIT is lame and optimizes calls before ret into a tail call - // and this makes the method disappear from the call stack, so we try to thwart that - // by inserting some bogus instructions between the call and the return. - // Note that this optimization doesn't appear to happen if the method has exception handlers, - // so in that case we don't do anything. - bool x64hack = false; -#if !NET_4_0 - if (exceptions.Length == 0 && i > 0) - { - int k = i - 1; - while (k > 0 && code[k].NormalizedOpCode == NormalizedByteCode.__nop) - { - k--; - } - switch (code[k].NormalizedOpCode) - { - case NormalizedByteCode.__invokeinterface: - case NormalizedByteCode.__invokespecial: - case NormalizedByteCode.__invokestatic: - case NormalizedByteCode.__invokevirtual: - x64hack = true; - break; - } - } -#endif - // if there is junk on the stack (other than the return value), we must pop it off - // because in .NET this is invalid (unlike in Java) - int stackHeight = ma.GetStackHeight(i); - if (instr.NormalizedOpCode == NormalizedByteCode.__return) - { - if (stackHeight != 0 || x64hack) - { - ilGenerator.EmitClearStack(); - } - ilGenerator.Emit(OpCodes.Ret); - } - else - { - TypeWrapper retTypeWrapper = mw.ReturnType; - retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); - if (stackHeight != 1) - { - CodeEmitterLocal local = ilGenerator.AllocTempLocal(retTypeWrapper.TypeAsSignatureType); - ilGenerator.Emit(OpCodes.Stloc, local); - ilGenerator.EmitClearStack(); - ilGenerator.Emit(OpCodes.Ldloc, local); - ilGenerator.ReleaseTempLocal(local); - } - else if (x64hack) - { - ilGenerator.EmitTailCallPrevention(); - } - ilGenerator.Emit(OpCodes.Ret); - } - } - break; - } - case NormalizedByteCode.__aload: - { - TypeWrapper type = ma.GetLocalTypeWrapper(i, instr.NormalizedArg1); - if (type == VerifierTypeWrapper.Null) - { - // if the local is known to be null, we just emit a null - ilGenerator.Emit(OpCodes.Ldnull); - } - else if (VerifierTypeWrapper.IsNotPresentOnStack(type)) - { - // since object isn't represented on the stack, we don't need to do anything here - } - else if (VerifierTypeWrapper.IsThis(type)) - { - ilGenerator.Emit(OpCodes.Ldarg_0); - } - else if (type == VerifierTypeWrapper.UninitializedThis) - { - // any unitialized this reference has to be loaded from arg 0 - // NOTE if the method overwrites the this references, it will always end up in - // a different local (due to the way the local variable liveness analysis works), - // so we don't have to worry about that. - ilGenerator.Emit(OpCodes.Ldarg_0); - } - else - { - LocalVar v = LoadLocal(i); - if (!type.IsUnloadable && (v.type.IsUnloadable || !v.type.IsAssignableTo(type))) - { - type.EmitCheckcast(ilGenerator); - } - } - break; - } - case NormalizedByteCode.__astore: - { - TypeWrapper type = ma.GetRawStackTypeWrapper(i, 0); - if (VerifierTypeWrapper.IsNotPresentOnStack(type)) - { - // object isn't really on the stack, so we can't copy it into the local - // (and the local doesn't exist anyway) - } - else if (type == VerifierTypeWrapper.UninitializedThis) - { - // any unitialized reference is always the this reference, we don't store anything - // here (because CLR won't allow unitialized references in locals) and then when - // the unitialized ref is loaded we redirect to the this reference - ilGenerator.Emit(OpCodes.Pop); - } - else - { - StoreLocal(i); - } - break; - } - case NormalizedByteCode.__iload: - case NormalizedByteCode.__lload: - case NormalizedByteCode.__fload: - case NormalizedByteCode.__dload: - LoadLocal(i); - break; - case NormalizedByteCode.__istore: - case NormalizedByteCode.__lstore: - StoreLocal(i); - break; - case NormalizedByteCode.__fstore: - StoreLocal(i); - break; - case NormalizedByteCode.__dstore: - if (ma.IsStackTypeExtendedDouble(i, 0)) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - StoreLocal(i); - break; - case NormalizedByteCode.__new: - { - TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); - if (wrapper.IsUnloadable) - { - Profiler.Count("EmitDynamicNewCheckOnly"); - // this is here to make sure we throw the exception in the right location (before - // evaluating the constructor arguments) - EmitDynamicClassLiteral(wrapper); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly); - } - else if (wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1, flags)) - { - // trigger cctor (as the spec requires) - wrapper.EmitRunClassConstructor(ilGenerator); - } - // we don't actually allocate the object here, the call to will be converted into a newobj instruction - break; - } - case NormalizedByteCode.__multianewarray: - { - CodeEmitterLocal localArray = ilGenerator.UnsafeAllocTempLocal(JVM.Import(typeof(int[]))); - CodeEmitterLocal localInt = ilGenerator.UnsafeAllocTempLocal(Types.Int32); - ilGenerator.EmitLdc_I4(instr.Arg2); - ilGenerator.Emit(OpCodes.Newarr, Types.Int32); - ilGenerator.Emit(OpCodes.Stloc, localArray); - for (int j = 1; j <= instr.Arg2; j++) - { - ilGenerator.Emit(OpCodes.Stloc, localInt); - ilGenerator.Emit(OpCodes.Ldloc, localArray); - ilGenerator.EmitLdc_I4(instr.Arg2 - j); - ilGenerator.Emit(OpCodes.Ldloc, localInt); - ilGenerator.Emit(OpCodes.Stelem_I4); - } - TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); - if (wrapper.IsUnloadable) - { - Profiler.Count("EmitDynamicMultianewarray"); - ilGenerator.Emit(OpCodes.Ldloc, localArray); - EmitDynamicClassLiteral(wrapper); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicMultianewarray); - } - else if (wrapper.IsGhost || wrapper.IsGhostArray) - { - TypeWrapper tw = wrapper; - while (tw.IsArray) - { - tw = tw.ElementTypeWrapper; - } - ilGenerator.Emit(OpCodes.Ldtoken, ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, wrapper.ArrayRank)); - ilGenerator.Emit(OpCodes.Ldloc, localArray); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.multianewarray_ghost); - ilGenerator.Emit(OpCodes.Castclass, wrapper.TypeAsArrayType); - } - else - { - Type type = wrapper.TypeAsArrayType; - ilGenerator.Emit(OpCodes.Ldtoken, type); - ilGenerator.Emit(OpCodes.Ldloc, localArray); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.multianewarray); - ilGenerator.Emit(OpCodes.Castclass, type); - } - break; - } - case NormalizedByteCode.__anewarray: - { - TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); - if (wrapper.IsUnloadable) - { - Profiler.Count("EmitDynamicNewarray"); - EmitDynamicClassLiteral(wrapper); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewarray); - } - else if (wrapper.IsGhost || wrapper.IsGhostArray) - { - // NOTE for ghost types we create object arrays to make sure that Ghost implementers can be - // stored in ghost arrays, but this has the unintended consequence that ghost arrays can - // contain *any* reference type (because they are compiled as Object arrays). We could - // modify aastore to emit code to check for this, but this would have an huge performance - // cost for all object arrays. - // Oddly, while the JVM accepts any reference for any other interface typed references, in the - // case of aastore it does check that the object actually implements the interface. This - // is unfortunate, but I think we can live with this minor incompatibility. - // Note that this does not break type safety, because when the incorrect object is eventually - // used as the ghost interface type it will generate a ClassCastException. - TypeWrapper tw = wrapper; - while (tw.IsArray) - { - tw = tw.ElementTypeWrapper; - } - ilGenerator.Emit(OpCodes.Ldtoken, ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, wrapper.ArrayRank + 1)); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.anewarray_ghost.MakeGenericMethod(wrapper.TypeAsArrayType)); - } - else - { - ilGenerator.Emit(OpCodes.Newarr, wrapper.TypeAsArrayType); - } - break; - } - case NormalizedByteCode.__newarray: - switch (instr.Arg1) - { - case 4: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BOOLEAN.TypeAsArrayType); - break; - case 5: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.CHAR.TypeAsArrayType); - break; - case 6: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.FLOAT.TypeAsArrayType); - break; - case 7: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.DOUBLE.TypeAsArrayType); - break; - case 8: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BYTE.TypeAsArrayType); - break; - case 9: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.SHORT.TypeAsArrayType); - break; - case 10: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.INT.TypeAsArrayType); - break; - case 11: - ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.LONG.TypeAsArrayType); - break; - default: - // this can't happen, the verifier would have caught it - throw new InvalidOperationException(); - } - break; - case NormalizedByteCode.__checkcast: - { - TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); - if (wrapper.IsUnloadable) - { - EmitDynamicCast(wrapper); - } - else - { - wrapper.EmitCheckcast(ilGenerator); - } - break; - } - case NormalizedByteCode.__instanceof: - { - TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); - if (wrapper.IsUnloadable) - { - EmitDynamicInstanceOf(wrapper); - } - else - { - wrapper.EmitInstanceOf(ilGenerator); - } - break; - } - case NormalizedByteCode.__aaload: - { - TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 1); - if (tw.IsUnloadable) - { - Profiler.Count("EmitDynamicAaload"); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAaload); - } - else - { - TypeWrapper elem = tw.ElementTypeWrapper; - if (elem.IsNonPrimitiveValueType) - { - Type t = elem.TypeAsTBD; - ilGenerator.Emit(OpCodes.Ldelema, t); - ilGenerator.Emit(OpCodes.Ldobj, t); - elem.EmitBox(ilGenerator); - } - else - { - ilGenerator.Emit(OpCodes.Ldelem_Ref); - } - } - break; - } - case NormalizedByteCode.__baload: - // NOTE both the JVM and the CLR use signed bytes for boolean arrays (how convenient!) - ilGenerator.Emit(OpCodes.Ldelem_I1); - break; - case NormalizedByteCode.__bastore: - ilGenerator.Emit(OpCodes.Stelem_I1); - break; - case NormalizedByteCode.__caload: - ilGenerator.Emit(OpCodes.Ldelem_U2); - break; - case NormalizedByteCode.__castore: - ilGenerator.Emit(OpCodes.Stelem_I2); - break; - case NormalizedByteCode.__saload: - ilGenerator.Emit(OpCodes.Ldelem_I2); - break; - case NormalizedByteCode.__sastore: - ilGenerator.Emit(OpCodes.Stelem_I2); - break; - case NormalizedByteCode.__iaload: - ilGenerator.Emit(OpCodes.Ldelem_I4); - break; - case NormalizedByteCode.__iastore: - ilGenerator.Emit(OpCodes.Stelem_I4); - break; - case NormalizedByteCode.__laload: - ilGenerator.Emit(OpCodes.Ldelem_I8); - break; - case NormalizedByteCode.__lastore: - ilGenerator.Emit(OpCodes.Stelem_I8); - break; - case NormalizedByteCode.__faload: - ilGenerator.Emit(OpCodes.Ldelem_R4); - break; - case NormalizedByteCode.__fastore: - ilGenerator.Emit(OpCodes.Stelem_R4); - break; - case NormalizedByteCode.__daload: - ilGenerator.Emit(OpCodes.Ldelem_R8); - break; - case NormalizedByteCode.__dastore: - if (ma.IsStackTypeExtendedDouble(i, 0)) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - ilGenerator.Emit(OpCodes.Stelem_R8); - break; - case NormalizedByteCode.__aastore: - { - TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 2); - if (tw.IsUnloadable) - { - Profiler.Count("EmitDynamicAastore"); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAastore); - } - else - { - TypeWrapper elem = tw.ElementTypeWrapper; - if (elem.IsNonPrimitiveValueType) - { - Type t = elem.TypeAsTBD; - CodeEmitterLocal local = ilGenerator.UnsafeAllocTempLocal(Types.Object); - ilGenerator.Emit(OpCodes.Stloc, local); - ilGenerator.Emit(OpCodes.Ldelema, t); - ilGenerator.Emit(OpCodes.Ldloc, local); - elem.EmitUnbox(ilGenerator); - ilGenerator.Emit(OpCodes.Stobj, t); - } - else - { - // NOTE for verifiability it is expressly *not* required that the - // value matches the array type, so we don't need to handle interface - // references here. - ilGenerator.Emit(OpCodes.Stelem_Ref); - } - } - break; - } - case NormalizedByteCode.__arraylength: - if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) - { - ilGenerator.Emit(OpCodes.Castclass, Types.Array); - ilGenerator.Emit(OpCodes.Callvirt, Types.Array.GetMethod("get_Length")); - } - else - { - ilGenerator.Emit(OpCodes.Ldlen); - } - break; - case NormalizedByteCode.__lcmp: - ilGenerator.Emit_lcmp(); - break; - case NormalizedByteCode.__fcmpl: - ilGenerator.Emit_fcmpl(); - break; - case NormalizedByteCode.__fcmpg: - ilGenerator.Emit_fcmpg(); - break; - case NormalizedByteCode.__dcmpl: - ilGenerator.Emit_dcmpl(); - break; - case NormalizedByteCode.__dcmpg: - ilGenerator.Emit_dcmpg(); - break; - case NormalizedByteCode.__if_icmpeq: - ilGenerator.EmitBeq(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_icmpne: - ilGenerator.EmitBne_Un(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_icmple: - ilGenerator.EmitBle(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_icmplt: - ilGenerator.EmitBlt(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_icmpge: - ilGenerator.EmitBge(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_icmpgt: - ilGenerator.EmitBgt(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ifle: - ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.LessOrEqual, block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__iflt: - ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.LessThan, block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ifge: - ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.GreaterOrEqual, block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ifgt: - ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.GreaterThan, block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ifne: - case NormalizedByteCode.__ifnonnull: - ilGenerator.EmitBrtrue(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ifeq: - case NormalizedByteCode.__ifnull: - ilGenerator.EmitBrfalse(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_acmpeq: - ilGenerator.EmitBeq(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__if_acmpne: - ilGenerator.EmitBne_Un(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__goto: - case NormalizedByteCode.__goto_finally: - ilGenerator.EmitBr(block.GetLabel(instr.TargetIndex)); - break; - case NormalizedByteCode.__ineg: - case NormalizedByteCode.__lneg: - case NormalizedByteCode.__fneg: - case NormalizedByteCode.__dneg: - ilGenerator.Emit(OpCodes.Neg); - break; - case NormalizedByteCode.__iadd: - case NormalizedByteCode.__ladd: - ilGenerator.Emit(OpCodes.Add); - break; - case NormalizedByteCode.__fadd: - ilGenerator.Emit(OpCodes.Add); - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__dadd: - ilGenerator.Emit(OpCodes.Add); - if (strictfp) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - break; - case NormalizedByteCode.__isub: - case NormalizedByteCode.__lsub: - ilGenerator.Emit(OpCodes.Sub); - break; - case NormalizedByteCode.__fsub: - ilGenerator.Emit(OpCodes.Sub); - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__dsub: - ilGenerator.Emit(OpCodes.Sub); - if (strictfp) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - break; - case NormalizedByteCode.__ixor: - case NormalizedByteCode.__lxor: - ilGenerator.Emit(OpCodes.Xor); - break; - case NormalizedByteCode.__ior: - case NormalizedByteCode.__lor: - ilGenerator.Emit(OpCodes.Or); - break; - case NormalizedByteCode.__iand: - case NormalizedByteCode.__land: - ilGenerator.Emit(OpCodes.And); - break; - case NormalizedByteCode.__imul: - case NormalizedByteCode.__lmul: - ilGenerator.Emit(OpCodes.Mul); - break; - case NormalizedByteCode.__fmul: - ilGenerator.Emit(OpCodes.Mul); - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__dmul: - ilGenerator.Emit(OpCodes.Mul); - if (strictfp) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - break; - case NormalizedByteCode.__idiv: - ilGenerator.Emit_idiv(); - break; - case NormalizedByteCode.__ldiv: - ilGenerator.Emit_ldiv(); - break; - case NormalizedByteCode.__fdiv: - ilGenerator.Emit(OpCodes.Div); - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__ddiv: - ilGenerator.Emit(OpCodes.Div); - if (strictfp) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - break; - case NormalizedByteCode.__irem: - case NormalizedByteCode.__lrem: - { - // we need to special case taking the remainder of dividing by -1, - // because the CLR rem instruction throws an OverflowException when - // taking the remainder of dividing Int32.MinValue by -1, and - // Java just silently overflows - ilGenerator.Emit(OpCodes.Dup); - ilGenerator.Emit(OpCodes.Ldc_I4_M1); - if (instr.NormalizedOpCode == NormalizedByteCode.__lrem) - { - ilGenerator.Emit(OpCodes.Conv_I8); - } - CodeEmitterLabel label = ilGenerator.DefineLabel(); - ilGenerator.EmitBne_Un(label); - ilGenerator.Emit(OpCodes.Pop); - ilGenerator.Emit(OpCodes.Pop); - ilGenerator.Emit(OpCodes.Ldc_I4_0); - if (instr.NormalizedOpCode == NormalizedByteCode.__lrem) - { - ilGenerator.Emit(OpCodes.Conv_I8); - } - CodeEmitterLabel label2 = ilGenerator.DefineLabel(); - ilGenerator.EmitBr(label2); - ilGenerator.MarkLabel(label); - ilGenerator.Emit(OpCodes.Rem); - ilGenerator.MarkLabel(label2); - break; - } - case NormalizedByteCode.__frem: - ilGenerator.Emit(OpCodes.Rem); - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__drem: - ilGenerator.Emit(OpCodes.Rem); - if (strictfp) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - break; - case NormalizedByteCode.__ishl: - ilGenerator.Emit_And_I4(31); - ilGenerator.Emit(OpCodes.Shl); - break; - case NormalizedByteCode.__lshl: - ilGenerator.Emit_And_I4(63); - ilGenerator.Emit(OpCodes.Shl); - break; - case NormalizedByteCode.__iushr: - ilGenerator.Emit_And_I4(31); - ilGenerator.Emit(OpCodes.Shr_Un); - break; - case NormalizedByteCode.__lushr: - ilGenerator.Emit_And_I4(63); - ilGenerator.Emit(OpCodes.Shr_Un); - break; - case NormalizedByteCode.__ishr: - ilGenerator.Emit_And_I4(31); - ilGenerator.Emit(OpCodes.Shr); - break; - case NormalizedByteCode.__lshr: - ilGenerator.Emit_And_I4(63); - ilGenerator.Emit(OpCodes.Shr); - break; - case NormalizedByteCode.__swap: - { - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); - dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); - dh.Store(0); - dh.Store(1); - dh.Load(0); - dh.Load(1); - dh.Release(); - break; - } - case NormalizedByteCode.__dup: - // if the TOS contains a "new" object or a fault block exception, it isn't really there, so we don't dup it - if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 0))) - { - ilGenerator.Emit(OpCodes.Dup); - } - break; - case NormalizedByteCode.__dup2: - { - TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); - if (type1.IsWidePrimitive) - { - ilGenerator.Emit(OpCodes.Dup); - } - else - { - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, type1); - dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); - dh.Store(0); - dh.Store(1); - dh.Load(1); - dh.Load(0); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - break; - } - case NormalizedByteCode.__dup_x1: - { - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); - dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); - dh.Store(0); - dh.Store(1); - dh.Load(0); - dh.Load(1); - dh.Load(0); - dh.Release(); - break; - } - case NormalizedByteCode.__dup2_x1: - { - TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); - if (type1.IsWidePrimitive) - { - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, type1); - dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); - dh.Store(0); - dh.Store(1); - dh.Load(0); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - else - { - DupHelper dh = new DupHelper(this, 3); - dh.SetType(0, type1); - dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); - dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); - dh.Store(0); - dh.Store(1); - dh.Store(2); - dh.Load(1); - dh.Load(0); - dh.Load(2); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - break; - } - case NormalizedByteCode.__dup2_x2: - { - TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); - TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1); - if (type1.IsWidePrimitive) - { - if (type2.IsWidePrimitive) - { - // Form 4 - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, type1); - dh.SetType(1, type2); - dh.Store(0); - dh.Store(1); - dh.Load(0); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - else - { - // Form 2 - DupHelper dh = new DupHelper(this, 3); - dh.SetType(0, type1); - dh.SetType(1, type2); - dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); - dh.Store(0); - dh.Store(1); - dh.Store(2); - dh.Load(0); - dh.Load(2); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - } - else - { - TypeWrapper type3 = ma.GetRawStackTypeWrapper(i, 2); - if (type3.IsWidePrimitive) - { - // Form 3 - DupHelper dh = new DupHelper(this, 3); - dh.SetType(0, type1); - dh.SetType(1, type2); - dh.SetType(2, type3); - dh.Store(0); - dh.Store(1); - dh.Store(2); - dh.Load(1); - dh.Load(0); - dh.Load(2); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - else - { - // Form 1 - DupHelper dh = new DupHelper(this, 4); - dh.SetType(0, type1); - dh.SetType(1, type2); - dh.SetType(2, type3); - dh.SetType(3, ma.GetRawStackTypeWrapper(i, 3)); - dh.Store(0); - dh.Store(1); - dh.Store(2); - dh.Store(3); - dh.Load(1); - dh.Load(0); - dh.Load(3); - dh.Load(2); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - } - break; - } - case NormalizedByteCode.__dup_x2: - { - TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1); - if (type2.IsWidePrimitive) - { - // Form 2 - DupHelper dh = new DupHelper(this, 2); - dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); - dh.SetType(1, type2); - dh.Store(0); - dh.Store(1); - dh.Load(0); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - else - { - // Form 1 - DupHelper dh = new DupHelper(this, 3); - dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); - dh.SetType(1, type2); - dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); - dh.Store(0); - dh.Store(1); - dh.Store(2); - dh.Load(0); - dh.Load(2); - dh.Load(1); - dh.Load(0); - dh.Release(); - } - break; - } - case NormalizedByteCode.__pop2: - { - TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); - if (type1.IsWidePrimitive) - { - ilGenerator.Emit(OpCodes.Pop); - } - else - { - if (!VerifierTypeWrapper.IsNotPresentOnStack(type1)) - { - ilGenerator.Emit(OpCodes.Pop); - } - if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 1))) - { - ilGenerator.Emit(OpCodes.Pop); - } - } - break; - } - case NormalizedByteCode.__pop: - // if the TOS is a new object or a fault block exception, it isn't really there, so we don't need to pop it - if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 0))) - { - ilGenerator.Emit(OpCodes.Pop); - } - break; - case NormalizedByteCode.__monitorenter: - ilGenerator.EmitMonitorEnter(); - break; - case NormalizedByteCode.__monitorexit: - ilGenerator.EmitMonitorExit(); - break; - case NormalizedByteCode.__athrow_no_unmap: - if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) - { - ilGenerator.Emit(OpCodes.Castclass, Types.Exception); - } - ilGenerator.Emit(OpCodes.Throw); - break; - case NormalizedByteCode.__athrow: - if (VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(i, 0))) - { - ilGenerator.Emit(OpCodes.Endfinally); - } - else - { - if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) - { - ilGenerator.Emit(OpCodes.Castclass, Types.Exception); - } - ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod); - ilGenerator.Emit(OpCodes.Throw); - } - break; - case NormalizedByteCode.__tableswitch: - { - // note that a tableswitch always has at least one entry - // (otherwise it would have failed verification) - CodeEmitterLabel[] labels = new CodeEmitterLabel[instr.SwitchEntryCount]; - for (int j = 0; j < labels.Length; j++) - { - labels[j] = block.GetLabel(instr.GetSwitchTargetIndex(j)); - } - if (instr.GetSwitchValue(0) != 0) - { - ilGenerator.EmitLdc_I4(instr.GetSwitchValue(0)); - ilGenerator.Emit(OpCodes.Sub); - } - ilGenerator.EmitSwitch(labels); - ilGenerator.EmitBr(block.GetLabel(instr.DefaultTarget)); - break; - } - case NormalizedByteCode.__lookupswitch: - for (int j = 0; j < instr.SwitchEntryCount; j++) - { - ilGenerator.Emit(OpCodes.Dup); - ilGenerator.EmitLdc_I4(instr.GetSwitchValue(j)); - CodeEmitterLabel label = ilGenerator.DefineLabel(); - ilGenerator.EmitBne_Un(label); - ilGenerator.Emit(OpCodes.Pop); - ilGenerator.EmitBr(block.GetLabel(instr.GetSwitchTargetIndex(j))); - ilGenerator.MarkLabel(label); - } - ilGenerator.Emit(OpCodes.Pop); - ilGenerator.EmitBr(block.GetLabel(instr.DefaultTarget)); - break; - case NormalizedByteCode.__iinc: - LoadLocal(i); - ilGenerator.EmitLdc_I4(instr.Arg2); - ilGenerator.Emit(OpCodes.Add); - StoreLocal(i); - break; - case NormalizedByteCode.__i2b: - ilGenerator.Emit(OpCodes.Conv_I1); - break; - case NormalizedByteCode.__i2c: - ilGenerator.Emit(OpCodes.Conv_U2); - break; - case NormalizedByteCode.__i2s: - ilGenerator.Emit(OpCodes.Conv_I2); - break; - case NormalizedByteCode.__l2i: - ilGenerator.Emit(OpCodes.Conv_I4); - break; - case NormalizedByteCode.__f2i: - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2i); - break; - case NormalizedByteCode.__d2i: - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2i); - break; - case NormalizedByteCode.__f2l: - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2l); - break; - case NormalizedByteCode.__d2l: - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2l); - break; - case NormalizedByteCode.__i2l: - ilGenerator.Emit(OpCodes.Conv_I8); - break; - case NormalizedByteCode.__i2f: - case NormalizedByteCode.__l2f: - case NormalizedByteCode.__d2f: - ilGenerator.Emit(OpCodes.Conv_R4); - break; - case NormalizedByteCode.__i2d: - case NormalizedByteCode.__l2d: - case NormalizedByteCode.__f2d: - ilGenerator.Emit(OpCodes.Conv_R8); - break; - case NormalizedByteCode.__nop: - ilGenerator.Emit(OpCodes.Nop); - break; - case NormalizedByteCode.__intrinsic_gettype: - ilGenerator.Emit(OpCodes.Callvirt, getTypeMethod); - break; - case NormalizedByteCode.__static_error: - { - bool wrapIncompatibleClassChangeError = false; - TypeWrapper exceptionType; - switch (instr.HardError) - { - case HardError.AbstractMethodError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.AbstractMethodError"); - break; - case HardError.IllegalAccessError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessError"); - break; - case HardError.IncompatibleClassChangeError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IncompatibleClassChangeError"); - break; - case HardError.InstantiationError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.InstantiationError"); - break; - case HardError.LinkageError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.LinkageError"); - break; - case HardError.NoClassDefFoundError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoClassDefFoundError"); - break; - case HardError.NoSuchFieldError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchFieldError"); - break; - case HardError.NoSuchMethodError: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchMethodError"); - break; - case HardError.IllegalAccessException: - exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessException"); - wrapIncompatibleClassChangeError = true; - break; - default: - throw new InvalidOperationException(); - } - if (wrapIncompatibleClassChangeError) - { - ClassLoaderWrapper.LoadClassCritical("java.lang.IncompatibleClassChangeError").GetMethodWrapper("", "()V", false).EmitNewobj(ilGenerator); - } - string message = harderrors[instr.HardErrorMessageId]; - Tracer.Error(Tracer.Compiler, "{0}: {1}\n\tat {2}.{3}{4}", exceptionType.Name, message, classFile.Name, m.Name, m.Signature); - ilGenerator.Emit(OpCodes.Ldstr, message); - MethodWrapper method = exceptionType.GetMethodWrapper("", "(Ljava.lang.String;)V", false); - method.Link(); - method.EmitNewobj(ilGenerator); - if (wrapIncompatibleClassChangeError) - { - CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("initCause", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false).EmitCallvirt(ilGenerator); - } - ilGenerator.Emit(OpCodes.Throw); - break; - } - default: - throw new NotImplementedException(instr.NormalizedOpCode.ToString()); - } - // mark next instruction as inuse - switch (ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode)) - { - case ByteCodeFlowControl.Switch: - case ByteCodeFlowControl.Branch: - case ByteCodeFlowControl.Return: - case ByteCodeFlowControl.Throw: - instructionIsForwardReachable = false; - break; - case ByteCodeFlowControl.CondBranch: - case ByteCodeFlowControl.Next: - instructionIsForwardReachable = true; - Debug.Assert((flags[i + 1] & InstructionFlags.Reachable) != 0); - // don't fall through end of try block - if (block.EndIndex == i + 1) - { - // TODO instead of emitting a branch to the leave stub, it would be more efficient to put the leave stub here - ilGenerator.EmitBr(block.GetLabel(i + 1)); - } - break; - default: - throw new InvalidOperationException(); - } - } - } - - private void EmitReturnTypeConversion(TypeWrapper returnType) - { - returnType.EmitConvSignatureTypeToStackType(ilGenerator); - if (!strictfp) - { - // no need to convert - } - else if (returnType == PrimitiveTypeWrapper.DOUBLE) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - else if (returnType == PrimitiveTypeWrapper.FLOAT) - { - ilGenerator.Emit(OpCodes.Conv_R4); - } - } - - private void EmitLoadConstant(CodeEmitter ilgen, int constant) - { - switch (classFile.GetConstantPoolConstantType(constant)) - { - case ClassFile.ConstantType.Double: - ilgen.EmitLdc_R8(classFile.GetConstantPoolConstantDouble(constant)); - break; - case ClassFile.ConstantType.Float: - ilgen.EmitLdc_R4(classFile.GetConstantPoolConstantFloat(constant)); - break; - case ClassFile.ConstantType.Integer: - ilgen.EmitLdc_I4(classFile.GetConstantPoolConstantInteger(constant)); - break; - case ClassFile.ConstantType.Long: - ilgen.EmitLdc_I8(classFile.GetConstantPoolConstantLong(constant)); - break; - case ClassFile.ConstantType.String: - ilgen.Emit(OpCodes.Ldstr, classFile.GetConstantPoolConstantString(constant)); - break; - case ClassFile.ConstantType.Class: - EmitLoadClass(ilgen, classFile.GetConstantPoolClassType(constant)); - break; - case ClassFile.ConstantType.MethodHandle: - context.GetValue(constant).Emit(this, ilgen, constant); - break; - case ClassFile.ConstantType.MethodType: - context.GetValue(constant).Emit(this, ilgen, constant); - break; -#if !STATIC_COMPILER - case ClassFile.ConstantType.LiveObject: - context.EmitLiveObjectLoad(ilgen, classFile.GetConstantPoolConstantLiveObject(constant)); - break; -#endif - default: - throw new InvalidOperationException(); - } - } - - private void EmitDynamicCast(TypeWrapper tw) - { - Debug.Assert(tw.IsUnloadable); - Profiler.Count("EmitDynamicCast"); - // NOTE it's important that we don't try to load the class if obj == null - CodeEmitterLabel ok = ilGenerator.DefineLabel(); - ilGenerator.Emit(OpCodes.Dup); - ilGenerator.EmitBrfalse(ok); - EmitDynamicClassLiteral(tw); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicCast); - ilGenerator.MarkLabel(ok); - } - - private void EmitDynamicInstanceOf(TypeWrapper tw) - { - // NOTE it's important that we don't try to load the class if obj == null - CodeEmitterLabel notnull = ilGenerator.DefineLabel(); - CodeEmitterLabel end = ilGenerator.DefineLabel(); - ilGenerator.Emit(OpCodes.Dup); - ilGenerator.EmitBrtrue(notnull); - ilGenerator.Emit(OpCodes.Pop); - ilGenerator.EmitLdc_I4(0); - ilGenerator.EmitBr(end); - ilGenerator.MarkLabel(notnull); - EmitDynamicClassLiteral(tw); - ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicInstanceOf); - ilGenerator.MarkLabel(end); - } - - private void EmitDynamicClassLiteral(TypeWrapper tw) - { - context.EmitDynamicClassLiteral(ilGenerator, tw, m.IsLambdaFormCompiled); - } - - private void EmitLoadClass(CodeEmitter ilgen, TypeWrapper tw) - { - if (tw.IsUnloadable) - { - Profiler.Count("EmitDynamicClassLiteral"); - context.EmitDynamicClassLiteral(ilgen, tw, m.IsLambdaFormCompiled); - } - else - { - tw.EmitClassLiteral(ilgen); - } - } - - internal static bool HasUnloadable(TypeWrapper[] args, TypeWrapper ret) - { - TypeWrapper tw = ret; - for (int i = 0; !tw.IsUnloadable && i < args.Length; i++) - { - tw = args[i]; - } - return tw.IsUnloadable; - } - - private static class InvokeDynamicBuilder - { - private static readonly Type typeofOpenIndyCallSite; - private static readonly Type typeofCallSite; - private static readonly MethodWrapper methodLookup; - - static InvokeDynamicBuilder() - { -#if STATIC_COMPILER - typeofOpenIndyCallSite = StaticCompiler.GetRuntimeType("IKVM.Runtime.IndyCallSite`1"); - typeofCallSite = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.CallSite").TypeAsSignatureType; -#elif !FIRST_PASS - typeofOpenIndyCallSite = typeof(IKVM.Runtime.IndyCallSite<>); - typeofCallSite = typeof(java.lang.invoke.CallSite); -#endif - methodLookup = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.MethodHandles") - .GetMethodWrapper("lookup", "()Ljava.lang.invoke.MethodHandles$Lookup;", false); - methodLookup.Link(); - } - - internal static void Emit(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, Type delegateType) - { - Type typeofIndyCallSite = typeofOpenIndyCallSite.MakeGenericType(delegateType); - MethodInfo methodCreateBootStrap; - MethodInfo methodGetTarget; - if (ReflectUtil.ContainsTypeBuilder(typeofIndyCallSite)) - { - methodCreateBootStrap = TypeBuilder.GetMethod(typeofIndyCallSite, typeofOpenIndyCallSite.GetMethod("CreateBootstrap")); - methodGetTarget = TypeBuilder.GetMethod(typeofIndyCallSite, typeofOpenIndyCallSite.GetMethod("GetTarget")); - } - else - { - methodCreateBootStrap = typeofIndyCallSite.GetMethod("CreateBootstrap"); - methodGetTarget = typeofIndyCallSite.GetMethod("GetTarget"); - } - TypeBuilder tb = compiler.context.DefineIndyCallSiteType(); - FieldBuilder fb = tb.DefineField("value", typeofIndyCallSite, FieldAttributes.Static | FieldAttributes.Assembly); - CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb, compiler.clazz.GetClassLoader())); - ilgen.Emit(OpCodes.Ldnull); - ilgen.Emit(OpCodes.Ldftn, CreateBootstrapStub(compiler, cpi, delegateType, tb, fb, methodGetTarget)); - ilgen.Emit(OpCodes.Newobj, MethodHandleUtil.GetDelegateConstructor(delegateType)); - ilgen.Emit(OpCodes.Call, methodCreateBootStrap); - ilgen.Emit(OpCodes.Stsfld, fb); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - - compiler.ilGenerator.Emit(OpCodes.Ldsfld, fb); - compiler.ilGenerator.Emit(OpCodes.Call, methodGetTarget); - } - - private static MethodBuilder CreateBootstrapStub(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, Type delegateType, TypeBuilder tb, FieldBuilder fb, MethodInfo methodGetTarget) - { - Type[] args = Type.EmptyTypes; - if (delegateType.IsGenericType) - { - // MONOBUG we don't look at the invoke method directly here, because Mono doesn't support GetParameters() on a builder instantiation - args = delegateType.GetGenericArguments(); - if (cpi.GetRetType() != PrimitiveTypeWrapper.VOID) - { - Array.Resize(ref args, args.Length - 1); - } - } - MethodBuilder mb = tb.DefineMethod("BootstrapStub", MethodAttributes.Static | MethodAttributes.PrivateScope, cpi.GetRetType().TypeAsSignatureType, args); - CodeEmitter ilgen = CodeEmitter.Create(mb); - CodeEmitterLocal cs = ilgen.DeclareLocal(typeofCallSite); - CodeEmitterLocal ex = ilgen.DeclareLocal(Types.Exception); - CodeEmitterLocal ok = ilgen.DeclareLocal(Types.Boolean); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.BeginExceptionBlock(); - if (EmitCallBootstrapMethod(compiler, cpi, ilgen, ok)) - { - ilgen.Emit(OpCodes.Isinst, typeofCallSite); - ilgen.Emit(OpCodes.Stloc, cs); - } - ilgen.EmitLeave(label); - ilgen.BeginCatchBlock(Types.Exception); - ilgen.Emit(OpCodes.Stloc, ex); - ilgen.Emit(OpCodes.Ldloc, ok); - CodeEmitterLabel label2 = ilgen.DefineLabel(); - ilgen.EmitBrtrue(label2); - ilgen.Emit(OpCodes.Rethrow); - ilgen.MarkLabel(label2); - ilgen.EmitLeave(label); - ilgen.EndExceptionBlock(); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Ldsflda, fb); - ilgen.Emit(OpCodes.Ldloc, cs); - ilgen.Emit(OpCodes.Ldloc, ex); - if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) - { - ilgen.Emit(OpCodes.Ldstr, cpi.Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormHidden); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLinkIndyCallSite.MakeGenericMethod(delegateType)); - } - else - { - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LinkIndyCallSite.MakeGenericMethod(delegateType)); - } - ilgen.Emit(OpCodes.Ldsfld, fb); - ilgen.Emit(OpCodes.Call, methodGetTarget); - for (int i = 0; i < args.Length; i++) - { - ilgen.EmitLdarg(i); - } - ilgen.Emit(OpCodes.Callvirt, MethodHandleUtil.GetDelegateInvokeMethod(delegateType)); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return mb; - } - - private static bool EmitCallBootstrapMethod(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen, CodeEmitterLocal ok) - { - ClassFile.BootstrapMethod bsm = compiler.classFile.GetBootstrapMethod(cpi.BootstrapMethod); - if (3 + bsm.ArgumentCount > 255) - { - ilgen.EmitThrow("java.lang.BootstrapMethodError", "too many bootstrap method arguments"); - return false; - } - ClassFile.ConstantPoolItemMethodHandle mh = compiler.classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex); - MethodWrapper mw = mh.Member as MethodWrapper; - switch (mh.Kind) - { - case ClassFile.RefKind.invokeStatic: - if (mw != null && !mw.IsStatic) - goto default; - break; - case ClassFile.RefKind.newInvokeSpecial: - if (mw != null && !mw.IsConstructor) - goto default; - break; - default: - // to throw the right exception, we have to resolve the MH constant here - compiler.context.GetValue(bsm.BootstrapMethodIndex).Emit(compiler, ilgen, bsm.BootstrapMethodIndex); - ilgen.Emit(OpCodes.Pop); - ilgen.EmitLdc_I4(1); - ilgen.Emit(OpCodes.Stloc, ok); - ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); - return false; - } - if (mw == null) - { - // to throw the right exception (i.e. without wrapping it in a BootstrapMethodError), we have to resolve the MH constant here - compiler.context.GetValue(bsm.BootstrapMethodIndex).Emit(compiler, ilgen, bsm.BootstrapMethodIndex); - ilgen.Emit(OpCodes.Pop); - ClassFile.ConstantPoolItemMI cpiMI; - if ((cpiMI = mh.MemberConstantPoolItem as ClassFile.ConstantPoolItemMI) != null) - { - mw = new DynamicBinder().Get(compiler, mh.Kind, cpiMI, false); - } - else - { - ilgen.EmitLdc_I4(1); - ilgen.Emit(OpCodes.Stloc, ok); - ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); - return false; - } - } - TypeWrapper[] parameters = mw.GetParameters(); - int extraArgs = parameters.Length - 3; - int fixedArgs; - int varArgs; - if (extraArgs == 1 && parameters[3].IsArray && parameters[3].ElementTypeWrapper == CoreClasses.java.lang.Object.Wrapper) - { - fixedArgs = 0; - varArgs = bsm.ArgumentCount - fixedArgs; - } - else if (extraArgs != bsm.ArgumentCount) - { - ilgen.EmitLdc_I4(1); - ilgen.Emit(OpCodes.Stloc, ok); - ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); - return false; - } - else - { - fixedArgs = extraArgs; - varArgs = -1; - } - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - methodLookup.EmitCall(ilgen); - ilgen.Emit(OpCodes.Ldstr, cpi.Name); - parameters[1].EmitConvStackTypeToSignatureType(ilgen, CoreClasses.java.lang.String.Wrapper); - if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) - { - // the cache is useless since we only run once, so we use a local - ilgen.Emit(OpCodes.Ldloca, ilgen.DeclareLocal(CoreClasses.java.lang.invoke.MethodType.Wrapper.TypeAsSignatureType)); - ilgen.Emit(OpCodes.Ldstr, cpi.Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); - } - else - { - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(MethodHandleUtil.CreateDelegateTypeForLoadConstant(cpi.GetArgTypes(), cpi.GetRetType()))); - } - parameters[2].EmitConvStackTypeToSignatureType(ilgen, CoreClasses.java.lang.invoke.MethodType.Wrapper); - for (int i = 0; i < fixedArgs; i++) - { - EmitExtraArg(compiler, ilgen, bsm, i, parameters[i + 3], ok); - } - if (varArgs >= 0) - { - ilgen.EmitLdc_I4(varArgs); - TypeWrapper elemType = parameters[parameters.Length - 1].ElementTypeWrapper; - ilgen.Emit(OpCodes.Newarr, elemType.TypeAsArrayType); - for (int i = 0; i < varArgs; i++) - { - ilgen.Emit(OpCodes.Dup); - ilgen.EmitLdc_I4(i); - EmitExtraArg(compiler, ilgen, bsm, i + fixedArgs, elemType, ok); - ilgen.Emit(OpCodes.Stelem_Ref); - } - } - ilgen.EmitLdc_I4(1); - ilgen.Emit(OpCodes.Stloc, ok); - if (mw.IsConstructor) - { - mw.EmitNewobj(ilgen); - } - else - { - mw.EmitCall(ilgen); - } - return true; - } - - private static void EmitExtraArg(Compiler compiler, CodeEmitter ilgen, ClassFile.BootstrapMethod bsm, int index, TypeWrapper targetType, CodeEmitterLocal wrapException) - { - int constant = bsm.GetArgument(index); - compiler.EmitLoadConstant(ilgen, constant); - TypeWrapper constType; - switch (compiler.classFile.GetConstantPoolConstantType(constant)) - { - case ClassFile.ConstantType.Integer: - constType = PrimitiveTypeWrapper.INT; - break; - case ClassFile.ConstantType.Long: - constType = PrimitiveTypeWrapper.LONG; - break; - case ClassFile.ConstantType.Float: - constType = PrimitiveTypeWrapper.FLOAT; - break; - case ClassFile.ConstantType.Double: - constType = PrimitiveTypeWrapper.DOUBLE; - break; - case ClassFile.ConstantType.Class: - constType = CoreClasses.java.lang.Class.Wrapper; - break; - case ClassFile.ConstantType.String: - constType = CoreClasses.java.lang.String.Wrapper; - break; - case ClassFile.ConstantType.MethodHandle: - constType = CoreClasses.java.lang.invoke.MethodHandle.Wrapper; - break; - case ClassFile.ConstantType.MethodType: - constType = CoreClasses.java.lang.invoke.MethodType.Wrapper; - break; - default: - throw new InvalidOperationException(); - } - if (constType != targetType) - { - ilgen.EmitLdc_I4(1); - ilgen.Emit(OpCodes.Stloc, wrapException); - if (constType.IsPrimitive) - { - string dummy; - TypeWrapper wrapper = GetWrapperType(constType, out dummy); - wrapper.GetMethodWrapper("valueOf", "(" + constType.SigName + ")" + wrapper.SigName, false).EmitCall(ilgen); - } - if (targetType.IsUnloadable) - { - // do nothing - } - else if (targetType.IsPrimitive) - { - string unbox; - TypeWrapper wrapper = GetWrapperType(targetType, out unbox); - ilgen.Emit(OpCodes.Castclass, wrapper.TypeAsBaseType); - wrapper.GetMethodWrapper(unbox, "()" + targetType.SigName, false).EmitCallvirt(ilgen); - } - else if (!constType.IsAssignableTo(targetType)) - { - ilgen.Emit(OpCodes.Castclass, targetType.TypeAsBaseType); - } - targetType.EmitConvStackTypeToSignatureType(ilgen, targetType); - ilgen.EmitLdc_I4(0); - ilgen.Emit(OpCodes.Stloc, wrapException); - } - } - - private static TypeWrapper GetWrapperType(TypeWrapper tw, out string unbox) - { - if (tw == PrimitiveTypeWrapper.INT) - { - unbox = "intValue"; - return ClassLoaderWrapper.LoadClassCritical("java.lang.Integer"); - } - else if (tw == PrimitiveTypeWrapper.LONG) - { - unbox = "longValue"; - return ClassLoaderWrapper.LoadClassCritical("java.lang.Long"); - } - else if (tw == PrimitiveTypeWrapper.FLOAT) - { - unbox = "floatValue"; - return ClassLoaderWrapper.LoadClassCritical("java.lang.Float"); - } - else if (tw == PrimitiveTypeWrapper.DOUBLE) - { - unbox = "doubleValue"; - return ClassLoaderWrapper.LoadClassCritical("java.lang.Double"); - } - else - { - throw new InvalidOperationException(); - } - } - } - - private void EmitInvokeDynamic(ClassFile.ConstantPoolItemInvokeDynamic cpi) - { - CodeEmitter ilgen = ilGenerator; - TypeWrapper[] args = cpi.GetArgTypes(); - CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; - for (int i = args.Length - 1; i >= 0; i--) - { - temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temps[i]); - } - Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); - InvokeDynamicBuilder.Emit(this, cpi, delegateType); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, temps[i]); - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - } - - private sealed class MethodHandleConstant - { - private FieldBuilder field; + struct MethodKey : IEquatable + { - internal void Emit(Compiler compiler, CodeEmitter ilgen, int index) - { - if (field == null) - { - field = compiler.context.DefineDynamicMethodHandleCacheField(); - } - ClassFile.ConstantPoolItemMethodHandle mh = compiler.classFile.GetConstantPoolConstantMethodHandle(index); - ilgen.Emit(OpCodes.Ldsflda, field); - ilgen.EmitLdc_I4((int)mh.Kind); - ilgen.Emit(OpCodes.Ldstr, mh.Class); - ilgen.Emit(OpCodes.Ldstr, mh.Name); - ilgen.Emit(OpCodes.Ldstr, mh.Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodHandle); - } - } - - private sealed class MethodTypeConstant - { - private FieldBuilder field; - private bool dynamic; - - internal void Emit(Compiler compiler, CodeEmitter ilgen, int index) - { - if (field == null) - { - field = CreateField(compiler, index, ref dynamic); - } - if (dynamic) - { - ilgen.Emit(OpCodes.Ldsflda, field); - ilgen.Emit(OpCodes.Ldstr, compiler.classFile.GetConstantPoolConstantMethodType(index).Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); - } - else - { - ilgen.Emit(OpCodes.Ldsfld, field); - } - } - - private static FieldBuilder CreateField(Compiler compiler, int index, ref bool dynamic) - { - ClassFile.ConstantPoolItemMethodType cpi = compiler.classFile.GetConstantPoolConstantMethodType(index); - TypeWrapper[] args = cpi.GetArgTypes(); - TypeWrapper ret = cpi.GetRetType(); - - if (HasUnloadable(args, ret)) - { - dynamic = true; - return compiler.context.DefineDynamicMethodTypeCacheField(); - } - else - { - TypeBuilder tb = compiler.context.DefineMethodTypeConstantType(index); - FieldBuilder field = tb.DefineField("value", CoreClasses.java.lang.invoke.MethodType.Wrapper.TypeAsSignatureType, FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); - CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb, compiler.clazz.GetClassLoader())); - Type delegateType = MethodHandleUtil.CreateDelegateTypeForLoadConstant(args, ret); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); - ilgen.Emit(OpCodes.Stsfld, field); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return field; - } - } - } - - private bool RequiresExplicitClassInit(TypeWrapper tw, int index, InstructionFlags[] flags) - { - ClassFile.Method.Instruction[] code = m.Instructions; - for (; index < code.Length; index++) - { - if (code[index].NormalizedOpCode == NormalizedByteCode.__invokespecial) - { - ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(code[index].Arg1); - MethodWrapper mw = cpi.GetMethodForInvokespecial(); - return !mw.IsConstructor || mw.DeclaringType != tw; - } - if ((flags[index] & InstructionFlags.BranchTarget) != 0 - || ByteCodeMetaData.IsBranch(code[index].NormalizedOpCode) - || ByteCodeMetaData.CanThrowException(code[index].NormalizedOpCode)) - { - break; - } - } - return true; - } - - // NOTE despite its name this also handles value type args - private void CastInterfaceArgs(TypeWrapper declaringType, TypeWrapper[] args, int instructionIndex, bool instanceMethod) - { - bool needsCast = false; - int firstCastArg = -1; - - if (!needsCast) - { - for (int i = 0; i < args.Length; i++) - { - if (args[i].IsUnloadable) - { - // nothing to do, callee will (eventually) do the cast - } - else if (args[i].IsGhost) - { - needsCast = true; - firstCastArg = i; - break; - } - else if (args[i].IsInterfaceOrInterfaceArray) - { - TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i); - if (tw.IsUnloadable || NeedsInterfaceDownCast(tw, args[i])) - { - needsCast = true; - firstCastArg = i; - break; - } - } - else if (args[i].IsNonPrimitiveValueType) - { - if (i == 0 && instanceMethod && declaringType != args[i]) - { - // no cast needed because we're calling an inherited method - } - else - { - needsCast = true; - firstCastArg = i; - break; - } - } - // if the stack contains an unloadable, we might need to cast it - // (e.g. if the argument type is a base class that is loadable) - if (ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i).IsUnloadable) - { - needsCast = true; - firstCastArg = i; - break; - } - } - } - - if (needsCast) - { - DupHelper dh = new DupHelper(this, args.Length); - for (int i = firstCastArg + 1; i < args.Length; i++) - { - TypeWrapper tw = ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i); - if (tw != VerifierTypeWrapper.UninitializedThis - && !VerifierTypeWrapper.IsThis(tw)) - { - tw = args[i]; - } - dh.SetType(i, tw); - } - for (int i = args.Length - 1; i >= firstCastArg; i--) - { - if (!args[i].IsUnloadable && !args[i].IsGhost) - { - TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i); - if (tw.IsUnloadable || (args[i].IsInterfaceOrInterfaceArray && NeedsInterfaceDownCast(tw, args[i]))) - { - ilGenerator.EmitAssertType(args[i].TypeAsTBD); - Profiler.Count("InterfaceDownCast"); - } - } - if (i != firstCastArg) - { - dh.Store(i); - } - } - if (instanceMethod && args[0].IsUnloadable && !declaringType.IsUnloadable) - { - if (declaringType.IsInterface) - { - ilGenerator.EmitAssertType(declaringType.TypeAsTBD); - } - else if (declaringType.IsNonPrimitiveValueType) - { - ilGenerator.Emit(OpCodes.Unbox, declaringType.TypeAsTBD); - } - else - { - ilGenerator.Emit(OpCodes.Castclass, declaringType.TypeAsSignatureType); - } - } - for (int i = firstCastArg; i < args.Length; i++) - { - if (i != firstCastArg) - { - dh.Load(i); - } - if (!args[i].IsUnloadable && args[i].IsGhost) - { - if (i == 0 && instanceMethod && !declaringType.IsInterface) - { - // we're calling a java.lang.Object method through a ghost interface reference, - // no ghost handling is needed - } - else if (VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i))) - { - // we're an instance method in a ghost interface, so the this pointer is a managed pointer to the - // wrapper value and if we're not calling another instance method on ourself, we need to load - // the wrapper value onto the stack - if (!instanceMethod || i != 0) - { - ilGenerator.Emit(OpCodes.Ldobj, args[i].TypeAsSignatureType); - } - } - else - { - CodeEmitterLocal ghost = ilGenerator.AllocTempLocal(Types.Object); - ilGenerator.Emit(OpCodes.Stloc, ghost); - CodeEmitterLocal local = ilGenerator.AllocTempLocal(args[i].TypeAsSignatureType); - ilGenerator.Emit(OpCodes.Ldloca, local); - ilGenerator.Emit(OpCodes.Ldloc, ghost); - ilGenerator.Emit(OpCodes.Stfld, args[i].GhostRefField); - ilGenerator.Emit(OpCodes.Ldloca, local); - ilGenerator.ReleaseTempLocal(local); - ilGenerator.ReleaseTempLocal(ghost); - // NOTE when the this argument is a value type, we need the address on the stack instead of the value - if (i != 0 || !instanceMethod) - { - ilGenerator.Emit(OpCodes.Ldobj, args[i].TypeAsSignatureType); - } - } - } - else - { - if (!args[i].IsUnloadable) - { - if (args[i].IsNonPrimitiveValueType) - { - if (i == 0 && instanceMethod) - { - // we only need to unbox if the method was actually declared on the value type - if (declaringType == args[i]) - { - ilGenerator.Emit(OpCodes.Unbox, args[i].TypeAsTBD); - } - } - else - { - args[i].EmitUnbox(ilGenerator); - } - } - else if (ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i).IsUnloadable) - { - ilGenerator.Emit(OpCodes.Castclass, args[i].TypeAsSignatureType); - } - } - } - } - dh.Release(); - } - } - - private bool NeedsInterfaceDownCast(TypeWrapper tw, TypeWrapper arg) - { - if (tw == VerifierTypeWrapper.Null) - { - return false; - } - if (!tw.IsAccessibleFrom(clazz)) - { - tw = tw.GetPublicBaseTypeWrapper(); - } - return !tw.IsAssignableTo(arg); - } - - private void DynamicGetPutField(Instruction instr, int i) - { - ClassFile.RefKind kind; - switch (instr.NormalizedOpCode) - { - case NormalizedByteCode.__dynamic_getfield: - Profiler.Count("EmitDynamicGetfield"); - kind = ClassFile.RefKind.getField; - break; - case NormalizedByteCode.__dynamic_putfield: - Profiler.Count("EmitDynamicPutfield"); - kind = ClassFile.RefKind.putField; - break; - case NormalizedByteCode.__dynamic_getstatic: - Profiler.Count("EmitDynamicGetstatic"); - kind = ClassFile.RefKind.getStatic; - break; - case NormalizedByteCode.__dynamic_putstatic: - Profiler.Count("EmitDynamicPutstatic"); - kind = ClassFile.RefKind.putStatic; - break; - default: - throw new InvalidOperationException(); - } - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); - TypeWrapper fieldType = cpi.GetFieldType(); - if (kind == ClassFile.RefKind.putField || kind == ClassFile.RefKind.putStatic) - { - fieldType.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); - if (strictfp) - { - // no need to convert - } - else if (fieldType == PrimitiveTypeWrapper.DOUBLE) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - } - context.GetValue(instr.Arg1 | ((byte)kind << 24)).Emit(this, cpi, kind); - if (kind == ClassFile.RefKind.getField || kind == ClassFile.RefKind.getStatic) - { - fieldType.EmitConvSignatureTypeToStackType(ilGenerator); - } - } + readonly string className; + readonly string methodName; + readonly string methodSig; - private static void EmitReturnTypeConversion(CodeEmitter ilgen, TypeWrapper typeWrapper) - { - if (typeWrapper.IsUnloadable) - { - // nothing to do for unloadables - } - else if (typeWrapper == PrimitiveTypeWrapper.VOID) - { - ilgen.Emit(OpCodes.Pop); - } - else if (typeWrapper.IsPrimitive) - { - // NOTE we don't need to use TypeWrapper.EmitUnbox, because the return value cannot be null - ilgen.Emit(OpCodes.Unbox, typeWrapper.TypeAsTBD); - ilgen.Emit(OpCodes.Ldobj, typeWrapper.TypeAsTBD); - if (typeWrapper == PrimitiveTypeWrapper.BYTE) - { - ilgen.Emit(OpCodes.Conv_I1); - } - } - else - { - typeWrapper.EmitCheckcast(ilgen); - } - } + internal MethodKey(string className, string methodName, string methodSig) + { + this.className = className; + this.methodName = methodName; + this.methodSig = methodSig; + } - internal sealed class MethodHandleMethodWrapper : MethodWrapper - { + public bool Equals(MethodKey other) + { + return className == other.className && methodName == other.methodName && methodSig == other.methodSig; + } - private readonly Compiler compiler; - private readonly TypeWrapper wrapper; - private readonly ClassFile.ConstantPoolItemMI cpi; + public override int GetHashCode() + { + return className.GetHashCode() ^ methodName.GetHashCode() ^ methodSig.GetHashCode(); + } - internal MethodHandleMethodWrapper(Compiler compiler, TypeWrapper wrapper, ClassFile.ConstantPoolItemMI cpi) - : base(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), Modifiers.Public, MemberFlags.None) - { - this.compiler = compiler; - this.wrapper = wrapper; - this.cpi = cpi; - } + } - private static void ToBasic(TypeWrapper tw, CodeEmitter ilgen) - { - if (tw.IsNonPrimitiveValueType) - { - tw.EmitBox(ilgen); - } - else if (tw.IsGhost) - { - tw.EmitConvSignatureTypeToStackType(ilgen); - } - } +} - private static void FromBasic(TypeWrapper tw, CodeEmitter ilgen) - { - if (tw.IsNonPrimitiveValueType) - { - tw.EmitUnbox(ilgen); - } - else if (tw.IsGhost) - { - tw.EmitConvStackTypeToSignatureType(ilgen, null); - } - else if (!tw.IsPrimitive && tw != CoreClasses.java.lang.Object.Wrapper) - { - tw.EmitCheckcast(ilgen); - } - } +namespace IKVM.Runtime +{ - internal override void EmitCall(CodeEmitter ilgen) - { - Debug.Assert(cpi.Name == "linkToVirtual" || cpi.Name == "linkToStatic" || cpi.Name == "linkToSpecial" || cpi.Name == "linkToInterface"); - EmitLinkToCall(ilgen, cpi.GetArgTypes(), cpi.GetRetType()); - } + sealed class Compiler + { + internal static readonly MethodInfo unmapExceptionMethod; + private static readonly MethodInfo fixateExceptionMethod; + private static readonly MethodInfo suppressFillInStackTraceMethod; + internal static readonly MethodInfo getTypeFromHandleMethod; + internal static readonly MethodInfo getTypeMethod; + private static readonly MethodInfo keepAliveMethod; + internal static readonly MethodWrapper getClassFromTypeHandle; + internal static readonly MethodWrapper getClassFromTypeHandle2; + private readonly DynamicTypeWrapper.FinishContext context; + private readonly DynamicTypeWrapper clazz; + private readonly MethodWrapper mw; + private readonly ClassFile classFile; + private readonly ClassFile.Method m; + private readonly CodeEmitter ilGenerator; + private readonly CodeInfo ma; + private readonly UntangledExceptionTable exceptions; + private readonly List harderrors; + private readonly LocalVarInfo localVars; + private bool nonleaf; + private readonly bool debug; + private readonly bool keepAlive; + private readonly bool strictfp; + private readonly bool emitLineNumbers; + private int[] scopeBegin; + private int[] scopeClose; +#if IMPORTER + private readonly MethodWrapper[] replacedMethodWrappers; +#endif - internal static void EmitLinkToCall(CodeEmitter ilgen, TypeWrapper[] args, TypeWrapper retType) - { -#if !FIRST_PASS && !STATIC_COMPILER + static Compiler() + { + getTypeFromHandleMethod = Types.Type.GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public, null, new Type[] { Types.RuntimeTypeHandle }, null); + getTypeMethod = Types.Object.GetMethod("GetType", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null); + keepAliveMethod = JVM.Import(typeof(System.GC)).GetMethod("KeepAlive", BindingFlags.Static | BindingFlags.Public, null, new Type[] { Types.Object }, null); + // HACK we need to special case core compilation, because the __ methods are HideFromJava + if (CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType is TypeBuilder) + { + MethodWrapper mw; + mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "()V", false); + mw.Link(); + suppressFillInStackTraceMethod = (MethodInfo)mw.GetMethod(); + mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false); + mw.Link(); + unmapExceptionMethod = (MethodInfo)mw.GetMethod(); + mw = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false); + mw.Link(); + fixateExceptionMethod = (MethodInfo)mw.GetMethod(); + } + else + { + suppressFillInStackTraceMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", Type.EmptyTypes); + unmapExceptionMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", new Type[] { Types.Exception }); + fixateExceptionMethod = CoreClasses.java.lang.Throwable.Wrapper.TypeAsBaseType.GetMethod("__", new Type[] { Types.Exception }); + } + getClassFromTypeHandle = ClassLoaderWrapper.LoadClassCritical("ikvm.runtime.Util").GetMethodWrapper("getClassFromTypeHandle", "(Lcli.System.RuntimeTypeHandle;)Ljava.lang.Class;", false); + getClassFromTypeHandle.Link(); + getClassFromTypeHandle2 = ClassLoaderWrapper.LoadClassCritical("ikvm.runtime.Util").GetMethodWrapper("getClassFromTypeHandle", "(Lcli.System.RuntimeTypeHandle;I)Ljava.lang.Class;", false); + getClassFromTypeHandle2.Link(); + } + + private Compiler(DynamicTypeWrapper.FinishContext context, TypeWrapper host, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ClassLoaderWrapper classLoader) + { + this.context = context; + this.clazz = clazz; + this.mw = mw; + this.classFile = classFile; + this.m = m; + this.ilGenerator = ilGenerator; + this.debug = classLoader.EmitDebugInfo; + this.strictfp = m.IsStrictfp; + if (mw.IsConstructor) + { + MethodWrapper finalize = clazz.GetMethodWrapper(StringConstants.FINALIZE, StringConstants.SIG_VOID, true); + keepAlive = finalize != null && finalize.DeclaringType != CoreClasses.java.lang.Object.Wrapper && finalize.DeclaringType != CoreClasses.cli.System.Object.Wrapper && finalize.DeclaringType != CoreClasses.java.lang.Throwable.Wrapper && finalize.DeclaringType != CoreClasses.cli.System.Exception.Wrapper; + } +#if IMPORTER + replacedMethodWrappers = clazz.GetReplacedMethodsFor(mw); +#endif + + TypeWrapper[] args = mw.GetParameters(); + for (int i = 0; i < args.Length; i++) + { + if (args[i].IsUnloadable) + { + ilGenerator.EmitLdarg(i + (m.IsStatic ? 0 : 1)); + EmitDynamicCast(args[i]); + ilGenerator.Emit(OpCodes.Pop); + } + } + + Profiler.Enter("MethodAnalyzer"); + try + { + if (classFile.MajorVersion < 51 && m.HasJsr) + { + JsrInliner.InlineJsrs(classLoader, mw, classFile, m); + } + MethodAnalyzer verifier = new MethodAnalyzer(host, clazz, mw, classFile, m, classLoader); + exceptions = MethodAnalyzer.UntangleExceptionBlocks(classFile, m); + ma = verifier.GetCodeInfoAndErrors(exceptions, out harderrors); + localVars = new LocalVarInfo(ma, classFile, m, exceptions, mw, classLoader); + } + finally + { + Profiler.Leave("MethodAnalyzer"); + } + + if (m.LineNumberTableAttribute != null) + { + if (classLoader.EmitDebugInfo) + { + emitLineNumbers = true; + } + else if (classLoader.EmitStackTraceInfo) + { + InstructionFlags[] flags = ComputePartialReachability(0, false); + for (int i = 0; i < m.Instructions.Length; i++) + { + if ((flags[i] & InstructionFlags.Reachable) == 0) + { + // skip unreachable instructions + } + else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getfield + && VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 0))) + { + // loading a field from the current object cannot throw + } + else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putfield + && VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(i, 1))) + { + // storing a field in the current object cannot throw + } + else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic + && classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz) + { + // loading a field from the current class cannot throw + } + else if (m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic + && classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz) + { + // storing a field to the current class cannot throw + } + else if (ByteCodeMetaData.CanThrowException(m.Instructions[i].NormalizedOpCode)) + { + emitLineNumbers = true; + break; + } + } + } + } + + LocalVar[] locals = localVars.GetAllLocalVars(); + foreach (LocalVar v in locals) + { + if (v.isArg) + { + int arg = m.ArgMap[v.local]; + TypeWrapper tw; + if (m.IsStatic) + { + tw = args[arg]; + } + else if (arg == 0) + { + continue; + } + else + { + tw = args[arg - 1]; + } + if (!tw.IsUnloadable && + (v.type != tw || tw.TypeAsLocalOrStackType != tw.TypeAsSignatureType)) + { + v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); + if (debug && v.name != null) + { + v.builder.SetLocalSymInfo(v.name); + } + v.isArg = false; + ilGenerator.EmitLdarg(arg); + tw.EmitConvSignatureTypeToStackType(ilGenerator); + ilGenerator.Emit(OpCodes.Stloc, v.builder); + } + } + } + + // if we're emitting debugging information, we need to use scopes for local variables + if (debug) + { + SetupLocalVariableScopes(); + } + + Workaroundx64JitBug(args); + } + + // workaround for x64 JIT bug + // https://connect.microsoft.com/VisualStudio/feedback/details/636466/variable-is-not-incrementing-in-c-release-x64#details + // (see also https://sourceforge.net/mailarchive/message.php?msg_id=28250469) + private void Workaroundx64JitBug(TypeWrapper[] args) + { + if (args.Length > (m.IsStatic ? 4 : 3) && m.ExceptionTable.Length != 0) + { + bool[] workarounds = null; + InstructionFlags[] flags = ComputePartialReachability(0, false); + for (int i = 0; i < m.Instructions.Length; i++) + { + if ((flags[i] & InstructionFlags.Reachable) == 0) + { + // skip unreachable instructions + } + else + { + switch (m.Instructions[i].NormalizedOpCode) + { + case NormalizedByteCode.__iinc: + case NormalizedByteCode.__astore: + case NormalizedByteCode.__istore: + case NormalizedByteCode.__lstore: + case NormalizedByteCode.__fstore: + case NormalizedByteCode.__dstore: + int arg = m.IsStatic ? m.Instructions[i].Arg1 : m.Instructions[i].Arg1 - 1; + if (arg >= 3 && arg < args.Length) + { + if (workarounds == null) + { + workarounds = new bool[args.Length + 1]; + } + workarounds[m.Instructions[i].Arg1] = true; + } + break; + } + } + } + if (workarounds != null) + { + for (int i = 0; i < workarounds.Length; i++) + { + if (workarounds[i]) + { + // TODO prevent this from getting optimized away + ilGenerator.EmitLdarga(i); + ilGenerator.Emit(OpCodes.Pop); + } + } + } + } + } + + private void SetupLocalVariableScopes() + { + LocalVariableTableEntry[] lvt = m.LocalVariableTableAttribute; + if (lvt != null) + { + scopeBegin = new int[m.Instructions.Length]; + scopeClose = new int[m.Instructions.Length]; + + // FXBUG make sure we always have an outer scope + // (otherwise LocalBuilder.SetLocalSymInfo() might throw an IndexOutOfRangeException) + // fix for bug 2881954. + scopeBegin[0]++; + scopeClose[m.Instructions.Length - 1]++; + + for (int i = 0; i < lvt.Length; i++) + { + int startIndex = SafeFindPcIndex(lvt[i].start_pc); + int endIndex = SafeFindPcIndex(lvt[i].start_pc + lvt[i].length); + if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) + { + if (startIndex > 0) + { + // NOTE javac (correctly) sets start_pc of the LVT entry to the instruction + // following the store that first initializes the local, so we have to + // detect that case and adjust our local scope (because we'll be creating + // the local when we encounter the first store). + LocalVar v = localVars.GetLocalVar(startIndex - 1); + if (v != null && v.local == lvt[i].index) + { + startIndex--; + } + } + scopeBegin[startIndex]++; + scopeClose[endIndex]++; + } + } + } + } + + private int SafeFindPcIndex(int pc) + { + for (int i = 0; i < m.Instructions.Length; i++) + { + if (m.Instructions[i].PC >= pc) + { + return i; + } + } + return -1; + } + + private sealed class ReturnCookie + { + private readonly CodeEmitterLabel stub; + private readonly CodeEmitterLocal local; + + internal ReturnCookie(CodeEmitterLabel stub, CodeEmitterLocal local) + { + this.stub = stub; + this.local = local; + } + + internal void EmitRet(CodeEmitter ilgen) + { + ilgen.MarkLabel(stub); + if (local != null) + { + ilgen.Emit(OpCodes.Ldloc, local); + } + ilgen.Emit(OpCodes.Ret); + } + } + + private sealed class BranchCookie + { + // NOTE Stub gets used for both the push stub (inside the exception block) as well as the pop stub (outside the block) + internal CodeEmitterLabel Stub; + internal CodeEmitterLabel TargetLabel; + internal bool ContentOnStack; + internal readonly int TargetIndex; + internal DupHelper dh; + + internal BranchCookie(Compiler compiler, int stackHeight, int targetIndex) + { + this.Stub = compiler.ilGenerator.DefineLabel(); + this.TargetIndex = targetIndex; + this.dh = new DupHelper(compiler, stackHeight); + } + + internal BranchCookie(CodeEmitterLabel label, int targetIndex) + { + this.Stub = label; + this.TargetIndex = targetIndex; + } + } + + private struct DupHelper + { + private enum StackType : byte + { + Null, + New, + This, + UnitializedThis, + FaultBlockException, + Other + } + private readonly Compiler compiler; + private readonly StackType[] types; + private readonly CodeEmitterLocal[] locals; + + internal DupHelper(Compiler compiler, int count) + { + this.compiler = compiler; + types = new StackType[count]; + locals = new CodeEmitterLocal[count]; + } + + internal void Release() + { + foreach (CodeEmitterLocal lb in locals) + { + if (lb != null) + { + compiler.ilGenerator.ReleaseTempLocal(lb); + } + } + } + + internal int Count + { + get + { + return types.Length; + } + } + + internal void SetType(int i, TypeWrapper type) + { + if (type == VerifierTypeWrapper.Null) + { + types[i] = StackType.Null; + } + else if (VerifierTypeWrapper.IsNew(type)) + { + // new objects aren't really there on the stack + types[i] = StackType.New; + } + else if (VerifierTypeWrapper.IsThis(type)) + { + types[i] = StackType.This; + } + else if (type == VerifierTypeWrapper.UninitializedThis) + { + // uninitialized references cannot be stored in a local, but we can reload them + types[i] = StackType.UnitializedThis; + } + else if (VerifierTypeWrapper.IsFaultBlockException(type)) + { + types[i] = StackType.FaultBlockException; + } + else + { + types[i] = StackType.Other; + locals[i] = compiler.ilGenerator.AllocTempLocal(compiler.GetLocalBuilderType(type)); + } + } + + internal void Load(int i) + { + switch (types[i]) + { + case StackType.Null: + compiler.ilGenerator.Emit(OpCodes.Ldnull); + break; + case StackType.New: + case StackType.FaultBlockException: + // objects aren't really there on the stack + break; + case StackType.This: + case StackType.UnitializedThis: + compiler.ilGenerator.Emit(OpCodes.Ldarg_0); + break; + case StackType.Other: + compiler.ilGenerator.Emit(OpCodes.Ldloc, locals[i]); + break; + default: + throw new InvalidOperationException(); + } + } + + internal void Store(int i) + { + switch (types[i]) + { + case StackType.Null: + case StackType.This: + case StackType.UnitializedThis: + compiler.ilGenerator.Emit(OpCodes.Pop); + break; + case StackType.New: + case StackType.FaultBlockException: + // objects aren't really there on the stack + break; + case StackType.Other: + compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]); + break; + default: + throw new InvalidOperationException(); + } + } + } + + internal static void Compile(DynamicTypeWrapper.FinishContext context, TypeWrapper host, DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, CodeEmitter ilGenerator, ref bool nonleaf) + { + var classLoader = clazz.GetClassLoader(); + if (classLoader.EmitDebugInfo) + { + if (classFile.SourcePath != null) + { + ilGenerator.DefineSymbolDocument(classLoader.GetTypeWrapperFactory().ModuleBuilder, classFile.SourcePath, SymLanguageType.Java, Guid.Empty, SymDocumentType.Text); + + // the very first instruction in the method must have an associated line number, to be able + // to step into the method in Visual Studio .NET + var table = m.LineNumberTableAttribute; + if (table != null) + { + int firstPC = int.MaxValue; + int firstLine = -1; + for (int i = 0; i < table.Length; i++) + { + if (table[i].start_pc < firstPC && table[i].line_number != 0) + { + firstLine = table[i].line_number; + firstPC = table[i].start_pc; + } + } + + if (firstLine > 0) + ilGenerator.SetLineNumber((ushort)firstLine); + } + } + } + Compiler c; + try + { + Profiler.Enter("new Compiler"); + try + { + c = new Compiler(context, host, clazz, mw, classFile, m, ilGenerator, classLoader); + } + finally + { + Profiler.Leave("new Compiler"); + } + } + catch (VerifyError x) + { +#if IMPORTER + classLoader.IssueMessage(Message.EmittedVerificationError, classFile.Name + "." + m.Name + m.Signature, x.Message); +#endif + Tracer.Error(Tracer.Verifier, x.ToString()); + clazz.SetHasVerifyError(); + // because in Java the method is only verified if it is actually called, + // we generate code here to throw the VerificationError + ilGenerator.EmitThrow("java.lang.VerifyError", x.Message); + return; + } + catch (ClassFormatError x) + { +#if IMPORTER + classLoader.IssueMessage(Message.EmittedClassFormatError, classFile.Name + "." + m.Name + m.Signature, x.Message); +#endif + Tracer.Error(Tracer.Verifier, x.ToString()); + clazz.SetHasClassFormatError(); + ilGenerator.EmitThrow("java.lang.ClassFormatError", x.Message); + return; + } + + Profiler.Enter("Compile"); + + try + { + if (m.IsSynchronized && m.IsStatic) + { + clazz.EmitClassLiteral(ilGenerator); + ilGenerator.Emit(OpCodes.Dup); + CodeEmitterLocal monitor = ilGenerator.DeclareLocal(Types.Object); + ilGenerator.Emit(OpCodes.Stloc, monitor); + ilGenerator.EmitMonitorEnter(); + ilGenerator.BeginExceptionBlock(); + var b = new Block(c, 0, int.MaxValue, -1, new List(), true); + c.Compile(b, 0); + b.Leave(); + ilGenerator.BeginFinallyBlock(); + ilGenerator.Emit(OpCodes.Ldloc, monitor); + ilGenerator.EmitMonitorExit(); + ilGenerator.Emit(OpCodes.Endfinally); + ilGenerator.EndExceptionBlock(); + b.LeaveStubs(new Block(c, 0, int.MaxValue, -1, null, false)); + } + else + { + var b = new Block(c, 0, int.MaxValue, -1, null, false); + c.Compile(b, 0); + b.Leave(); + } + + nonleaf = c.nonleaf; + } + finally + { + Profiler.Leave("Compile"); + } + } + + private sealed class Block + { + private readonly Compiler compiler; + private readonly CodeEmitter ilgen; + private readonly int beginIndex; + private readonly int endIndex; + private readonly int exceptionIndex; + private List exits; + private readonly bool nested; + private readonly object[] labels; + + internal Block(Compiler compiler, int beginIndex, int endIndex, int exceptionIndex, List exits, bool nested) + { + this.compiler = compiler; + this.ilgen = compiler.ilGenerator; + this.beginIndex = beginIndex; + this.endIndex = endIndex; + this.exceptionIndex = exceptionIndex; + this.exits = exits; + this.nested = nested; + labels = new object[compiler.m.Instructions.Length]; + } + + internal int EndIndex + { + get + { + return endIndex; + } + } + + internal int ExceptionIndex + { + get + { + return exceptionIndex; + } + } + + internal void SetBackwardBranchLabel(int instructionIndex, BranchCookie bc) + { + // NOTE we're overwriting the label that is already there + labels[instructionIndex] = bc.Stub; + if (exits == null) + { + exits = new List(); + } + exits.Add(bc); + } + + internal CodeEmitterLabel GetLabel(int targetIndex) + { + if (IsInRange(targetIndex)) + { + CodeEmitterLabel l = (CodeEmitterLabel)labels[targetIndex]; + if (l == null) + { + l = ilgen.DefineLabel(); + labels[targetIndex] = l; + } + return l; + } + else + { + BranchCookie l = (BranchCookie)labels[targetIndex]; + if (l == null) + { + // if we're branching out of the current exception block, we need to indirect this thru a stub + // that saves the stack and uses leave to leave the exception block (to another stub that recovers + // the stack) + int stackHeight = compiler.ma.GetStackHeight(targetIndex); + BranchCookie bc = new BranchCookie(compiler, stackHeight, targetIndex); + bc.ContentOnStack = true; + for (int i = 0; i < stackHeight; i++) + { + bc.dh.SetType(i, compiler.ma.GetRawStackTypeWrapper(targetIndex, i)); + } + exits.Add(bc); + l = bc; + labels[targetIndex] = l; + } + return l.Stub; + } + } + + internal bool HasLabel(int instructionIndex) + { + return labels[instructionIndex] != null; + } + + internal void MarkLabel(int instructionIndex) + { + object label = labels[instructionIndex]; + if (label == null) + { + CodeEmitterLabel l = ilgen.DefineLabel(); + ilgen.MarkLabel(l); + labels[instructionIndex] = l; + } + else + { + ilgen.MarkLabel((CodeEmitterLabel)label); + } + } + + internal bool IsInRange(int index) + { + return beginIndex <= index && index < endIndex; + } + + internal void Leave() + { + if (exits != null) + { + for (int i = 0; i < exits.Count; i++) + { + object exit = exits[i]; + BranchCookie bc = exit as BranchCookie; + if (bc != null && bc.ContentOnStack) + { + bc.ContentOnStack = false; + int stack = bc.dh.Count; + // HACK this is unreachable code, but we make sure that + // forward pass verification always yields a valid stack + // (this is required for unreachable leave stubs that are + // generated for unreachable code that follows an + // embedded exception emitted by the compiler for invalid + // code (e.g. NoSuchFieldError)) + for (int n = stack - 1; n >= 0; n--) + { + bc.dh.Load(n); + } + ilgen.MarkLabel(bc.Stub); + for (int n = 0; n < stack; n++) + { + bc.dh.Store(n); + } + if (bc.TargetIndex == -1) + { + ilgen.EmitBr(bc.TargetLabel); + } + else + { + bc.Stub = ilgen.DefineLabel(); + ilgen.EmitLeave(bc.Stub); + } + } + } + } + } + + internal void LeaveStubs(Block newBlock) + { + if (exits != null) + { + for (int i = 0; i < exits.Count; i++) + { + object exit = exits[i]; + ReturnCookie rc = exit as ReturnCookie; + if (rc != null) + { + if (newBlock.IsNested) + { + newBlock.exits.Add(rc); + } + else + { + rc.EmitRet(ilgen); + } + } + else + { + BranchCookie bc = exit as BranchCookie; + if (bc != null && bc.TargetIndex != -1) + { + Debug.Assert(!bc.ContentOnStack); + // if the target is within the new block, we handle it, otherwise we + // defer the cookie to our caller + if (newBlock.IsInRange(bc.TargetIndex)) + { + bc.ContentOnStack = true; + ilgen.MarkLabel(bc.Stub); + int stack = bc.dh.Count; + for (int n = stack - 1; n >= 0; n--) + { + bc.dh.Load(n); + } + ilgen.EmitBr(newBlock.GetLabel(bc.TargetIndex)); + } + else + { + newBlock.exits.Add(bc); + } + } + } + } + } + } + + internal void AddExitHack(object bc) + { + exits.Add(bc); + } + + internal bool IsNested => nested; + + } + + private void Compile(Block block, int startIndex) + { + InstructionFlags[] flags = ComputePartialReachability(startIndex, true); + ExceptionTableEntry[] exceptions = GetExceptionTableFor(flags); + int exceptionIndex = 0; + Instruction[] code = m.Instructions; + Stack blockStack = new Stack(); + bool instructionIsForwardReachable = true; + if (startIndex != 0) + { + for (int i = 0; i < flags.Length; i++) + { + if ((flags[i] & InstructionFlags.Reachable) != 0) + { + if (i < startIndex) + { + instructionIsForwardReachable = false; + ilGenerator.EmitBr(block.GetLabel(startIndex)); + } + break; + } + } + } + for (int i = 0; i < code.Length; i++) + { + Instruction instr = code[i]; + + if (scopeBegin != null) + { + for (int j = scopeClose[i]; j > 0; j--) + { + ilGenerator.EndScope(); + } + for (int j = scopeBegin[i]; j > 0; j--) + { + ilGenerator.BeginScope(); + } + } + + // if we've left the current exception block, do the exit processing + while (block.EndIndex == i) + { + block.Leave(); + + ExceptionTableEntry exc = exceptions[block.ExceptionIndex]; + + Block prevBlock = block; + block = blockStack.Pop(); + + exceptionIndex = block.ExceptionIndex + 1; + // skip over exception handlers that are no longer relevant + for (; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].endIndex <= i; exceptionIndex++) + { + } + + int handlerIndex = exc.handlerIndex; + + if (exc.catch_type == 0 && VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(handlerIndex, 0))) + { + if (exc.isFinally) + { + ilGenerator.BeginFinallyBlock(); + } + else + { + ilGenerator.BeginFaultBlock(); + } + Block b = new Block(this, 0, block.EndIndex, -1, null, false); + Compile(b, handlerIndex); + b.Leave(); + ilGenerator.EndExceptionBlock(); + } + else + { + TypeWrapper exceptionTypeWrapper; + bool remap; + if (exc.catch_type == 0) + { + exceptionTypeWrapper = CoreClasses.java.lang.Throwable.Wrapper; + remap = true; + } + else + { + exceptionTypeWrapper = classFile.GetConstantPoolClassType(exc.catch_type); + remap = exceptionTypeWrapper.IsUnloadable || !exceptionTypeWrapper.IsSubTypeOf(CoreClasses.cli.System.Exception.Wrapper); + } + Type excType = exceptionTypeWrapper.TypeAsExceptionType; + bool mapSafe = !exceptionTypeWrapper.IsUnloadable && !exceptionTypeWrapper.IsMapUnsafeException && !exceptionTypeWrapper.IsRemapped; + if (mapSafe) + { + ilGenerator.BeginCatchBlock(excType); + } + else + { + ilGenerator.BeginCatchBlock(Types.Exception); + } + BranchCookie bc = new BranchCookie(this, 1, exc.handlerIndex); + prevBlock.AddExitHack(bc); + Instruction handlerInstr = code[handlerIndex]; + bool unusedException = (handlerInstr.NormalizedOpCode == NormalizedByteCode.__pop || + (handlerInstr.NormalizedOpCode == NormalizedByteCode.__astore && + localVars.GetLocalVar(handlerIndex) == null)); + int mapFlags = unusedException ? 2 : 0; + if (mapSafe && unusedException) + { + // we don't need to do anything with the exception + } + else if (mapSafe) + { + ilGenerator.EmitLdc_I4(mapFlags | 1); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(excType)); + } + else if (exceptionTypeWrapper == CoreClasses.java.lang.Throwable.Wrapper) + { + ilGenerator.EmitLdc_I4(mapFlags); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception)); + } + else + { + ilGenerator.EmitLdc_I4(mapFlags | (remap ? 0 : 1)); + if (exceptionTypeWrapper.IsUnloadable) + { + Profiler.Count("EmitDynamicExceptionHandler"); + EmitDynamicClassLiteral(exceptionTypeWrapper); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicMapException); + } + else + { + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(excType)); + } + if (!unusedException) + { + ilGenerator.Emit(OpCodes.Dup); + } + CodeEmitterLabel leave = ilGenerator.DefineLabel(); + ilGenerator.EmitBrtrue(leave); + ilGenerator.Emit(OpCodes.Rethrow); + ilGenerator.MarkLabel(leave); + } + if (unusedException) + { + // we must still have an item on the stack, even though it isn't used! + bc.dh.SetType(0, VerifierTypeWrapper.Null); + } + else + { + bc.dh.SetType(0, exceptionTypeWrapper); + bc.dh.Store(0); + } + ilGenerator.EmitLeave(bc.Stub); + ilGenerator.EndExceptionBlock(); + } + prevBlock.LeaveStubs(block); + } + + if ((flags[i] & InstructionFlags.Reachable) == 0) + { + // skip any unreachable instructions + continue; + } + + // if there was a forward branch to this instruction, it is forward reachable + instructionIsForwardReachable |= block.HasLabel(i); + + if (block.HasLabel(i) || (flags[i] & InstructionFlags.BranchTarget) != 0) + { + block.MarkLabel(i); + } + + // if the instruction is only backward reachable, ECMA says it must have an empty stack, + // so we move the stack to locals + if (!instructionIsForwardReachable) + { + int stackHeight = ma.GetStackHeight(i); + if (stackHeight != 0) + { + BranchCookie bc = new BranchCookie(this, stackHeight, -1); + bc.ContentOnStack = true; + bc.TargetLabel = ilGenerator.DefineLabel(); + ilGenerator.MarkLabel(bc.TargetLabel); + for (int j = 0; j < stackHeight; j++) + { + bc.dh.SetType(j, ma.GetRawStackTypeWrapper(i, j)); + } + for (int j = stackHeight - 1; j >= 0; j--) + { + bc.dh.Load(j); + } + block.SetBackwardBranchLabel(i, bc); + } + } + + // if we're entering an exception block, we need to setup the exception block and + // transfer the stack into it + // Note that an exception block that *starts* at an unreachable instruction, + // is completely unreachable, because it is impossible to branch into an exception block. + for (; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].startIndex == i; exceptionIndex++) + { + int stackHeight = ma.GetStackHeight(i); + if (stackHeight != 0) + { + DupHelper dh = new DupHelper(this, stackHeight); + for (int k = 0; k < stackHeight; k++) + { + dh.SetType(k, ma.GetRawStackTypeWrapper(i, k)); + dh.Store(k); + } + ilGenerator.BeginExceptionBlock(); + for (int k = stackHeight - 1; k >= 0; k--) + { + dh.Load(k); + } + dh.Release(); + } + else + { + ilGenerator.BeginExceptionBlock(); + } + blockStack.Push(block); + block = new Block(this, exceptions[exceptionIndex].startIndex, exceptions[exceptionIndex].endIndex, exceptionIndex, new List(), true); + block.MarkLabel(i); + } + + if (emitLineNumbers) + { + ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute; + for (int j = 0; j < table.Length; j++) + { + if (table[j].start_pc == code[i].PC && table[j].line_number != 0) + { + ilGenerator.SetLineNumber(table[j].line_number); + break; + } + } + } + + if (keepAlive) + { + // JSR 133 specifies that a finalizer cannot run while the constructor is still in progress. + // This code attempts to implement that by adding calls to GC.KeepAlive(this) before return, + // backward branches and throw instructions. I don't think it is perfect, you may be able to + // fool it by calling a trivial method that loops forever which the CLR JIT will then inline + // and see that control flow doesn't continue and hence the lifetime of "this" will be + // shorter than the constructor. + switch (ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode)) + { + case ByteCodeFlowControl.Return: + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, keepAliveMethod); + break; + case ByteCodeFlowControl.Branch: + case ByteCodeFlowControl.CondBranch: + if (instr.TargetIndex <= i) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, keepAliveMethod); + } + break; + case ByteCodeFlowControl.Throw: + case ByteCodeFlowControl.Switch: + if (ma.GetLocalTypeWrapper(i, 0) != VerifierTypeWrapper.UninitializedThis) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, keepAliveMethod); + } + break; + } + } + + switch (instr.NormalizedOpCode) + { + case NormalizedByteCode.__getstatic: + { + var cpi = classFile.GetFieldref(instr.Arg1); + if (cpi.GetClassType() != clazz) + nonleaf = true; // we may trigger a static initializer, which is equivalent to a call + + var field = cpi.GetField(); + field.EmitGet(ilGenerator); + field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator); + break; + } + case NormalizedByteCode.__getfield: + { + ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); + FieldWrapper field = cpi.GetField(); + if (ma.GetStackTypeWrapper(i, 0).IsUnloadable) + { + if (field.IsProtected) + { + // downcast receiver to our type + clazz.EmitCheckcast(ilGenerator); + } + else + { + // downcast receiver to field declaring type + field.DeclaringType.EmitCheckcast(ilGenerator); + } + } + field.EmitGet(ilGenerator); + field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator); + break; + } + case NormalizedByteCode.__putstatic: + { + var cpi = classFile.GetFieldref(instr.Arg1); + if (cpi.GetClassType() != clazz) + nonleaf = true; // we may trigger a static initializer, which is equivalent to a call + FieldWrapper field = cpi.GetField(); + TypeWrapper tw = field.FieldTypeWrapper; + tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); + if (strictfp) + { + // no need to convert + } + else if (tw == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + field.EmitSet(ilGenerator); + break; + } + case NormalizedByteCode.__putfield: + { + ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); + FieldWrapper field = cpi.GetField(); + TypeWrapper tw = field.FieldTypeWrapper; + if (ma.GetStackTypeWrapper(i, 1).IsUnloadable) + { + CodeEmitterLocal temp = ilGenerator.UnsafeAllocTempLocal(tw.TypeAsLocalOrStackType); + ilGenerator.Emit(OpCodes.Stloc, temp); + if (field.IsProtected) + { + // downcast receiver to our type + clazz.EmitCheckcast(ilGenerator); + } + else + { + // downcast receiver to field declaring type + field.DeclaringType.EmitCheckcast(ilGenerator); + } + ilGenerator.Emit(OpCodes.Ldloc, temp); + } + tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); + if (strictfp) + { + // no need to convert + } + else if (tw == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + field.EmitSet(ilGenerator); + break; + } + case NormalizedByteCode.__dynamic_getstatic: + case NormalizedByteCode.__dynamic_putstatic: + case NormalizedByteCode.__dynamic_getfield: + case NormalizedByteCode.__dynamic_putfield: + nonleaf = true; + DynamicGetPutField(instr, i); + break; + case NormalizedByteCode.__aconst_null: + ilGenerator.Emit(OpCodes.Ldnull); + break; + case NormalizedByteCode.__iconst: + ilGenerator.EmitLdc_I4(instr.NormalizedArg1); + break; + case NormalizedByteCode.__lconst_0: + ilGenerator.EmitLdc_I8(0L); + break; + case NormalizedByteCode.__lconst_1: + ilGenerator.EmitLdc_I8(1L); + break; + case NormalizedByteCode.__fconst_0: + case NormalizedByteCode.__dconst_0: + // floats are stored as native size on the stack, so both R4 and R8 are the same + ilGenerator.EmitLdc_R4(0.0f); + break; + case NormalizedByteCode.__fconst_1: + case NormalizedByteCode.__dconst_1: + // floats are stored as native size on the stack, so both R4 and R8 are the same + ilGenerator.EmitLdc_R4(1.0f); + break; + case NormalizedByteCode.__fconst_2: + ilGenerator.EmitLdc_R4(2.0f); + break; + case NormalizedByteCode.__ldc_nothrow: + case NormalizedByteCode.__ldc: + EmitLoadConstant(ilGenerator, instr.Arg1); + break; + case NormalizedByteCode.__invokedynamic: + { + var cpi = classFile.GetInvokeDynamic(instr.Arg1); + CastInterfaceArgs(null, cpi.GetArgTypes(), i, false); + if (!LambdaMetafactory.Emit(context, classFile, instr.Arg1, cpi, ilGenerator)) + { + EmitInvokeDynamic(cpi); + EmitReturnTypeConversion(cpi.GetRetType()); + } + nonleaf = true; + break; + } + case NormalizedByteCode.__dynamic_invokestatic: + case NormalizedByteCode.__privileged_invokestatic: + case NormalizedByteCode.__invokestatic: + case NormalizedByteCode.__methodhandle_link: + { + MethodWrapper method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); + if (method.IsIntrinsic && method.EmitIntrinsic(new EmitIntrinsicContext(method, context, ilGenerator, ma, i, mw, classFile, code, flags))) + { + break; + } + // if the stack values don't match the argument types (for interface argument types) + // we must emit code to cast the stack value to the interface type + CastInterfaceArgs(method.DeclaringType, method.GetParameters(), i, false); + if (method.HasCallerID) + { + context.EmitCallerID(ilGenerator, m.IsLambdaFormCompiled); + } + method.EmitCall(ilGenerator); + EmitReturnTypeConversion(method.ReturnType); + nonleaf = true; + break; + } + case NormalizedByteCode.__dynamic_invokeinterface: + case NormalizedByteCode.__dynamic_invokevirtual: + case NormalizedByteCode.__dynamic_invokespecial: + case NormalizedByteCode.__privileged_invokevirtual: + case NormalizedByteCode.__privileged_invokespecial: + case NormalizedByteCode.__invokevirtual: + case NormalizedByteCode.__invokeinterface: + case NormalizedByteCode.__invokespecial: + case NormalizedByteCode.__methodhandle_invoke: + { + var isinvokespecial = instr.NormalizedOpCode == NormalizedByteCode.__invokespecial + || instr.NormalizedOpCode == NormalizedByteCode.__dynamic_invokespecial + || instr.NormalizedOpCode == NormalizedByteCode.__privileged_invokespecial; + var method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); + var argcount = method.GetParameters().Length; + var type = ma.GetRawStackTypeWrapper(i, argcount); + TypeWrapper thisType = ComputeThisType(type, method, instr.NormalizedOpCode); + + var eic = new EmitIntrinsicContext(method, context, ilGenerator, ma, i, mw, classFile, code, flags); + if (method.IsIntrinsic && method.EmitIntrinsic(eic)) + { + nonleaf |= eic.NonLeaf; + break; + } + + nonleaf = true; + + // HACK this code is duplicated in java.lang.invoke.cs + if (method.IsFinalizeOrClone) + { + // HACK we may need to redirect finalize or clone from java.lang.Object/Throwable + // to a more specific base type. + if (thisType.IsAssignableTo(CoreClasses.cli.System.Object.Wrapper)) + { + method = CoreClasses.cli.System.Object.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); + } + else if (thisType.IsAssignableTo(CoreClasses.cli.System.Exception.Wrapper)) + { + method = CoreClasses.cli.System.Exception.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); + } + else if (thisType.IsAssignableTo(CoreClasses.java.lang.Throwable.Wrapper)) + { + method = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper(method.Name, method.Signature, true); + } + } + + // if the stack values don't match the argument types (for interface argument types) + // we must emit code to cast the stack value to the interface type + if (isinvokespecial && method.IsConstructor && VerifierTypeWrapper.IsNew(type)) + { + CastInterfaceArgs(method.DeclaringType, method.GetParameters(), i, false); + } + else + { + // the this reference is included in the argument list because it may also need to be cast + TypeWrapper[] methodArgs = method.GetParameters(); + TypeWrapper[] args = new TypeWrapper[methodArgs.Length + 1]; + methodArgs.CopyTo(args, 1); + if (instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface) + { + args[0] = method.DeclaringType; + } + else + { + args[0] = thisType; + } + CastInterfaceArgs(method.DeclaringType, args, i, true); + } + + if (isinvokespecial && method.IsConstructor) + { + if (VerifierTypeWrapper.IsNew(type)) + { + // we have to construct a list of all the unitialized references to the object + // we're about to create on the stack, so that we can reconstruct the stack after + // the "newobj" instruction + int trivcount = 0; + bool nontrivial = false; + bool[] stackfix = new bool[ma.GetStackHeight(i) - (argcount + 1)]; + for (int j = 0; j < stackfix.Length; j++) + { + if (ma.GetRawStackTypeWrapper(i, argcount + 1 + j) == type) + { + stackfix[j] = true; + if (trivcount == j) + { + trivcount++; + } + else + { + // if there is other stuff on the stack between the new object + // references, we need to do more work to construct the proper stack + // layout after the newobj instruction + nontrivial = true; + } + } + } + for (int j = 0; !nontrivial && j < m.MaxLocals; j++) + { + if (ma.GetLocalTypeWrapper(i, j) == type) + { + nontrivial = true; + } + } + if (!thisType.IsUnloadable && thisType.IsSubTypeOf(CoreClasses.java.lang.Throwable.Wrapper)) + { + // if the next instruction is an athrow and the exception type + // doesn't override fillInStackTrace, we can suppress the call + // to fillInStackTrace from the constructor (and this is + // a huge perf win) + // NOTE we also can't call suppressFillInStackTrace for non-Java + // exceptions (because then the suppress flag won't be cleared), + // but this case is handled by the "is fillInStackTrace overridden?" + // test, because cli.System.Exception overrides fillInStackTrace. + if (code[i + 1].NormalizedOpCode == NormalizedByteCode.__athrow) + { + if (thisType.GetMethodWrapper("fillInStackTrace", "()Ljava.lang.Throwable;", true).DeclaringType == CoreClasses.java.lang.Throwable.Wrapper) + { + ilGenerator.Emit(OpCodes.Call, suppressFillInStackTraceMethod); + } + if ((flags[i + 1] & InstructionFlags.BranchTarget) == 0) + { + code[i + 1].PatchOpCode(NormalizedByteCode.__athrow_no_unmap); + } + } + } + method.EmitNewobj(ilGenerator); + if (!thisType.IsUnloadable && thisType.IsSubTypeOf(CoreClasses.cli.System.Exception.Wrapper)) + { + // we call Throwable.__() to disable remapping the exception + ilGenerator.Emit(OpCodes.Call, fixateExceptionMethod); + } + if (nontrivial) + { + // this could be done a little more efficiently, but since in practice this + // code never runs (for code compiled from Java source) it doesn't + // really matter + CodeEmitterLocal newobj = ilGenerator.DeclareLocal(GetLocalBuilderType(thisType)); + ilGenerator.Emit(OpCodes.Stloc, newobj); + CodeEmitterLocal[] tempstack = new CodeEmitterLocal[stackfix.Length]; + for (int j = 0; j < stackfix.Length; j++) + { + if (!stackfix[j]) + { + TypeWrapper stacktype = ma.GetStackTypeWrapper(i, argcount + 1 + j); + // it could be another new object reference (not from current invokespecial + // instruction) + if (stacktype == VerifierTypeWrapper.Null) + { + // NOTE we abuse the newobj local as a cookie to signal null! + tempstack[j] = newobj; + ilGenerator.Emit(OpCodes.Pop); + } + else if (!VerifierTypeWrapper.IsNotPresentOnStack(stacktype)) + { + CodeEmitterLocal lb = ilGenerator.DeclareLocal(GetLocalBuilderType(stacktype)); + ilGenerator.Emit(OpCodes.Stloc, lb); + tempstack[j] = lb; + } + } + } + for (int j = stackfix.Length - 1; j >= 0; j--) + { + if (stackfix[j]) + { + ilGenerator.Emit(OpCodes.Ldloc, newobj); + } + else if (tempstack[j] != null) + { + // NOTE we abuse the newobj local as a cookie to signal null! + if (tempstack[j] == newobj) + { + ilGenerator.Emit(OpCodes.Ldnull); + } + else + { + ilGenerator.Emit(OpCodes.Ldloc, tempstack[j]); + } + } + } + LocalVar[] locals = localVars.GetLocalVarsForInvokeSpecial(i); + for (int j = 0; j < locals.Length; j++) + { + if (locals[j] != null) + { + if (locals[j].builder == null) + { + // for invokespecial the resulting type can never be null + locals[j].builder = ilGenerator.DeclareLocal(GetLocalBuilderType(locals[j].type)); + } + ilGenerator.Emit(OpCodes.Ldloc, newobj); + ilGenerator.Emit(OpCodes.Stloc, locals[j].builder); + } + } + } + else + { + if (trivcount == 0) + { + ilGenerator.Emit(OpCodes.Pop); + } + else + { + for (int j = 1; j < trivcount; j++) + { + ilGenerator.Emit(OpCodes.Dup); + } + } + } + } + else + { + Debug.Assert(type == VerifierTypeWrapper.UninitializedThis); + method.EmitCall(ilGenerator); + LocalVar[] locals = localVars.GetLocalVarsForInvokeSpecial(i); + for (int j = 0; j < locals.Length; j++) + { + if (locals[j] != null) + { + if (locals[j].builder == null) + { + // for invokespecial the resulting type can never be null + locals[j].builder = ilGenerator.DeclareLocal(GetLocalBuilderType(locals[j].type)); + } + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Stloc, locals[j].builder); + } + } + } + } + else + { + if (method.HasCallerID) + { + context.EmitCallerID(ilGenerator, m.IsLambdaFormCompiled); + } + + if (isinvokespecial) + { + if (VerifierTypeWrapper.IsThis(type)) + { + method.EmitCall(ilGenerator); + } + else if (method.IsPrivate) + { + // if the method is private, we can get away with a callvirt (and not generate the stub) + method.EmitCallvirt(ilGenerator); + } + else if (instr.NormalizedOpCode == NormalizedByteCode.__privileged_invokespecial) + { + method.EmitCall(ilGenerator); + } + else + { + ilGenerator.Emit(OpCodes.Callvirt, context.GetInvokeSpecialStub(method)); + } + } + else + { + // NOTE this check is written somewhat pessimistically, because we need to + // take remapped types into account. For example, Throwable.getClass() "overrides" + // the final Object.getClass() method and we don't want to call Object.getClass() + // on a Throwable instance, because that would yield unverifiable code (java.lang.Throwable + // extends System.Exception instead of java.lang.Object in the .NET type system). + if (VerifierTypeWrapper.IsThis(type) + && (method.IsFinal || clazz.IsFinal) + && clazz.GetMethodWrapper(method.Name, method.Signature, true) == method) + { + // we're calling a method on our own instance that can't possibly be overriden, + // so we don't need to use callvirt + method.EmitCall(ilGenerator); + } + else + { + method.EmitCallvirt(ilGenerator); + } + } + EmitReturnTypeConversion(method.ReturnType); + } + break; + } + case NormalizedByteCode.__clone_array: + ilGenerator.Emit(OpCodes.Callvirt, ArrayTypeWrapper.CloneMethod); + break; + case NormalizedByteCode.__return: + case NormalizedByteCode.__areturn: + case NormalizedByteCode.__ireturn: + case NormalizedByteCode.__lreturn: + case NormalizedByteCode.__freturn: + case NormalizedByteCode.__dreturn: + { + if (block.IsNested) + { + // if we're inside an exception block, copy TOS to local, emit "leave" and push item onto our "todo" list + CodeEmitterLocal local = null; + if (instr.NormalizedOpCode != NormalizedByteCode.__return) + { + var retTypeWrapper = mw.ReturnType; + retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); + local = ilGenerator.UnsafeAllocTempLocal(retTypeWrapper.TypeAsSignatureType); + ilGenerator.Emit(OpCodes.Stloc, local); + } + + var label = ilGenerator.DefineLabel(); + ilGenerator.EmitLeave(label); + block.AddExitHack(new ReturnCookie(label, local)); + } + else + { + // HACK the x64 JIT is lame and optimizes calls before ret into a tail call + // and this makes the method disappear from the call stack, so we try to thwart that + // by inserting some bogus instructions between the call and the return. + // Note that this optimization doesn't appear to happen if the method has exception handlers, + // so in that case we don't do anything. + var x64hack = false; + if (exceptions.Length == 0 && i > 0) + { + int k = i - 1; + while (k > 0 && (code[k].NormalizedOpCode == NormalizedByteCode.__nop || code[k].NormalizedOpCode == NormalizedByteCode.__pop)) + k--; + + switch (code[k].NormalizedOpCode) + { + case NormalizedByteCode.__invokeinterface: + case NormalizedByteCode.__invokespecial: + case NormalizedByteCode.__invokestatic: + case NormalizedByteCode.__invokevirtual: + x64hack = true; + break; + } + } + + // if there is junk on the stack (other than the return value), we must pop it off + // because in .NET this is invalid (unlike in Java) + var stackHeight = ma.GetStackHeight(i); + if (instr.NormalizedOpCode == NormalizedByteCode.__return) + { + if (stackHeight != 0 || x64hack) + ilGenerator.EmitClearStack(); + + ilGenerator.Emit(OpCodes.Ret); + } + else + { + var retTypeWrapper = mw.ReturnType; + retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); + if (stackHeight != 1) + { + var local = ilGenerator.AllocTempLocal(retTypeWrapper.TypeAsSignatureType); + ilGenerator.Emit(OpCodes.Stloc, local); + ilGenerator.EmitClearStack(); + ilGenerator.Emit(OpCodes.Ldloc, local); + ilGenerator.ReleaseTempLocal(local); + } + else if (x64hack) + { + ilGenerator.EmitTailCallPrevention(); + } + + ilGenerator.Emit(OpCodes.Ret); + } + } + break; + } + case NormalizedByteCode.__aload: + { + TypeWrapper type = ma.GetLocalTypeWrapper(i, instr.NormalizedArg1); + if (type == VerifierTypeWrapper.Null) + { + // if the local is known to be null, we just emit a null + ilGenerator.Emit(OpCodes.Ldnull); + } + else if (VerifierTypeWrapper.IsNotPresentOnStack(type)) + { + // since object isn't represented on the stack, we don't need to do anything here + } + else if (VerifierTypeWrapper.IsThis(type)) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + } + else if (type == VerifierTypeWrapper.UninitializedThis) + { + // any unitialized this reference has to be loaded from arg 0 + // NOTE if the method overwrites the this references, it will always end up in + // a different local (due to the way the local variable liveness analysis works), + // so we don't have to worry about that. + ilGenerator.Emit(OpCodes.Ldarg_0); + } + else + { + LocalVar v = LoadLocal(i); + if (!type.IsUnloadable && (v.type.IsUnloadable || !v.type.IsAssignableTo(type))) + { + type.EmitCheckcast(ilGenerator); + } + } + break; + } + case NormalizedByteCode.__astore: + { + TypeWrapper type = ma.GetRawStackTypeWrapper(i, 0); + if (VerifierTypeWrapper.IsNotPresentOnStack(type)) + { + // object isn't really on the stack, so we can't copy it into the local + // (and the local doesn't exist anyway) + } + else if (type == VerifierTypeWrapper.UninitializedThis) + { + // any unitialized reference is always the this reference, we don't store anything + // here (because CLR won't allow unitialized references in locals) and then when + // the unitialized ref is loaded we redirect to the this reference + ilGenerator.Emit(OpCodes.Pop); + } + else + { + StoreLocal(i); + } + break; + } + case NormalizedByteCode.__iload: + case NormalizedByteCode.__lload: + case NormalizedByteCode.__fload: + case NormalizedByteCode.__dload: + LoadLocal(i); + break; + case NormalizedByteCode.__istore: + case NormalizedByteCode.__lstore: + StoreLocal(i); + break; + case NormalizedByteCode.__fstore: + StoreLocal(i); + break; + case NormalizedByteCode.__dstore: + if (ma.IsStackTypeExtendedDouble(i, 0)) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + StoreLocal(i); + break; + case NormalizedByteCode.__new: + { + TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); + if (wrapper.IsUnloadable) + { + Profiler.Count("EmitDynamicNewCheckOnly"); + // this is here to make sure we throw the exception in the right location (before + // evaluating the constructor arguments) + EmitDynamicClassLiteral(wrapper); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly); + } + else if (wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1, flags)) + { + // trigger cctor (as the spec requires) + wrapper.EmitRunClassConstructor(ilGenerator); + } + // we don't actually allocate the object here, the call to will be converted into a newobj instruction + break; + } + case NormalizedByteCode.__multianewarray: + { + CodeEmitterLocal localArray = ilGenerator.UnsafeAllocTempLocal(JVM.Import(typeof(int[]))); + CodeEmitterLocal localInt = ilGenerator.UnsafeAllocTempLocal(Types.Int32); + ilGenerator.EmitLdc_I4(instr.Arg2); + ilGenerator.Emit(OpCodes.Newarr, Types.Int32); + ilGenerator.Emit(OpCodes.Stloc, localArray); + for (int j = 1; j <= instr.Arg2; j++) + { + ilGenerator.Emit(OpCodes.Stloc, localInt); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + ilGenerator.EmitLdc_I4(instr.Arg2 - j); + ilGenerator.Emit(OpCodes.Ldloc, localInt); + ilGenerator.Emit(OpCodes.Stelem_I4); + } + TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); + if (wrapper.IsUnloadable) + { + Profiler.Count("EmitDynamicMultianewarray"); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + EmitDynamicClassLiteral(wrapper); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicMultianewarray); + } + else if (wrapper.IsGhost || wrapper.IsGhostArray) + { + TypeWrapper tw = wrapper; + while (tw.IsArray) + { + tw = tw.ElementTypeWrapper; + } + ilGenerator.Emit(OpCodes.Ldtoken, ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, wrapper.ArrayRank)); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.multianewarray_ghost); + ilGenerator.Emit(OpCodes.Castclass, wrapper.TypeAsArrayType); + } + else + { + Type type = wrapper.TypeAsArrayType; + ilGenerator.Emit(OpCodes.Ldtoken, type); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.multianewarray); + ilGenerator.Emit(OpCodes.Castclass, type); + } + break; + } + case NormalizedByteCode.__anewarray: + { + TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); + if (wrapper.IsUnloadable) + { + Profiler.Count("EmitDynamicNewarray"); + EmitDynamicClassLiteral(wrapper); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewarray); + } + else if (wrapper.IsGhost || wrapper.IsGhostArray) + { + // NOTE for ghost types we create object arrays to make sure that Ghost implementers can be + // stored in ghost arrays, but this has the unintended consequence that ghost arrays can + // contain *any* reference type (because they are compiled as Object arrays). We could + // modify aastore to emit code to check for this, but this would have an huge performance + // cost for all object arrays. + // Oddly, while the JVM accepts any reference for any other interface typed references, in the + // case of aastore it does check that the object actually implements the interface. This + // is unfortunate, but I think we can live with this minor incompatibility. + // Note that this does not break type safety, because when the incorrect object is eventually + // used as the ghost interface type it will generate a ClassCastException. + TypeWrapper tw = wrapper; + while (tw.IsArray) + { + tw = tw.ElementTypeWrapper; + } + ilGenerator.Emit(OpCodes.Ldtoken, ArrayTypeWrapper.MakeArrayType(tw.TypeAsTBD, wrapper.ArrayRank + 1)); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.anewarray_ghost.MakeGenericMethod(wrapper.TypeAsArrayType)); + } + else + { + ilGenerator.Emit(OpCodes.Newarr, wrapper.TypeAsArrayType); + } + break; + } + case NormalizedByteCode.__newarray: + switch (instr.Arg1) + { + case 4: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BOOLEAN.TypeAsArrayType); + break; + case 5: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.CHAR.TypeAsArrayType); + break; + case 6: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.FLOAT.TypeAsArrayType); + break; + case 7: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.DOUBLE.TypeAsArrayType); + break; + case 8: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BYTE.TypeAsArrayType); + break; + case 9: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.SHORT.TypeAsArrayType); + break; + case 10: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.INT.TypeAsArrayType); + break; + case 11: + ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.LONG.TypeAsArrayType); + break; + default: + // this can't happen, the verifier would have caught it + throw new InvalidOperationException(); + } + break; + case NormalizedByteCode.__checkcast: + { + TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); + if (wrapper.IsUnloadable) + { + EmitDynamicCast(wrapper); + } + else + { + wrapper.EmitCheckcast(ilGenerator); + } + break; + } + case NormalizedByteCode.__instanceof: + { + TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1); + if (wrapper.IsUnloadable) + { + EmitDynamicInstanceOf(wrapper); + } + else + { + wrapper.EmitInstanceOf(ilGenerator); + } + break; + } + case NormalizedByteCode.__aaload: + { + TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 1); + if (tw.IsUnloadable) + { + Profiler.Count("EmitDynamicAaload"); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAaload); + } + else + { + TypeWrapper elem = tw.ElementTypeWrapper; + if (elem.IsNonPrimitiveValueType) + { + Type t = elem.TypeAsTBD; + ilGenerator.Emit(OpCodes.Ldelema, t); + ilGenerator.Emit(OpCodes.Ldobj, t); + elem.EmitBox(ilGenerator); + } + else + { + ilGenerator.Emit(OpCodes.Ldelem_Ref); + } + } + break; + } + case NormalizedByteCode.__baload: + // NOTE both the JVM and the CLR use signed bytes for boolean arrays (how convenient!) + ilGenerator.Emit(OpCodes.Ldelem_I1); + break; + case NormalizedByteCode.__bastore: + ilGenerator.Emit(OpCodes.Stelem_I1); + break; + case NormalizedByteCode.__caload: + ilGenerator.Emit(OpCodes.Ldelem_U2); + break; + case NormalizedByteCode.__castore: + ilGenerator.Emit(OpCodes.Stelem_I2); + break; + case NormalizedByteCode.__saload: + ilGenerator.Emit(OpCodes.Ldelem_I2); + break; + case NormalizedByteCode.__sastore: + ilGenerator.Emit(OpCodes.Stelem_I2); + break; + case NormalizedByteCode.__iaload: + ilGenerator.Emit(OpCodes.Ldelem_I4); + break; + case NormalizedByteCode.__iastore: + ilGenerator.Emit(OpCodes.Stelem_I4); + break; + case NormalizedByteCode.__laload: + ilGenerator.Emit(OpCodes.Ldelem_I8); + break; + case NormalizedByteCode.__lastore: + ilGenerator.Emit(OpCodes.Stelem_I8); + break; + case NormalizedByteCode.__faload: + ilGenerator.Emit(OpCodes.Ldelem_R4); + break; + case NormalizedByteCode.__fastore: + ilGenerator.Emit(OpCodes.Stelem_R4); + break; + case NormalizedByteCode.__daload: + ilGenerator.Emit(OpCodes.Ldelem_R8); + break; + case NormalizedByteCode.__dastore: + if (ma.IsStackTypeExtendedDouble(i, 0)) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + ilGenerator.Emit(OpCodes.Stelem_R8); + break; + case NormalizedByteCode.__aastore: + { + TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 2); + if (tw.IsUnloadable) + { + Profiler.Count("EmitDynamicAastore"); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAastore); + } + else + { + TypeWrapper elem = tw.ElementTypeWrapper; + if (elem.IsNonPrimitiveValueType) + { + Type t = elem.TypeAsTBD; + CodeEmitterLocal local = ilGenerator.UnsafeAllocTempLocal(Types.Object); + ilGenerator.Emit(OpCodes.Stloc, local); + ilGenerator.Emit(OpCodes.Ldelema, t); + ilGenerator.Emit(OpCodes.Ldloc, local); + elem.EmitUnbox(ilGenerator); + ilGenerator.Emit(OpCodes.Stobj, t); + } + else + { + // NOTE for verifiability it is expressly *not* required that the + // value matches the array type, so we don't need to handle interface + // references here. + ilGenerator.Emit(OpCodes.Stelem_Ref); + } + } + break; + } + case NormalizedByteCode.__arraylength: + if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) + { + ilGenerator.Emit(OpCodes.Castclass, Types.Array); + ilGenerator.Emit(OpCodes.Callvirt, Types.Array.GetMethod("get_Length")); + } + else + { + ilGenerator.Emit(OpCodes.Ldlen); + } + break; + case NormalizedByteCode.__lcmp: + ilGenerator.Emit_lcmp(); + break; + case NormalizedByteCode.__fcmpl: + ilGenerator.Emit_fcmpl(); + break; + case NormalizedByteCode.__fcmpg: + ilGenerator.Emit_fcmpg(); + break; + case NormalizedByteCode.__dcmpl: + ilGenerator.Emit_dcmpl(); + break; + case NormalizedByteCode.__dcmpg: + ilGenerator.Emit_dcmpg(); + break; + case NormalizedByteCode.__if_icmpeq: + ilGenerator.EmitBeq(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_icmpne: + ilGenerator.EmitBne_Un(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_icmple: + ilGenerator.EmitBle(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_icmplt: + ilGenerator.EmitBlt(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_icmpge: + ilGenerator.EmitBge(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_icmpgt: + ilGenerator.EmitBgt(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ifle: + ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.LessOrEqual, block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__iflt: + ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.LessThan, block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ifge: + ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.GreaterOrEqual, block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ifgt: + ilGenerator.Emit_if_le_lt_ge_gt(CodeEmitter.Comparison.GreaterThan, block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__ifnonnull: + ilGenerator.EmitBrtrue(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifnull: + ilGenerator.EmitBrfalse(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_acmpeq: + ilGenerator.EmitBeq(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__if_acmpne: + ilGenerator.EmitBne_Un(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__goto: + case NormalizedByteCode.__goto_finally: + ilGenerator.EmitBr(block.GetLabel(instr.TargetIndex)); + break; + case NormalizedByteCode.__ineg: + case NormalizedByteCode.__lneg: + case NormalizedByteCode.__fneg: + case NormalizedByteCode.__dneg: + ilGenerator.Emit(OpCodes.Neg); + break; + case NormalizedByteCode.__iadd: + case NormalizedByteCode.__ladd: + ilGenerator.Emit(OpCodes.Add); + break; + case NormalizedByteCode.__fadd: + ilGenerator.Emit(OpCodes.Add); + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__dadd: + ilGenerator.Emit(OpCodes.Add); + if (strictfp) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + break; + case NormalizedByteCode.__isub: + case NormalizedByteCode.__lsub: + ilGenerator.Emit(OpCodes.Sub); + break; + case NormalizedByteCode.__fsub: + ilGenerator.Emit(OpCodes.Sub); + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__dsub: + ilGenerator.Emit(OpCodes.Sub); + if (strictfp) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + break; + case NormalizedByteCode.__ixor: + case NormalizedByteCode.__lxor: + ilGenerator.Emit(OpCodes.Xor); + break; + case NormalizedByteCode.__ior: + case NormalizedByteCode.__lor: + ilGenerator.Emit(OpCodes.Or); + break; + case NormalizedByteCode.__iand: + case NormalizedByteCode.__land: + ilGenerator.Emit(OpCodes.And); + break; + case NormalizedByteCode.__imul: + case NormalizedByteCode.__lmul: + ilGenerator.Emit(OpCodes.Mul); + break; + case NormalizedByteCode.__fmul: + ilGenerator.Emit(OpCodes.Mul); + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__dmul: + ilGenerator.Emit(OpCodes.Mul); + if (strictfp) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + break; + case NormalizedByteCode.__idiv: + ilGenerator.Emit_idiv(); + break; + case NormalizedByteCode.__ldiv: + ilGenerator.Emit_ldiv(); + break; + case NormalizedByteCode.__fdiv: + ilGenerator.Emit(OpCodes.Div); + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__ddiv: + ilGenerator.Emit(OpCodes.Div); + if (strictfp) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + break; + case NormalizedByteCode.__irem: + case NormalizedByteCode.__lrem: + { + // we need to special case taking the remainder of dividing by -1, + // because the CLR rem instruction throws an OverflowException when + // taking the remainder of dividing Int32.MinValue by -1, and + // Java just silently overflows + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + if (instr.NormalizedOpCode == NormalizedByteCode.__lrem) + { + ilGenerator.Emit(OpCodes.Conv_I8); + } + CodeEmitterLabel label = ilGenerator.DefineLabel(); + ilGenerator.EmitBne_Un(label); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + if (instr.NormalizedOpCode == NormalizedByteCode.__lrem) + { + ilGenerator.Emit(OpCodes.Conv_I8); + } + CodeEmitterLabel label2 = ilGenerator.DefineLabel(); + ilGenerator.EmitBr(label2); + ilGenerator.MarkLabel(label); + ilGenerator.Emit(OpCodes.Rem); + ilGenerator.MarkLabel(label2); + break; + } + case NormalizedByteCode.__frem: + ilGenerator.Emit(OpCodes.Rem); + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__drem: + ilGenerator.Emit(OpCodes.Rem); + if (strictfp) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + break; + case NormalizedByteCode.__ishl: + ilGenerator.Emit_And_I4(31); + ilGenerator.Emit(OpCodes.Shl); + break; + case NormalizedByteCode.__lshl: + ilGenerator.Emit_And_I4(63); + ilGenerator.Emit(OpCodes.Shl); + break; + case NormalizedByteCode.__iushr: + ilGenerator.Emit_And_I4(31); + ilGenerator.Emit(OpCodes.Shr_Un); + break; + case NormalizedByteCode.__lushr: + ilGenerator.Emit_And_I4(63); + ilGenerator.Emit(OpCodes.Shr_Un); + break; + case NormalizedByteCode.__ishr: + ilGenerator.Emit_And_I4(31); + ilGenerator.Emit(OpCodes.Shr); + break; + case NormalizedByteCode.__lshr: + ilGenerator.Emit_And_I4(63); + ilGenerator.Emit(OpCodes.Shr); + break; + case NormalizedByteCode.__swap: + { + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); + dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); + dh.Store(0); + dh.Store(1); + dh.Load(0); + dh.Load(1); + dh.Release(); + break; + } + case NormalizedByteCode.__dup: + // if the TOS contains a "new" object or a fault block exception, it isn't really there, so we don't dup it + if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 0))) + { + ilGenerator.Emit(OpCodes.Dup); + } + break; + case NormalizedByteCode.__dup2: + { + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if (type1.IsWidePrimitive) + { + ilGenerator.Emit(OpCodes.Dup); + } + else + { + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, type1); + dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); + dh.Store(0); + dh.Store(1); + dh.Load(1); + dh.Load(0); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + break; + } + case NormalizedByteCode.__dup_x1: + { + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); + dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); + dh.Store(0); + dh.Store(1); + dh.Load(0); + dh.Load(1); + dh.Load(0); + dh.Release(); + break; + } + case NormalizedByteCode.__dup2_x1: + { + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if (type1.IsWidePrimitive) + { + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, type1); + dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); + dh.Store(0); + dh.Store(1); + dh.Load(0); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + else + { + DupHelper dh = new DupHelper(this, 3); + dh.SetType(0, type1); + dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1)); + dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); + dh.Store(0); + dh.Store(1); + dh.Store(2); + dh.Load(1); + dh.Load(0); + dh.Load(2); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + break; + } + case NormalizedByteCode.__dup2_x2: + { + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1); + if (type1.IsWidePrimitive) + { + if (type2.IsWidePrimitive) + { + // Form 4 + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, type1); + dh.SetType(1, type2); + dh.Store(0); + dh.Store(1); + dh.Load(0); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + else + { + // Form 2 + DupHelper dh = new DupHelper(this, 3); + dh.SetType(0, type1); + dh.SetType(1, type2); + dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); + dh.Store(0); + dh.Store(1); + dh.Store(2); + dh.Load(0); + dh.Load(2); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + } + else + { + TypeWrapper type3 = ma.GetRawStackTypeWrapper(i, 2); + if (type3.IsWidePrimitive) + { + // Form 3 + DupHelper dh = new DupHelper(this, 3); + dh.SetType(0, type1); + dh.SetType(1, type2); + dh.SetType(2, type3); + dh.Store(0); + dh.Store(1); + dh.Store(2); + dh.Load(1); + dh.Load(0); + dh.Load(2); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + else + { + // Form 1 + DupHelper dh = new DupHelper(this, 4); + dh.SetType(0, type1); + dh.SetType(1, type2); + dh.SetType(2, type3); + dh.SetType(3, ma.GetRawStackTypeWrapper(i, 3)); + dh.Store(0); + dh.Store(1); + dh.Store(2); + dh.Store(3); + dh.Load(1); + dh.Load(0); + dh.Load(3); + dh.Load(2); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + } + break; + } + case NormalizedByteCode.__dup_x2: + { + TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1); + if (type2.IsWidePrimitive) + { + // Form 2 + DupHelper dh = new DupHelper(this, 2); + dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); + dh.SetType(1, type2); + dh.Store(0); + dh.Store(1); + dh.Load(0); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + else + { + // Form 1 + DupHelper dh = new DupHelper(this, 3); + dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0)); + dh.SetType(1, type2); + dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2)); + dh.Store(0); + dh.Store(1); + dh.Store(2); + dh.Load(0); + dh.Load(2); + dh.Load(1); + dh.Load(0); + dh.Release(); + } + break; + } + case NormalizedByteCode.__pop2: + { + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if (type1.IsWidePrimitive) + { + ilGenerator.Emit(OpCodes.Pop); + } + else + { + if (!VerifierTypeWrapper.IsNotPresentOnStack(type1)) + { + ilGenerator.Emit(OpCodes.Pop); + } + if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 1))) + { + ilGenerator.Emit(OpCodes.Pop); + } + } + break; + } + case NormalizedByteCode.__pop: + // if the TOS is a new object or a fault block exception, it isn't really there, so we don't need to pop it + if (!VerifierTypeWrapper.IsNotPresentOnStack(ma.GetRawStackTypeWrapper(i, 0))) + { + ilGenerator.Emit(OpCodes.Pop); + } + break; + case NormalizedByteCode.__monitorenter: + ilGenerator.EmitMonitorEnter(); + break; + case NormalizedByteCode.__monitorexit: + ilGenerator.EmitMonitorExit(); + break; + case NormalizedByteCode.__athrow_no_unmap: + if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) + { + ilGenerator.Emit(OpCodes.Castclass, Types.Exception); + } + ilGenerator.Emit(OpCodes.Throw); + break; + case NormalizedByteCode.__athrow: + if (VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(i, 0))) + { + ilGenerator.Emit(OpCodes.Endfinally); + } + else + { + if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable) + { + ilGenerator.Emit(OpCodes.Castclass, Types.Exception); + } + ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod); + ilGenerator.Emit(OpCodes.Throw); + } + break; + case NormalizedByteCode.__tableswitch: + { + // note that a tableswitch always has at least one entry + // (otherwise it would have failed verification) + CodeEmitterLabel[] labels = new CodeEmitterLabel[instr.SwitchEntryCount]; + for (int j = 0; j < labels.Length; j++) + { + labels[j] = block.GetLabel(instr.GetSwitchTargetIndex(j)); + } + if (instr.GetSwitchValue(0) != 0) + { + ilGenerator.EmitLdc_I4(instr.GetSwitchValue(0)); + ilGenerator.Emit(OpCodes.Sub); + } + ilGenerator.EmitSwitch(labels); + ilGenerator.EmitBr(block.GetLabel(instr.DefaultTarget)); + break; + } + case NormalizedByteCode.__lookupswitch: + for (int j = 0; j < instr.SwitchEntryCount; j++) + { + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.EmitLdc_I4(instr.GetSwitchValue(j)); + CodeEmitterLabel label = ilGenerator.DefineLabel(); + ilGenerator.EmitBne_Un(label); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.EmitBr(block.GetLabel(instr.GetSwitchTargetIndex(j))); + ilGenerator.MarkLabel(label); + } + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.EmitBr(block.GetLabel(instr.DefaultTarget)); + break; + case NormalizedByteCode.__iinc: + LoadLocal(i); + ilGenerator.EmitLdc_I4(instr.Arg2); + ilGenerator.Emit(OpCodes.Add); + StoreLocal(i); + break; + case NormalizedByteCode.__i2b: + ilGenerator.Emit(OpCodes.Conv_I1); + break; + case NormalizedByteCode.__i2c: + ilGenerator.Emit(OpCodes.Conv_U2); + break; + case NormalizedByteCode.__i2s: + ilGenerator.Emit(OpCodes.Conv_I2); + break; + case NormalizedByteCode.__l2i: + ilGenerator.Emit(OpCodes.Conv_I4); + break; + case NormalizedByteCode.__f2i: + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2i); + break; + case NormalizedByteCode.__d2i: + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2i); + break; + case NormalizedByteCode.__f2l: + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2l); + break; + case NormalizedByteCode.__d2l: + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2l); + break; + case NormalizedByteCode.__i2l: + ilGenerator.Emit(OpCodes.Conv_I8); + break; + case NormalizedByteCode.__i2f: + case NormalizedByteCode.__l2f: + case NormalizedByteCode.__d2f: + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__i2d: + case NormalizedByteCode.__l2d: + case NormalizedByteCode.__f2d: + ilGenerator.Emit(OpCodes.Conv_R8); + break; + case NormalizedByteCode.__nop: + ilGenerator.Emit(OpCodes.Nop); + break; + case NormalizedByteCode.__intrinsic_gettype: + ilGenerator.Emit(OpCodes.Callvirt, getTypeMethod); + break; + case NormalizedByteCode.__static_error: + { + bool wrapIncompatibleClassChangeError = false; + TypeWrapper exceptionType; + switch (instr.HardError) + { + case HardError.AbstractMethodError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.AbstractMethodError"); + break; + case HardError.IllegalAccessError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessError"); + break; + case HardError.IncompatibleClassChangeError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IncompatibleClassChangeError"); + break; + case HardError.InstantiationError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.InstantiationError"); + break; + case HardError.LinkageError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.LinkageError"); + break; + case HardError.NoClassDefFoundError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoClassDefFoundError"); + break; + case HardError.NoSuchFieldError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchFieldError"); + break; + case HardError.NoSuchMethodError: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchMethodError"); + break; + case HardError.IllegalAccessException: + exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessException"); + wrapIncompatibleClassChangeError = true; + break; + default: + throw new InvalidOperationException(); + } + if (wrapIncompatibleClassChangeError) + { + ClassLoaderWrapper.LoadClassCritical("java.lang.IncompatibleClassChangeError").GetMethodWrapper("", "()V", false).EmitNewobj(ilGenerator); + } + string message = harderrors[instr.HardErrorMessageId]; + Tracer.Error(Tracer.Compiler, "{0}: {1}\n\tat {2}.{3}{4}", exceptionType.Name, message, classFile.Name, m.Name, m.Signature); + ilGenerator.Emit(OpCodes.Ldstr, message); + MethodWrapper method = exceptionType.GetMethodWrapper("", "(Ljava.lang.String;)V", false); + method.Link(); + method.EmitNewobj(ilGenerator); + if (wrapIncompatibleClassChangeError) + { + CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("initCause", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false).EmitCallvirt(ilGenerator); + } + ilGenerator.Emit(OpCodes.Throw); + break; + } + default: + throw new NotImplementedException(instr.NormalizedOpCode.ToString()); + } + // mark next instruction as inuse + switch (ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode)) + { + case ByteCodeFlowControl.Switch: + case ByteCodeFlowControl.Branch: + case ByteCodeFlowControl.Return: + case ByteCodeFlowControl.Throw: + instructionIsForwardReachable = false; + break; + case ByteCodeFlowControl.CondBranch: + case ByteCodeFlowControl.Next: + instructionIsForwardReachable = true; + Debug.Assert((flags[i + 1] & InstructionFlags.Reachable) != 0); + // don't fall through end of try block + if (block.EndIndex == i + 1) + { + // TODO instead of emitting a branch to the leave stub, it would be more efficient to put the leave stub here + ilGenerator.EmitBr(block.GetLabel(i + 1)); + } + break; + default: + throw new InvalidOperationException(); + } + } + } + + private void EmitReturnTypeConversion(TypeWrapper returnType) + { + returnType.EmitConvSignatureTypeToStackType(ilGenerator); + if (!strictfp) + { + // no need to convert + } + else if (returnType == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + else if (returnType == PrimitiveTypeWrapper.FLOAT) + { + ilGenerator.Emit(OpCodes.Conv_R4); + } + } + + private void EmitLoadConstant(CodeEmitter ilgen, int constant) + { + switch (classFile.GetConstantPoolConstantType(constant)) + { + case ClassFile.ConstantType.Double: + ilgen.EmitLdc_R8(classFile.GetConstantPoolConstantDouble(constant)); + break; + case ClassFile.ConstantType.Float: + ilgen.EmitLdc_R4(classFile.GetConstantPoolConstantFloat(constant)); + break; + case ClassFile.ConstantType.Integer: + ilgen.EmitLdc_I4(classFile.GetConstantPoolConstantInteger(constant)); + break; + case ClassFile.ConstantType.Long: + ilgen.EmitLdc_I8(classFile.GetConstantPoolConstantLong(constant)); + break; + case ClassFile.ConstantType.String: + ilgen.Emit(OpCodes.Ldstr, classFile.GetConstantPoolConstantString(constant)); + break; + case ClassFile.ConstantType.Class: + EmitLoadClass(ilgen, classFile.GetConstantPoolClassType(constant)); + break; + case ClassFile.ConstantType.MethodHandle: + context.GetValue(constant).Emit(this, ilgen, constant); + break; + case ClassFile.ConstantType.MethodType: + context.GetValue(constant).Emit(this, ilgen, constant); + break; +#if !IMPORTER + case ClassFile.ConstantType.LiveObject: + context.EmitLiveObjectLoad(ilgen, classFile.GetConstantPoolConstantLiveObject(constant)); + break; +#endif + default: + throw new InvalidOperationException(); + } + } + + private void EmitDynamicCast(TypeWrapper tw) + { + Debug.Assert(tw.IsUnloadable); + Profiler.Count("EmitDynamicCast"); + // NOTE it's important that we don't try to load the class if obj == null + CodeEmitterLabel ok = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.EmitBrfalse(ok); + EmitDynamicClassLiteral(tw); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicCast); + ilGenerator.MarkLabel(ok); + } + + private void EmitDynamicInstanceOf(TypeWrapper tw) + { + // NOTE it's important that we don't try to load the class if obj == null + CodeEmitterLabel notnull = ilGenerator.DefineLabel(); + CodeEmitterLabel end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.EmitBrtrue(notnull); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.EmitLdc_I4(0); + ilGenerator.EmitBr(end); + ilGenerator.MarkLabel(notnull); + EmitDynamicClassLiteral(tw); + ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicInstanceOf); + ilGenerator.MarkLabel(end); + } + + private void EmitDynamicClassLiteral(TypeWrapper tw) + { + context.EmitDynamicClassLiteral(ilGenerator, tw, m.IsLambdaFormCompiled); + } + + private void EmitLoadClass(CodeEmitter ilgen, TypeWrapper tw) + { + if (tw.IsUnloadable) + { + Profiler.Count("EmitDynamicClassLiteral"); + context.EmitDynamicClassLiteral(ilgen, tw, m.IsLambdaFormCompiled); + } + else + { + tw.EmitClassLiteral(ilgen); + } + } + + internal static bool HasUnloadable(TypeWrapper[] args, TypeWrapper ret) + { + TypeWrapper tw = ret; + for (int i = 0; !tw.IsUnloadable && i < args.Length; i++) + { + tw = args[i]; + } + return tw.IsUnloadable; + } + + private static class InvokeDynamicBuilder + { + private static readonly Type typeofOpenIndyCallSite; + private static readonly Type typeofCallSite; + private static readonly MethodWrapper methodLookup; + + static InvokeDynamicBuilder() + { +#if IMPORTER + typeofOpenIndyCallSite = StaticCompiler.GetRuntimeType("IKVM.Runtime.IndyCallSite`1"); + typeofCallSite = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.CallSite").TypeAsSignatureType; +#elif !FIRST_PASS + typeofOpenIndyCallSite = typeof(IKVM.Runtime.IndyCallSite<>); + typeofCallSite = typeof(java.lang.invoke.CallSite); +#endif + methodLookup = ClassLoaderWrapper.LoadClassCritical("java.lang.invoke.MethodHandles").GetMethodWrapper("lookup", "()Ljava.lang.invoke.MethodHandles$Lookup;", false); + methodLookup.Link(); + } + + internal static void Emit(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, Type delegateType) + { + Type typeofIndyCallSite = typeofOpenIndyCallSite.MakeGenericType(delegateType); + MethodInfo methodCreateBootStrap; + MethodInfo methodGetTarget; + if (ReflectUtil.ContainsTypeBuilder(typeofIndyCallSite)) + { + methodCreateBootStrap = TypeBuilder.GetMethod(typeofIndyCallSite, typeofOpenIndyCallSite.GetMethod("CreateBootstrap")); + methodGetTarget = TypeBuilder.GetMethod(typeofIndyCallSite, typeofOpenIndyCallSite.GetMethod("GetTarget")); + } + else + { + methodCreateBootStrap = typeofIndyCallSite.GetMethod("CreateBootstrap"); + methodGetTarget = typeofIndyCallSite.GetMethod("GetTarget"); + } + TypeBuilder tb = compiler.context.DefineIndyCallSiteType(); + FieldBuilder fb = tb.DefineField("value", typeofIndyCallSite, FieldAttributes.Static | FieldAttributes.Assembly); + CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb, compiler.clazz.GetClassLoader())); + ilgen.Emit(OpCodes.Ldnull); + ilgen.Emit(OpCodes.Ldftn, CreateBootstrapStub(compiler, cpi, delegateType, tb, fb, methodGetTarget)); + ilgen.Emit(OpCodes.Newobj, MethodHandleUtil.GetDelegateConstructor(delegateType)); + ilgen.Emit(OpCodes.Call, methodCreateBootStrap); + ilgen.Emit(OpCodes.Stsfld, fb); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + + compiler.ilGenerator.Emit(OpCodes.Ldsfld, fb); + compiler.ilGenerator.Emit(OpCodes.Call, methodGetTarget); + } + + private static MethodBuilder CreateBootstrapStub(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, Type delegateType, TypeBuilder tb, FieldBuilder fb, MethodInfo methodGetTarget) + { + Type[] args = Type.EmptyTypes; + if (delegateType.IsGenericType) + { + // MONOBUG we don't look at the invoke method directly here, because Mono doesn't support GetParameters() on a builder instantiation + args = delegateType.GetGenericArguments(); + if (cpi.GetRetType() != PrimitiveTypeWrapper.VOID) + { + Array.Resize(ref args, args.Length - 1); + } + } + MethodBuilder mb = tb.DefineMethod("BootstrapStub", MethodAttributes.Static | MethodAttributes.PrivateScope, cpi.GetRetType().TypeAsSignatureType, args); + CodeEmitter ilgen = CodeEmitter.Create(mb); + CodeEmitterLocal cs = ilgen.DeclareLocal(typeofCallSite); + CodeEmitterLocal ex = ilgen.DeclareLocal(Types.Exception); + CodeEmitterLocal ok = ilgen.DeclareLocal(Types.Boolean); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.BeginExceptionBlock(); + if (EmitCallBootstrapMethod(compiler, cpi, ilgen, ok)) + { + ilgen.Emit(OpCodes.Isinst, typeofCallSite); + ilgen.Emit(OpCodes.Stloc, cs); + } + ilgen.EmitLeave(label); + ilgen.BeginCatchBlock(Types.Exception); + ilgen.Emit(OpCodes.Stloc, ex); + ilgen.Emit(OpCodes.Ldloc, ok); + CodeEmitterLabel label2 = ilgen.DefineLabel(); + ilgen.EmitBrtrue(label2); + ilgen.Emit(OpCodes.Rethrow); + ilgen.MarkLabel(label2); + ilgen.EmitLeave(label); + ilgen.EndExceptionBlock(); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Ldsflda, fb); + ilgen.Emit(OpCodes.Ldloc, cs); + ilgen.Emit(OpCodes.Ldloc, ex); + if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) + { + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormHidden); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLinkIndyCallSite.MakeGenericMethod(delegateType)); + } + else + { + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LinkIndyCallSite.MakeGenericMethod(delegateType)); + } + ilgen.Emit(OpCodes.Ldsfld, fb); + ilgen.Emit(OpCodes.Call, methodGetTarget); + for (int i = 0; i < args.Length; i++) + { + ilgen.EmitLdarg(i); + } + ilgen.Emit(OpCodes.Callvirt, MethodHandleUtil.GetDelegateInvokeMethod(delegateType)); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return mb; + } + + private static bool EmitCallBootstrapMethod(Compiler compiler, ClassFile.ConstantPoolItemInvokeDynamic cpi, CodeEmitter ilgen, CodeEmitterLocal ok) + { + ClassFile.BootstrapMethod bsm = compiler.classFile.GetBootstrapMethod(cpi.BootstrapMethod); + if (3 + bsm.ArgumentCount > 255) + { + ilgen.EmitThrow("java.lang.BootstrapMethodError", "too many bootstrap method arguments"); + return false; + } + ClassFile.ConstantPoolItemMethodHandle mh = compiler.classFile.GetConstantPoolConstantMethodHandle(bsm.BootstrapMethodIndex); + MethodWrapper mw = mh.Member as MethodWrapper; + switch (mh.Kind) + { + case ReferenceKind.InvokeStatic: + if (mw != null && !mw.IsStatic) + goto default; + break; + case ReferenceKind.NewInvokeSpecial: + if (mw != null && !mw.IsConstructor) + goto default; + break; + default: + // to throw the right exception, we have to resolve the MH constant here + compiler.context.GetValue(bsm.BootstrapMethodIndex).Emit(compiler, ilgen, bsm.BootstrapMethodIndex); + ilgen.Emit(OpCodes.Pop); + ilgen.EmitLdc_I4(1); + ilgen.Emit(OpCodes.Stloc, ok); + ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); + return false; + } + if (mw == null) + { + // to throw the right exception (i.e. without wrapping it in a BootstrapMethodError), we have to resolve the MH constant here + compiler.context.GetValue(bsm.BootstrapMethodIndex).Emit(compiler, ilgen, bsm.BootstrapMethodIndex); + ilgen.Emit(OpCodes.Pop); + ClassFile.ConstantPoolItemMI cpiMI; + if ((cpiMI = mh.MemberConstantPoolItem as ClassFile.ConstantPoolItemMI) != null) + { + mw = new DynamicBinder().Get(compiler, mh.Kind, cpiMI, false); + } + else + { + ilgen.EmitLdc_I4(1); + ilgen.Emit(OpCodes.Stloc, ok); + ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); + return false; + } + } + TypeWrapper[] parameters = mw.GetParameters(); + int extraArgs = parameters.Length - 3; + int fixedArgs; + int varArgs; + if (extraArgs == 1 && parameters[3].IsArray && parameters[3].ElementTypeWrapper == CoreClasses.java.lang.Object.Wrapper) + { + fixedArgs = 0; + varArgs = bsm.ArgumentCount - fixedArgs; + } + else if (extraArgs != bsm.ArgumentCount) + { + ilgen.EmitLdc_I4(1); + ilgen.Emit(OpCodes.Stloc, ok); + ilgen.EmitThrow("java.lang.invoke.WrongMethodTypeException"); + return false; + } + else + { + fixedArgs = extraArgs; + varArgs = -1; + } + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + methodLookup.EmitCall(ilgen); + ilgen.Emit(OpCodes.Ldstr, cpi.Name); + parameters[1].EmitConvStackTypeToSignatureType(ilgen, CoreClasses.java.lang.String.Wrapper); + if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) + { + // the cache is useless since we only run once, so we use a local + ilgen.Emit(OpCodes.Ldloca, ilgen.DeclareLocal(CoreClasses.java.lang.invoke.MethodType.Wrapper.TypeAsSignatureType)); + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); + } + else + { + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(MethodHandleUtil.CreateDelegateTypeForLoadConstant(cpi.GetArgTypes(), cpi.GetRetType()))); + } + parameters[2].EmitConvStackTypeToSignatureType(ilgen, CoreClasses.java.lang.invoke.MethodType.Wrapper); + for (int i = 0; i < fixedArgs; i++) + { + EmitExtraArg(compiler, ilgen, bsm, i, parameters[i + 3], ok); + } + if (varArgs >= 0) + { + ilgen.EmitLdc_I4(varArgs); + TypeWrapper elemType = parameters[parameters.Length - 1].ElementTypeWrapper; + ilgen.Emit(OpCodes.Newarr, elemType.TypeAsArrayType); + for (int i = 0; i < varArgs; i++) + { + ilgen.Emit(OpCodes.Dup); + ilgen.EmitLdc_I4(i); + EmitExtraArg(compiler, ilgen, bsm, i + fixedArgs, elemType, ok); + ilgen.Emit(OpCodes.Stelem_Ref); + } + } + ilgen.EmitLdc_I4(1); + ilgen.Emit(OpCodes.Stloc, ok); + if (mw.IsConstructor) + { + mw.EmitNewobj(ilgen); + } + else + { + mw.EmitCall(ilgen); + } + return true; + } + + private static void EmitExtraArg(Compiler compiler, CodeEmitter ilgen, ClassFile.BootstrapMethod bsm, int index, TypeWrapper targetType, CodeEmitterLocal wrapException) + { + int constant = bsm.GetArgument(index); + compiler.EmitLoadConstant(ilgen, constant); + TypeWrapper constType; + switch (compiler.classFile.GetConstantPoolConstantType(constant)) + { + case ClassFile.ConstantType.Integer: + constType = PrimitiveTypeWrapper.INT; + break; + case ClassFile.ConstantType.Long: + constType = PrimitiveTypeWrapper.LONG; + break; + case ClassFile.ConstantType.Float: + constType = PrimitiveTypeWrapper.FLOAT; + break; + case ClassFile.ConstantType.Double: + constType = PrimitiveTypeWrapper.DOUBLE; + break; + case ClassFile.ConstantType.Class: + constType = CoreClasses.java.lang.Class.Wrapper; + break; + case ClassFile.ConstantType.String: + constType = CoreClasses.java.lang.String.Wrapper; + break; + case ClassFile.ConstantType.MethodHandle: + constType = CoreClasses.java.lang.invoke.MethodHandle.Wrapper; + break; + case ClassFile.ConstantType.MethodType: + constType = CoreClasses.java.lang.invoke.MethodType.Wrapper; + break; + default: + throw new InvalidOperationException(); + } + if (constType != targetType) + { + ilgen.EmitLdc_I4(1); + ilgen.Emit(OpCodes.Stloc, wrapException); + if (constType.IsPrimitive) + { + string dummy; + TypeWrapper wrapper = GetWrapperType(constType, out dummy); + wrapper.GetMethodWrapper("valueOf", "(" + constType.SigName + ")" + wrapper.SigName, false).EmitCall(ilgen); + } + if (targetType.IsUnloadable) + { + // do nothing + } + else if (targetType.IsPrimitive) + { + string unbox; + TypeWrapper wrapper = GetWrapperType(targetType, out unbox); + ilgen.Emit(OpCodes.Castclass, wrapper.TypeAsBaseType); + wrapper.GetMethodWrapper(unbox, "()" + targetType.SigName, false).EmitCallvirt(ilgen); + } + else if (!constType.IsAssignableTo(targetType)) + { + ilgen.Emit(OpCodes.Castclass, targetType.TypeAsBaseType); + } + targetType.EmitConvStackTypeToSignatureType(ilgen, targetType); + ilgen.EmitLdc_I4(0); + ilgen.Emit(OpCodes.Stloc, wrapException); + } + } + + private static TypeWrapper GetWrapperType(TypeWrapper tw, out string unbox) + { + if (tw == PrimitiveTypeWrapper.INT) + { + unbox = "intValue"; + return ClassLoaderWrapper.LoadClassCritical("java.lang.Integer"); + } + else if (tw == PrimitiveTypeWrapper.LONG) + { + unbox = "longValue"; + return ClassLoaderWrapper.LoadClassCritical("java.lang.Long"); + } + else if (tw == PrimitiveTypeWrapper.FLOAT) + { + unbox = "floatValue"; + return ClassLoaderWrapper.LoadClassCritical("java.lang.Float"); + } + else if (tw == PrimitiveTypeWrapper.DOUBLE) + { + unbox = "doubleValue"; + return ClassLoaderWrapper.LoadClassCritical("java.lang.Double"); + } + else + { + throw new InvalidOperationException(); + } + } + } + + private void EmitInvokeDynamic(ClassFile.ConstantPoolItemInvokeDynamic cpi) + { + CodeEmitter ilgen = ilGenerator; + TypeWrapper[] args = cpi.GetArgTypes(); + CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; + for (int i = args.Length - 1; i >= 0; i--) + { + temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, temps[i]); + } + Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); + InvokeDynamicBuilder.Emit(this, cpi, delegateType); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, temps[i]); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + } + + private sealed class MethodHandleConstant + { + private FieldBuilder field; + + internal void Emit(Compiler compiler, CodeEmitter ilgen, int index) + { + if (field == null) + { + field = compiler.context.DefineDynamicMethodHandleCacheField(); + } + ClassFile.ConstantPoolItemMethodHandle mh = compiler.classFile.GetConstantPoolConstantMethodHandle(index); + ilgen.Emit(OpCodes.Ldsflda, field); + ilgen.EmitLdc_I4((int)mh.Kind); + ilgen.Emit(OpCodes.Ldstr, mh.Class); + ilgen.Emit(OpCodes.Ldstr, mh.Name); + ilgen.Emit(OpCodes.Ldstr, mh.Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodHandle); + } + } + + private sealed class MethodTypeConstant + { + private FieldBuilder field; + private bool dynamic; + + internal void Emit(Compiler compiler, CodeEmitter ilgen, int index) + { + if (field == null) + { + field = CreateField(compiler, index, ref dynamic); + } + if (dynamic) + { + ilgen.Emit(OpCodes.Ldsflda, field); + ilgen.Emit(OpCodes.Ldstr, compiler.classFile.GetConstantPoolConstantMethodType(index).Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); + } + else + { + ilgen.Emit(OpCodes.Ldsfld, field); + } + } + + private static FieldBuilder CreateField(Compiler compiler, int index, ref bool dynamic) + { + ClassFile.ConstantPoolItemMethodType cpi = compiler.classFile.GetConstantPoolConstantMethodType(index); + TypeWrapper[] args = cpi.GetArgTypes(); + TypeWrapper ret = cpi.GetRetType(); + + if (HasUnloadable(args, ret)) + { + dynamic = true; + return compiler.context.DefineDynamicMethodTypeCacheField(); + } + else + { + TypeBuilder tb = compiler.context.DefineMethodTypeConstantType(index); + FieldBuilder field = tb.DefineField("value", CoreClasses.java.lang.invoke.MethodType.Wrapper.TypeAsSignatureType, FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb, compiler.clazz.GetClassLoader())); + Type delegateType = MethodHandleUtil.CreateDelegateTypeForLoadConstant(args, ret); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); + ilgen.Emit(OpCodes.Stsfld, field); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return field; + } + } + } + + private bool RequiresExplicitClassInit(TypeWrapper tw, int index, InstructionFlags[] flags) + { + ClassFile.Method.Instruction[] code = m.Instructions; + for (; index < code.Length; index++) + { + if (code[index].NormalizedOpCode == NormalizedByteCode.__invokespecial) + { + ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(code[index].Arg1); + MethodWrapper mw = cpi.GetMethodForInvokespecial(); + return !mw.IsConstructor || mw.DeclaringType != tw; + } + if ((flags[index] & InstructionFlags.BranchTarget) != 0 + || ByteCodeMetaData.IsBranch(code[index].NormalizedOpCode) + || ByteCodeMetaData.CanThrowException(code[index].NormalizedOpCode)) + { + break; + } + } + return true; + } + + // NOTE despite its name this also handles value type args + private void CastInterfaceArgs(TypeWrapper declaringType, TypeWrapper[] args, int instructionIndex, bool instanceMethod) + { + bool needsCast = false; + int firstCastArg = -1; + + if (!needsCast) + { + for (int i = 0; i < args.Length; i++) + { + if (args[i].IsUnloadable) + { + // nothing to do, callee will (eventually) do the cast + } + else if (args[i].IsGhost) + { + needsCast = true; + firstCastArg = i; + break; + } + else if (args[i].IsInterfaceOrInterfaceArray) + { + TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i); + if (tw.IsUnloadable || NeedsInterfaceDownCast(tw, args[i])) + { + needsCast = true; + firstCastArg = i; + break; + } + } + else if (args[i].IsNonPrimitiveValueType) + { + if (i == 0 && instanceMethod && declaringType != args[i]) + { + // no cast needed because we're calling an inherited method + } + else + { + needsCast = true; + firstCastArg = i; + break; + } + } + // if the stack contains an unloadable, we might need to cast it + // (e.g. if the argument type is a base class that is loadable) + if (ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i).IsUnloadable) + { + needsCast = true; + firstCastArg = i; + break; + } + } + } + + if (needsCast) + { + DupHelper dh = new DupHelper(this, args.Length); + for (int i = firstCastArg + 1; i < args.Length; i++) + { + TypeWrapper tw = ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i); + if (tw != VerifierTypeWrapper.UninitializedThis + && !VerifierTypeWrapper.IsThis(tw)) + { + tw = args[i]; + } + dh.SetType(i, tw); + } + for (int i = args.Length - 1; i >= firstCastArg; i--) + { + if (!args[i].IsUnloadable && !args[i].IsGhost) + { + TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i); + if (tw.IsUnloadable || (args[i].IsInterfaceOrInterfaceArray && NeedsInterfaceDownCast(tw, args[i]))) + { + ilGenerator.EmitAssertType(args[i].TypeAsTBD); + Profiler.Count("InterfaceDownCast"); + } + } + if (i != firstCastArg) + { + dh.Store(i); + } + } + if (instanceMethod && args[0].IsUnloadable && !declaringType.IsUnloadable) + { + if (declaringType.IsInterface) + { + ilGenerator.EmitAssertType(declaringType.TypeAsTBD); + } + else if (declaringType.IsNonPrimitiveValueType) + { + ilGenerator.Emit(OpCodes.Unbox, declaringType.TypeAsTBD); + } + else + { + ilGenerator.Emit(OpCodes.Castclass, declaringType.TypeAsSignatureType); + } + } + for (int i = firstCastArg; i < args.Length; i++) + { + if (i != firstCastArg) + { + dh.Load(i); + } + if (!args[i].IsUnloadable && args[i].IsGhost) + { + if (i == 0 && instanceMethod && !declaringType.IsInterface) + { + // we're calling a java.lang.Object method through a ghost interface reference, + // no ghost handling is needed + } + else if (VerifierTypeWrapper.IsThis(ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i))) + { + // we're an instance method in a ghost interface, so the this pointer is a managed pointer to the + // wrapper value and if we're not calling another instance method on ourself, we need to load + // the wrapper value onto the stack + if (!instanceMethod || i != 0) + { + ilGenerator.Emit(OpCodes.Ldobj, args[i].TypeAsSignatureType); + } + } + else + { + CodeEmitterLocal ghost = ilGenerator.AllocTempLocal(Types.Object); + ilGenerator.Emit(OpCodes.Stloc, ghost); + CodeEmitterLocal local = ilGenerator.AllocTempLocal(args[i].TypeAsSignatureType); + ilGenerator.Emit(OpCodes.Ldloca, local); + ilGenerator.Emit(OpCodes.Ldloc, ghost); + ilGenerator.Emit(OpCodes.Stfld, args[i].GhostRefField); + ilGenerator.Emit(OpCodes.Ldloca, local); + ilGenerator.ReleaseTempLocal(local); + ilGenerator.ReleaseTempLocal(ghost); + // NOTE when the this argument is a value type, we need the address on the stack instead of the value + if (i != 0 || !instanceMethod) + { + ilGenerator.Emit(OpCodes.Ldobj, args[i].TypeAsSignatureType); + } + } + } + else + { + if (!args[i].IsUnloadable) + { + if (args[i].IsNonPrimitiveValueType) + { + if (i == 0 && instanceMethod) + { + // we only need to unbox if the method was actually declared on the value type + if (declaringType == args[i]) + { + ilGenerator.Emit(OpCodes.Unbox, args[i].TypeAsTBD); + } + } + else + { + args[i].EmitUnbox(ilGenerator); + } + } + else if (ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i).IsUnloadable) + { + ilGenerator.Emit(OpCodes.Castclass, args[i].TypeAsSignatureType); + } + } + } + } + dh.Release(); + } + } + + private bool NeedsInterfaceDownCast(TypeWrapper tw, TypeWrapper arg) + { + if (tw == VerifierTypeWrapper.Null) + { + return false; + } + if (!tw.IsAccessibleFrom(clazz)) + { + tw = tw.GetPublicBaseTypeWrapper(); + } + return !tw.IsAssignableTo(arg); + } + + private void DynamicGetPutField(Instruction instr, int i) + { + ReferenceKind kind; + switch (instr.NormalizedOpCode) + { + case NormalizedByteCode.__dynamic_getfield: + Profiler.Count("EmitDynamicGetfield"); + kind = ReferenceKind.GetField; + break; + case NormalizedByteCode.__dynamic_putfield: + Profiler.Count("EmitDynamicPutfield"); + kind = ReferenceKind.PutField; + break; + case NormalizedByteCode.__dynamic_getstatic: + Profiler.Count("EmitDynamicGetstatic"); + kind = ReferenceKind.GetStatic; + break; + case NormalizedByteCode.__dynamic_putstatic: + Profiler.Count("EmitDynamicPutstatic"); + kind = ReferenceKind.PutStatic; + break; + default: + throw new InvalidOperationException(); + } + ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1); + TypeWrapper fieldType = cpi.GetFieldType(); + if (kind == ReferenceKind.PutField || kind == ReferenceKind.PutStatic) + { + fieldType.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0)); + if (strictfp) + { + // no need to convert + } + else if (fieldType == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + } + context.GetValue(instr.Arg1 | ((byte)kind << 24)).Emit(this, cpi, kind); + if (kind == ReferenceKind.GetField || kind == ReferenceKind.GetStatic) + { + fieldType.EmitConvSignatureTypeToStackType(ilGenerator); + } + } + + private static void EmitReturnTypeConversion(CodeEmitter ilgen, TypeWrapper typeWrapper) + { + if (typeWrapper.IsUnloadable) + { + // nothing to do for unloadables + } + else if (typeWrapper == PrimitiveTypeWrapper.VOID) + { + ilgen.Emit(OpCodes.Pop); + } + else if (typeWrapper.IsPrimitive) + { + // NOTE we don't need to use TypeWrapper.EmitUnbox, because the return value cannot be null + ilgen.Emit(OpCodes.Unbox, typeWrapper.TypeAsTBD); + ilgen.Emit(OpCodes.Ldobj, typeWrapper.TypeAsTBD); + if (typeWrapper == PrimitiveTypeWrapper.BYTE) + { + ilgen.Emit(OpCodes.Conv_I1); + } + } + else + { + typeWrapper.EmitCheckcast(ilgen); + } + } + + internal sealed class MethodHandleMethodWrapper : MethodWrapper + { + + private readonly Compiler compiler; + private readonly TypeWrapper wrapper; + private readonly ClassFile.ConstantPoolItemMI cpi; + + internal MethodHandleMethodWrapper(Compiler compiler, TypeWrapper wrapper, ClassFile.ConstantPoolItemMI cpi) + : base(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), Modifiers.Public, MemberFlags.None) + { + this.compiler = compiler; + this.wrapper = wrapper; + this.cpi = cpi; + } + + private static void ToBasic(TypeWrapper tw, CodeEmitter ilgen) + { + if (tw.IsNonPrimitiveValueType) + { + tw.EmitBox(ilgen); + } + else if (tw.IsGhost) + { + tw.EmitConvSignatureTypeToStackType(ilgen); + } + } + + private static void FromBasic(TypeWrapper tw, CodeEmitter ilgen) + { + if (tw.IsNonPrimitiveValueType) + { + tw.EmitUnbox(ilgen); + } + else if (tw.IsGhost) + { + tw.EmitConvStackTypeToSignatureType(ilgen, null); + } + else if (!tw.IsPrimitive && tw != CoreClasses.java.lang.Object.Wrapper) + { + tw.EmitCheckcast(ilgen); + } + } + + internal override void EmitCall(CodeEmitter ilgen) + { + Debug.Assert(cpi.Name == "linkToVirtual" || cpi.Name == "linkToStatic" || cpi.Name == "linkToSpecial" || cpi.Name == "linkToInterface"); + EmitLinkToCall(ilgen, cpi.GetArgTypes(), cpi.GetRetType()); + } + + internal static void EmitLinkToCall(CodeEmitter ilgen, TypeWrapper[] args, TypeWrapper retType) + { +#if !FIRST_PASS && !IMPORTER CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; for (int i = args.Length - 1; i > 0; i--) { @@ -3592,316 +3483,316 @@ internal static void EmitLinkToCall(CodeEmitter ilgen, TypeWrapper[] args, TypeW MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); FromBasic(retType, ilgen); #else - throw new InvalidOperationException(); + throw new InvalidOperationException(); #endif - } - - private void EmitInvokeExact(CodeEmitter ilgen) - { - TypeWrapper[] args = cpi.GetArgTypes(); - CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; - for (int i = args.Length - 1; i >= 0; i--) - { - temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temps[i]); - } - Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); - if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) - { - // TODO consider sharing the cache for the same signatures - ilgen.Emit(OpCodes.Ldsflda, compiler.context.DefineDynamicMethodTypeCacheField()); - ilgen.Emit(OpCodes.Ldstr, cpi.Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicEraseInvokeExact); - } - MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeExact.MakeGenericMethod(delegateType); - ilgen.Emit(OpCodes.Call, mi); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, temps[i]); - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - } - - private void EmitInvokeMaxArity(CodeEmitter ilgen) - { - TypeWrapper[] args = cpi.GetArgTypes(); - CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; - for (int i = args.Length - 1; i >= 0; i--) - { - temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temps[i]); - } - Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); - CoreClasses.java.lang.invoke.MethodHandle.Wrapper.GetMethodWrapper("asType", "(Ljava.lang.invoke.MethodType;)Ljava.lang.invoke.MethodHandle;", false).EmitCallvirt(ilgen); - MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeExact.MakeGenericMethod(delegateType); - ilgen.Emit(OpCodes.Call, mi); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, temps[i]); - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - } - - private void EmitInvoke(CodeEmitter ilgen) - { - if (cpi.GetArgTypes().Length >= 127 && MethodHandleUtil.SlotCount(cpi.GetArgTypes()) >= 254) - { - EmitInvokeMaxArity(ilgen); - return; - } - TypeWrapper[] args = ArrayUtil.Concat(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, cpi.GetArgTypes()); - CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; - for (int i = args.Length - 1; i >= 0; i--) - { - temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temps[i]); - } - Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); - MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvoke.MakeGenericMethod(delegateType); - Type typeofInvokeCache; -#if STATIC_COMPILER + } + + private void EmitInvokeExact(CodeEmitter ilgen) + { + TypeWrapper[] args = cpi.GetArgTypes(); + CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; + for (int i = args.Length - 1; i >= 0; i--) + { + temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, temps[i]); + } + Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); + if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) + { + // TODO consider sharing the cache for the same signatures + ilgen.Emit(OpCodes.Ldsflda, compiler.context.DefineDynamicMethodTypeCacheField()); + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicEraseInvokeExact); + } + MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeExact.MakeGenericMethod(delegateType); + ilgen.Emit(OpCodes.Call, mi); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, temps[i]); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + } + + private void EmitInvokeMaxArity(CodeEmitter ilgen) + { + TypeWrapper[] args = cpi.GetArgTypes(); + CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; + for (int i = args.Length - 1; i >= 0; i--) + { + temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, temps[i]); + } + Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.LoadMethodType.MakeGenericMethod(delegateType)); + CoreClasses.java.lang.invoke.MethodHandle.Wrapper.GetMethodWrapper("asType", "(Ljava.lang.invoke.MethodType;)Ljava.lang.invoke.MethodHandle;", false).EmitCallvirt(ilgen); + MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeExact.MakeGenericMethod(delegateType); + ilgen.Emit(OpCodes.Call, mi); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, temps[i]); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + } + + private void EmitInvoke(CodeEmitter ilgen) + { + if (cpi.GetArgTypes().Length >= 127 && MethodHandleUtil.SlotCount(cpi.GetArgTypes()) >= 254) + { + EmitInvokeMaxArity(ilgen); + return; + } + TypeWrapper[] args = ArrayUtil.Concat(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, cpi.GetArgTypes()); + CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; + for (int i = args.Length - 1; i >= 0; i--) + { + temps[i] = ilgen.DeclareLocal(args[i].TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, temps[i]); + } + Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, cpi.GetRetType()); + MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvoke.MakeGenericMethod(delegateType); + Type typeofInvokeCache; +#if IMPORTER typeofInvokeCache = StaticCompiler.GetRuntimeType("IKVM.Runtime.InvokeCache`1"); #else - typeofInvokeCache = typeof(IKVM.Runtime.InvokeCache<>); + typeofInvokeCache = typeof(IKVM.Runtime.InvokeCache<>); #endif - FieldBuilder fb = compiler.context.DefineMethodHandleInvokeCacheField(typeofInvokeCache.MakeGenericType(delegateType)); - ilgen.Emit(OpCodes.Ldloc, temps[0]); - if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) - { - // TODO consider sharing the cache for the same signatures - ilgen.Emit(OpCodes.Ldsflda, compiler.context.DefineDynamicMethodTypeCacheField()); - ilgen.Emit(OpCodes.Ldstr, cpi.Signature); - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); - } - else - { - ilgen.Emit(OpCodes.Ldnull); - } - ilgen.Emit(OpCodes.Ldsflda, fb); - ilgen.Emit(OpCodes.Call, mi); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, temps[i]); - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - } - - private void EmitInvokeBasic(CodeEmitter ilgen) - { - TypeWrapper retType = cpi.GetRetType(); - EmitInvokeBasic(ilgen, cpi.GetArgTypes(), retType, true); - FromBasic(retType, ilgen); - } - - internal static void EmitInvokeBasic(CodeEmitter ilgen, TypeWrapper[] args, TypeWrapper retType, bool toBasic) - { - args = ArrayUtil.Concat(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, args); - CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; - for (int i = args.Length - 1; i > 0; i--) - { - temps[i] = ilgen.DeclareLocal(MethodHandleUtil.AsBasicType(args[i])); - if (toBasic) - { - ToBasic(args[i], ilgen); - } - ilgen.Emit(OpCodes.Stloc, temps[i]); - } - temps[0] = ilgen.DeclareLocal(args[0].TypeAsSignatureType); - ilgen.Emit(OpCodes.Stloc, temps[0]); - Type delegateType = MethodHandleUtil.CreateMemberWrapperDelegateType(args, retType); - MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeBasic.MakeGenericMethod(delegateType); - ilgen.Emit(OpCodes.Ldloc, temps[0]); - ilgen.Emit(OpCodes.Call, mi); - for (int i = 0; i < args.Length; i++) - { - ilgen.Emit(OpCodes.Ldloc, temps[i]); - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - } - - internal override void EmitCallvirt(CodeEmitter ilgen) - { - switch (cpi.Name) - { - case "invokeExact": - EmitInvokeExact(ilgen); - break; - case "invoke": - EmitInvoke(ilgen); - break; - case "invokeBasic": - EmitInvokeBasic(ilgen); - break; - default: - throw new InvalidOperationException(); - } - } - - internal override void EmitNewobj(CodeEmitter ilgen) - { - throw new InvalidOperationException(); - } - } - - private sealed class DynamicFieldBinder - { - private MethodInfo method; - - internal void Emit(Compiler compiler, ClassFile.ConstantPoolItemFieldref cpi, ClassFile.RefKind kind) - { - if (method == null) - { - method = CreateMethod(compiler, cpi, kind); - } - compiler.ilGenerator.Emit(OpCodes.Call, method); - } - - private static MethodInfo CreateMethod(Compiler compiler, ClassFile.ConstantPoolItemFieldref cpi, ClassFile.RefKind kind) - { - TypeWrapper ret; - TypeWrapper[] args; - switch (kind) - { - case ClassFile.RefKind.getField: - ret = cpi.GetFieldType(); - args = new TypeWrapper[] { cpi.GetClassType() }; - break; - case ClassFile.RefKind.getStatic: - ret = cpi.GetFieldType(); - args = TypeWrapper.EmptyArray; - break; - case ClassFile.RefKind.putField: - ret = PrimitiveTypeWrapper.VOID; - args = new TypeWrapper[] { cpi.GetClassType(), cpi.GetFieldType() }; - break; - case ClassFile.RefKind.putStatic: - ret = PrimitiveTypeWrapper.VOID; - args = new TypeWrapper[] { cpi.GetFieldType() }; - break; - default: - throw new InvalidOperationException(); - } - return DynamicBinder.Emit(compiler, kind, cpi, ret, args, false); - } - } - - private sealed class DynamicBinder - { - private MethodWrapper mw; - - internal MethodWrapper Get(Compiler compiler, ClassFile.RefKind kind, ClassFile.ConstantPoolItemMI cpi, bool privileged) - { - return mw ?? (mw = new DynamicBinderMethodWrapper(cpi, Emit(compiler, kind, cpi, privileged), kind)); - } - - private static MethodInfo Emit(Compiler compiler, ClassFile.RefKind kind, ClassFile.ConstantPoolItemMI cpi, bool privileged) - { - TypeWrapper ret; - TypeWrapper[] args; - if (kind == ClassFile.RefKind.invokeStatic) - { - ret = cpi.GetRetType(); - args = cpi.GetArgTypes(); - } - else if (kind == ClassFile.RefKind.newInvokeSpecial) - { - ret = cpi.GetClassType(); - args = cpi.GetArgTypes(); - } - else - { - ret = cpi.GetRetType(); - args = ArrayUtil.Concat(cpi.GetClassType(), cpi.GetArgTypes()); - } - return Emit(compiler, kind, cpi, ret, args, privileged); - } - - internal static MethodInfo Emit(Compiler compiler, ClassFile.RefKind kind, ClassFile.ConstantPoolItemFMI cpi, TypeWrapper ret, TypeWrapper[] args, bool privileged) - { - bool ghostTarget = (kind == ClassFile.RefKind.invokeSpecial || kind == ClassFile.RefKind.invokeVirtual || kind == ClassFile.RefKind.invokeInterface) && args[0].IsGhost; - Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, ret); - FieldBuilder fb = compiler.context.DefineMethodHandleInvokeCacheField(delegateType); - Type[] types = new Type[args.Length]; - for (int i = 0; i < types.Length; i++) - { - types[i] = args[i].TypeAsSignatureType; - } - if (ghostTarget) - { - types[0] = types[0].MakeByRefType(); - } - MethodBuilder mb = compiler.context.DefineMethodHandleDispatchStub(ret.TypeAsSignatureType, types); - CodeEmitter ilgen = CodeEmitter.Create(mb); - ilgen.Emit(OpCodes.Ldsfld, fb); - CodeEmitterLabel label = ilgen.DefineLabel(); - ilgen.EmitBrtrue(label); - ilgen.EmitLdc_I4((int)kind); - ilgen.Emit(OpCodes.Ldstr, cpi.Class); - ilgen.Emit(OpCodes.Ldstr, cpi.Name); - ilgen.Emit(OpCodes.Ldstr, cpi.Signature); - if (privileged) - { - compiler.context.EmitHostCallerID(ilgen); - } - else - { - compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); - } - ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicBinderMemberLookup.MakeGenericMethod(delegateType)); - ilgen.Emit(OpCodes.Volatile); - ilgen.Emit(OpCodes.Stsfld, fb); - ilgen.MarkLabel(label); - ilgen.Emit(OpCodes.Ldsfld, fb); - for (int i = 0; i < args.Length; i++) - { - ilgen.EmitLdarg(i); - if (i == 0 && ghostTarget) - { - ilgen.Emit(OpCodes.Ldobj, args[0].TypeAsSignatureType); - } - } - MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); - ilgen.Emit(OpCodes.Ret); - ilgen.DoEmit(); - return mb; - } - - private sealed class DynamicBinderMethodWrapper : MethodWrapper - { - private readonly MethodInfo method; - - internal DynamicBinderMethodWrapper(ClassFile.ConstantPoolItemMI cpi, MethodInfo method, ClassFile.RefKind kind) - : base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), kind == ClassFile.RefKind.invokeStatic ? Modifiers.Public | Modifiers.Static : Modifiers.Public, MemberFlags.None) - { - this.method = method; - } - - internal override void EmitCall(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, method); - } - - internal override void EmitCallvirt(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, method); - } - - internal override void EmitNewobj(CodeEmitter ilgen) - { - ilgen.Emit(OpCodes.Call, method); - } - } - } - - private MethodWrapper GetMethodCallEmitter(NormalizedByteCode invoke, int constantPoolIndex) - { - ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(constantPoolIndex); -#if STATIC_COMPILER + FieldBuilder fb = compiler.context.DefineMethodHandleInvokeCacheField(typeofInvokeCache.MakeGenericType(delegateType)); + ilgen.Emit(OpCodes.Ldloc, temps[0]); + if (HasUnloadable(cpi.GetArgTypes(), cpi.GetRetType())) + { + // TODO consider sharing the cache for the same signatures + ilgen.Emit(OpCodes.Ldsflda, compiler.context.DefineDynamicMethodTypeCacheField()); + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicLoadMethodType); + } + else + { + ilgen.Emit(OpCodes.Ldnull); + } + ilgen.Emit(OpCodes.Ldsflda, fb); + ilgen.Emit(OpCodes.Call, mi); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, temps[i]); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + } + + private void EmitInvokeBasic(CodeEmitter ilgen) + { + TypeWrapper retType = cpi.GetRetType(); + EmitInvokeBasic(ilgen, cpi.GetArgTypes(), retType, true); + FromBasic(retType, ilgen); + } + + internal static void EmitInvokeBasic(CodeEmitter ilgen, TypeWrapper[] args, TypeWrapper retType, bool toBasic) + { + args = ArrayUtil.Concat(CoreClasses.java.lang.invoke.MethodHandle.Wrapper, args); + CodeEmitterLocal[] temps = new CodeEmitterLocal[args.Length]; + for (int i = args.Length - 1; i > 0; i--) + { + temps[i] = ilgen.DeclareLocal(MethodHandleUtil.AsBasicType(args[i])); + if (toBasic) + { + ToBasic(args[i], ilgen); + } + ilgen.Emit(OpCodes.Stloc, temps[i]); + } + temps[0] = ilgen.DeclareLocal(args[0].TypeAsSignatureType); + ilgen.Emit(OpCodes.Stloc, temps[0]); + Type delegateType = MethodHandleUtil.CreateMemberWrapperDelegateType(args, retType); + MethodInfo mi = ByteCodeHelperMethods.GetDelegateForInvokeBasic.MakeGenericMethod(delegateType); + ilgen.Emit(OpCodes.Ldloc, temps[0]); + ilgen.Emit(OpCodes.Call, mi); + for (int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldloc, temps[i]); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + } + + internal override void EmitCallvirt(CodeEmitter ilgen) + { + switch (cpi.Name) + { + case "invokeExact": + EmitInvokeExact(ilgen); + break; + case "invoke": + EmitInvoke(ilgen); + break; + case "invokeBasic": + EmitInvokeBasic(ilgen); + break; + default: + throw new InvalidOperationException(); + } + } + + internal override void EmitNewobj(CodeEmitter ilgen) + { + throw new InvalidOperationException(); + } + } + + private sealed class DynamicFieldBinder + { + private MethodInfo method; + + internal void Emit(Compiler compiler, ClassFile.ConstantPoolItemFieldref cpi, ReferenceKind kind) + { + if (method == null) + { + method = CreateMethod(compiler, cpi, kind); + } + compiler.ilGenerator.Emit(OpCodes.Call, method); + } + + private static MethodInfo CreateMethod(Compiler compiler, ClassFile.ConstantPoolItemFieldref cpi, ReferenceKind kind) + { + TypeWrapper ret; + TypeWrapper[] args; + switch (kind) + { + case ReferenceKind.GetField: + ret = cpi.GetFieldType(); + args = new TypeWrapper[] { cpi.GetClassType() }; + break; + case ReferenceKind.GetStatic: + ret = cpi.GetFieldType(); + args = TypeWrapper.EmptyArray; + break; + case ReferenceKind.PutField: + ret = PrimitiveTypeWrapper.VOID; + args = new TypeWrapper[] { cpi.GetClassType(), cpi.GetFieldType() }; + break; + case ReferenceKind.PutStatic: + ret = PrimitiveTypeWrapper.VOID; + args = new TypeWrapper[] { cpi.GetFieldType() }; + break; + default: + throw new InvalidOperationException(); + } + return DynamicBinder.Emit(compiler, kind, cpi, ret, args, false); + } + } + + private sealed class DynamicBinder + { + private MethodWrapper mw; + + internal MethodWrapper Get(Compiler compiler, ReferenceKind kind, ClassFile.ConstantPoolItemMI cpi, bool privileged) + { + return mw ?? (mw = new DynamicBinderMethodWrapper(cpi, Emit(compiler, kind, cpi, privileged), kind)); + } + + private static MethodInfo Emit(Compiler compiler, ReferenceKind kind, ClassFile.ConstantPoolItemMI cpi, bool privileged) + { + TypeWrapper ret; + TypeWrapper[] args; + if (kind == ReferenceKind.InvokeStatic) + { + ret = cpi.GetRetType(); + args = cpi.GetArgTypes(); + } + else if (kind == ReferenceKind.NewInvokeSpecial) + { + ret = cpi.GetClassType(); + args = cpi.GetArgTypes(); + } + else + { + ret = cpi.GetRetType(); + args = ArrayUtil.Concat(cpi.GetClassType(), cpi.GetArgTypes()); + } + return Emit(compiler, kind, cpi, ret, args, privileged); + } + + internal static MethodInfo Emit(Compiler compiler, ReferenceKind kind, ClassFile.ConstantPoolItemFMI cpi, TypeWrapper ret, TypeWrapper[] args, bool privileged) + { + bool ghostTarget = (kind == ReferenceKind.InvokeSpecial || kind == ReferenceKind.InvokeVirtual || kind == ReferenceKind.InvokeInterface) && args[0].IsGhost; + Type delegateType = MethodHandleUtil.CreateMethodHandleDelegateType(args, ret); + FieldBuilder fb = compiler.context.DefineMethodHandleInvokeCacheField(delegateType); + Type[] types = new Type[args.Length]; + for (int i = 0; i < types.Length; i++) + { + types[i] = args[i].TypeAsSignatureType; + } + if (ghostTarget) + { + types[0] = types[0].MakeByRefType(); + } + MethodBuilder mb = compiler.context.DefineMethodHandleDispatchStub(ret.TypeAsSignatureType, types); + CodeEmitter ilgen = CodeEmitter.Create(mb); + ilgen.Emit(OpCodes.Ldsfld, fb); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitBrtrue(label); + ilgen.EmitLdc_I4((int)kind); + ilgen.Emit(OpCodes.Ldstr, cpi.Class); + ilgen.Emit(OpCodes.Ldstr, cpi.Name); + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + if (privileged) + { + compiler.context.EmitHostCallerID(ilgen); + } + else + { + compiler.context.EmitCallerID(ilgen, compiler.m.IsLambdaFormCompiled); + } + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicBinderMemberLookup.MakeGenericMethod(delegateType)); + ilgen.Emit(OpCodes.Volatile); + ilgen.Emit(OpCodes.Stsfld, fb); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Ldsfld, fb); + for (int i = 0; i < args.Length; i++) + { + ilgen.EmitLdarg(i); + if (i == 0 && ghostTarget) + { + ilgen.Emit(OpCodes.Ldobj, args[0].TypeAsSignatureType); + } + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return mb; + } + + private sealed class DynamicBinderMethodWrapper : MethodWrapper + { + private readonly MethodInfo method; + + internal DynamicBinderMethodWrapper(ClassFile.ConstantPoolItemMI cpi, MethodInfo method, ReferenceKind kind) + : base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), kind == ReferenceKind.InvokeStatic ? Modifiers.Public | Modifiers.Static : Modifiers.Public, MemberFlags.None) + { + this.method = method; + } + + internal override void EmitCall(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, method); + } + + internal override void EmitCallvirt(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, method); + } + + internal override void EmitNewobj(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, method); + } + } + } + + private MethodWrapper GetMethodCallEmitter(NormalizedByteCode invoke, int constantPoolIndex) + { + ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(constantPoolIndex); +#if IMPORTER if(replacedMethodWrappers != null) { for(int i = 0; i < replacedMethodWrappers.Length; i++) @@ -3917,217 +3808,217 @@ private MethodWrapper GetMethodCallEmitter(NormalizedByteCode invoke, int consta } } #endif - MethodWrapper mw = null; - switch (invoke) - { - case NormalizedByteCode.__invokespecial: - mw = cpi.GetMethodForInvokespecial(); - break; - case NormalizedByteCode.__invokeinterface: - mw = cpi.GetMethod(); - break; - case NormalizedByteCode.__invokestatic: - case NormalizedByteCode.__invokevirtual: - mw = cpi.GetMethod(); - break; - case NormalizedByteCode.__dynamic_invokeinterface: - case NormalizedByteCode.__dynamic_invokestatic: - case NormalizedByteCode.__dynamic_invokevirtual: - case NormalizedByteCode.__dynamic_invokespecial: - case NormalizedByteCode.__privileged_invokestatic: - case NormalizedByteCode.__privileged_invokevirtual: - case NormalizedByteCode.__privileged_invokespecial: - return GetDynamicMethodWrapper(constantPoolIndex, invoke, cpi); - case NormalizedByteCode.__methodhandle_invoke: - case NormalizedByteCode.__methodhandle_link: - return new MethodHandleMethodWrapper(this, clazz, cpi); - default: - throw new InvalidOperationException(); - } - if (mw.IsDynamicOnly) - { - return GetDynamicMethodWrapper(constantPoolIndex, invoke, cpi); - } - return mw; - } - - private MethodWrapper GetDynamicMethodWrapper(int index, NormalizedByteCode invoke, ClassFile.ConstantPoolItemMI cpi) - { - ClassFile.RefKind kind; - switch (invoke) - { - case NormalizedByteCode.__invokeinterface: - case NormalizedByteCode.__dynamic_invokeinterface: - kind = ClassFile.RefKind.invokeInterface; - break; - case NormalizedByteCode.__invokestatic: - case NormalizedByteCode.__dynamic_invokestatic: - case NormalizedByteCode.__privileged_invokestatic: - kind = ClassFile.RefKind.invokeStatic; - break; - case NormalizedByteCode.__invokevirtual: - case NormalizedByteCode.__dynamic_invokevirtual: - case NormalizedByteCode.__privileged_invokevirtual: - kind = ClassFile.RefKind.invokeVirtual; - break; - case NormalizedByteCode.__invokespecial: - case NormalizedByteCode.__dynamic_invokespecial: - kind = ClassFile.RefKind.newInvokeSpecial; - break; - case NormalizedByteCode.__privileged_invokespecial: - // we don't support calling a base class constructor - kind = cpi.GetMethod().IsConstructor - ? ClassFile.RefKind.newInvokeSpecial - : ClassFile.RefKind.invokeSpecial; - break; - default: - throw new InvalidOperationException(); - } - bool privileged; - switch (invoke) - { - case NormalizedByteCode.__privileged_invokestatic: - case NormalizedByteCode.__privileged_invokevirtual: - case NormalizedByteCode.__privileged_invokespecial: - privileged = true; - break; - default: - privileged = false; - break; - } - return context.GetValue(index | ((byte)kind << 24)).Get(this, kind, cpi, privileged); - } - - private TypeWrapper ComputeThisType(TypeWrapper type, MethodWrapper method, NormalizedByteCode invoke) - { - if (type == VerifierTypeWrapper.UninitializedThis - || VerifierTypeWrapper.IsThis(type)) - { - return clazz; - } - else if (VerifierTypeWrapper.IsNew(type)) - { - return ((VerifierTypeWrapper)type).UnderlyingType; - } - else if (type == VerifierTypeWrapper.Null) - { - return method.DeclaringType; - } - else if (invoke == NormalizedByteCode.__invokevirtual && method.IsProtected && type.IsUnloadable) - { - return clazz; - } - else - { - return type; - } - } - - private LocalVar LoadLocal(int instructionIndex) - { - LocalVar v = localVars.GetLocalVar(instructionIndex); - if (v.isArg) - { - ClassFile.Method.Instruction instr = m.Instructions[instructionIndex]; - int i = m.ArgMap[instr.NormalizedArg1]; - ilGenerator.EmitLdarg(i); - if (v.type == PrimitiveTypeWrapper.DOUBLE) - { - ilGenerator.Emit(OpCodes.Conv_R8); - } - if (v.type == PrimitiveTypeWrapper.FLOAT) - { - ilGenerator.Emit(OpCodes.Conv_R4); - } - } - else if (v.type == VerifierTypeWrapper.Null) - { - ilGenerator.Emit(OpCodes.Ldnull); - } - else - { - if (v.builder == null) - { - v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); - if (debug && v.name != null) - { - v.builder.SetLocalSymInfo(v.name); - } - } - ilGenerator.Emit(OpCodes.Ldloc, v.builder); - } - return v; - } - - private LocalVar StoreLocal(int instructionIndex) - { - LocalVar v = localVars.GetLocalVar(instructionIndex); - if (v == null) - { - // dead store - ilGenerator.Emit(OpCodes.Pop); - } - else if (v.isArg) - { - ClassFile.Method.Instruction instr = m.Instructions[instructionIndex]; - int i = m.ArgMap[instr.NormalizedArg1]; - ilGenerator.EmitStarg(i); - } - else if (v.type == VerifierTypeWrapper.Null) - { - ilGenerator.Emit(OpCodes.Pop); - } - else - { - if (v.builder == null) - { - v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); - if (debug && v.name != null) - { - v.builder.SetLocalSymInfo(v.name); - } - } - ilGenerator.Emit(OpCodes.Stloc, v.builder); - } - return v; - } - - private Type GetLocalBuilderType(TypeWrapper tw) - { - if (tw.IsUnloadable) - { - return Types.Object; - } - else if (tw.IsAccessibleFrom(clazz)) - { - return tw.TypeAsLocalOrStackType; - } - else - { - return tw.GetPublicBaseTypeWrapper().TypeAsLocalOrStackType; - } - } - - private ExceptionTableEntry[] GetExceptionTableFor(InstructionFlags[] flags) - { - List list = new List(); - // return only reachable exception handlers (because the code gen depends on that) - for (int i = 0; i < exceptions.Length; i++) - { - // if the first instruction is unreachable, the entire block is unreachable, - // because you can't jump into a block (we've just split the blocks to ensure that) - if ((flags[exceptions[i].startIndex] & InstructionFlags.Reachable) != 0) - { - list.Add(exceptions[i]); - } - } - return list.ToArray(); - } - - private InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, bool skipFaultBlocks) - { - return MethodAnalyzer.ComputePartialReachability(ma, m.Instructions, exceptions, initialInstructionIndex, skipFaultBlocks); - } - } + MethodWrapper mw = null; + switch (invoke) + { + case NormalizedByteCode.__invokespecial: + mw = cpi.GetMethodForInvokespecial(); + break; + case NormalizedByteCode.__invokeinterface: + mw = cpi.GetMethod(); + break; + case NormalizedByteCode.__invokestatic: + case NormalizedByteCode.__invokevirtual: + mw = cpi.GetMethod(); + break; + case NormalizedByteCode.__dynamic_invokeinterface: + case NormalizedByteCode.__dynamic_invokestatic: + case NormalizedByteCode.__dynamic_invokevirtual: + case NormalizedByteCode.__dynamic_invokespecial: + case NormalizedByteCode.__privileged_invokestatic: + case NormalizedByteCode.__privileged_invokevirtual: + case NormalizedByteCode.__privileged_invokespecial: + return GetDynamicMethodWrapper(constantPoolIndex, invoke, cpi); + case NormalizedByteCode.__methodhandle_invoke: + case NormalizedByteCode.__methodhandle_link: + return new MethodHandleMethodWrapper(this, clazz, cpi); + default: + throw new InvalidOperationException(); + } + if (mw.IsDynamicOnly) + { + return GetDynamicMethodWrapper(constantPoolIndex, invoke, cpi); + } + return mw; + } + + private MethodWrapper GetDynamicMethodWrapper(int index, NormalizedByteCode invoke, ClassFile.ConstantPoolItemMI cpi) + { + ReferenceKind kind; + switch (invoke) + { + case NormalizedByteCode.__invokeinterface: + case NormalizedByteCode.__dynamic_invokeinterface: + kind = ReferenceKind.InvokeInterface; + break; + case NormalizedByteCode.__invokestatic: + case NormalizedByteCode.__dynamic_invokestatic: + case NormalizedByteCode.__privileged_invokestatic: + kind = ReferenceKind.InvokeStatic; + break; + case NormalizedByteCode.__invokevirtual: + case NormalizedByteCode.__dynamic_invokevirtual: + case NormalizedByteCode.__privileged_invokevirtual: + kind = ReferenceKind.InvokeVirtual; + break; + case NormalizedByteCode.__invokespecial: + case NormalizedByteCode.__dynamic_invokespecial: + kind = ReferenceKind.NewInvokeSpecial; + break; + case NormalizedByteCode.__privileged_invokespecial: + // we don't support calling a base class constructor + kind = cpi.GetMethod().IsConstructor + ? ReferenceKind.NewInvokeSpecial + : ReferenceKind.InvokeSpecial; + break; + default: + throw new InvalidOperationException(); + } + bool privileged; + switch (invoke) + { + case NormalizedByteCode.__privileged_invokestatic: + case NormalizedByteCode.__privileged_invokevirtual: + case NormalizedByteCode.__privileged_invokespecial: + privileged = true; + break; + default: + privileged = false; + break; + } + return context.GetValue(index | ((byte)kind << 24)).Get(this, kind, cpi, privileged); + } + + private TypeWrapper ComputeThisType(TypeWrapper type, MethodWrapper method, NormalizedByteCode invoke) + { + if (type == VerifierTypeWrapper.UninitializedThis + || VerifierTypeWrapper.IsThis(type)) + { + return clazz; + } + else if (VerifierTypeWrapper.IsNew(type)) + { + return ((VerifierTypeWrapper)type).UnderlyingType; + } + else if (type == VerifierTypeWrapper.Null) + { + return method.DeclaringType; + } + else if (invoke == NormalizedByteCode.__invokevirtual && method.IsProtected && type.IsUnloadable) + { + return clazz; + } + else + { + return type; + } + } + + private LocalVar LoadLocal(int instructionIndex) + { + LocalVar v = localVars.GetLocalVar(instructionIndex); + if (v.isArg) + { + ClassFile.Method.Instruction instr = m.Instructions[instructionIndex]; + int i = m.ArgMap[instr.NormalizedArg1]; + ilGenerator.EmitLdarg(i); + if (v.type == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Conv_R8); + } + if (v.type == PrimitiveTypeWrapper.FLOAT) + { + ilGenerator.Emit(OpCodes.Conv_R4); + } + } + else if (v.type == VerifierTypeWrapper.Null) + { + ilGenerator.Emit(OpCodes.Ldnull); + } + else + { + if (v.builder == null) + { + v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); + if (debug && v.name != null) + { + v.builder.SetLocalSymInfo(v.name); + } + } + ilGenerator.Emit(OpCodes.Ldloc, v.builder); + } + return v; + } + + private LocalVar StoreLocal(int instructionIndex) + { + LocalVar v = localVars.GetLocalVar(instructionIndex); + if (v == null) + { + // dead store + ilGenerator.Emit(OpCodes.Pop); + } + else if (v.isArg) + { + ClassFile.Method.Instruction instr = m.Instructions[instructionIndex]; + int i = m.ArgMap[instr.NormalizedArg1]; + ilGenerator.EmitStarg(i); + } + else if (v.type == VerifierTypeWrapper.Null) + { + ilGenerator.Emit(OpCodes.Pop); + } + else + { + if (v.builder == null) + { + v.builder = ilGenerator.DeclareLocal(GetLocalBuilderType(v.type)); + if (debug && v.name != null) + { + v.builder.SetLocalSymInfo(v.name); + } + } + ilGenerator.Emit(OpCodes.Stloc, v.builder); + } + return v; + } + + private Type GetLocalBuilderType(TypeWrapper tw) + { + if (tw.IsUnloadable) + { + return Types.Object; + } + else if (tw.IsAccessibleFrom(clazz)) + { + return tw.TypeAsLocalOrStackType; + } + else + { + return tw.GetPublicBaseTypeWrapper().TypeAsLocalOrStackType; + } + } + + private ExceptionTableEntry[] GetExceptionTableFor(InstructionFlags[] flags) + { + List list = new List(); + // return only reachable exception handlers (because the code gen depends on that) + for (int i = 0; i < exceptions.Length; i++) + { + // if the first instruction is unreachable, the entire block is unreachable, + // because you can't jump into a block (we've just split the blocks to ensure that) + if ((flags[exceptions[i].startIndex] & InstructionFlags.Reachable) != 0) + { + list.Add(exceptions[i]); + } + } + return list.ToArray(); + } + + private InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, bool skipFaultBlocks) + { + return MethodAnalyzer.ComputePartialReachability(ma, m.Instructions, exceptions, initialInstructionIndex, skipFaultBlocks); + } + } } diff --git a/src/IKVM.Runtime/intrinsics.cs b/src/IKVM.Runtime/intrinsics.cs index 5e8c417472..356da57f22 100644 --- a/src/IKVM.Runtime/intrinsics.cs +++ b/src/IKVM.Runtime/intrinsics.cs @@ -24,27 +24,33 @@ Jeroen Frijters using System; using System.Collections.Generic; -#if STATIC_COMPILER + +using IKVM.Runtime; + +#if IMPORTER using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Tools.Importer; + using Type = IKVM.Reflection.Type; #else using System.Reflection; using System.Reflection.Emit; #endif -using System.Diagnostics; + using Instruction = IKVM.Internal.ClassFile.Method.Instruction; using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags; -using IKVM.Runtime; namespace IKVM.Internal { + sealed class EmitIntrinsicContext { + internal readonly MethodWrapper Method; internal readonly DynamicTypeWrapper.FinishContext Context; internal readonly CodeEmitter Emitter; - private readonly CodeInfo ma; + readonly CodeInfo ma; internal readonly int OpcodeIndex; internal readonly MethodWrapper Caller; internal readonly ClassFile ClassFile; @@ -68,21 +74,16 @@ internal EmitIntrinsicContext(MethodWrapper method, DynamicTypeWrapper.FinishCon internal bool MatchRange(int offset, int length) { if (OpcodeIndex + offset < 0) - { return false; - } + if (OpcodeIndex + offset + length > Code.Length) - { return false; - } + // we check for branches *into* the range, the start of the range may be a branch target for (int i = OpcodeIndex + offset + 1, end = OpcodeIndex + offset + length; i < end; i++) - { if ((Flags[i] & InstructionFlags.BranchTarget) != 0) - { return false; - } - } + return true; } @@ -130,6 +131,7 @@ internal void PatchOpCode(int offset, NormalizedByteCode opc) { Code[OpcodeIndex + offset].PatchOpCode(opc); } + } static class Intrinsics @@ -170,18 +172,19 @@ public override int GetHashCode() return methodName.GetHashCode(); } } - private static readonly Dictionary intrinsics = Register(); -#if STATIC_COMPILER - private static readonly Type typeofFloatConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.FloatConverter"); - private static readonly Type typeofDoubleConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.DoubleConverter"); + + static readonly Dictionary intrinsics = Register(); +#if IMPORTER + static readonly Type typeofFloatConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.FloatConverter"); + static readonly Type typeofDoubleConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.DoubleConverter"); #else - private static readonly Type typeofFloatConverter = typeof(IKVM.Runtime.FloatConverter); - private static readonly Type typeofDoubleConverter = typeof(IKVM.Runtime.DoubleConverter); + static readonly Type typeofFloatConverter = typeof(IKVM.Runtime.FloatConverter); + static readonly Type typeofDoubleConverter = typeof(IKVM.Runtime.DoubleConverter); #endif - private static Dictionary Register() + static Dictionary Register() { - Dictionary intrinsics = new Dictionary(); + var intrinsics = new Dictionary(); intrinsics.Add(new IntrinsicKey("java.lang.Object", "getClass", "()Ljava.lang.Class;"), Object_getClass); intrinsics.Add(new IntrinsicKey("java.lang.Class", "desiredAssertionStatus", "()Z"), Class_desiredAssertionStatus); intrinsics.Add(new IntrinsicKey("java.lang.Float", "floatToRawIntBits", "(F)I"), Float_floatToRawIntBits); @@ -190,15 +193,14 @@ private static Dictionary Register() intrinsics.Add(new IntrinsicKey("java.lang.Double", "longBitsToDouble", "(J)D"), Double_longBitsToDouble); intrinsics.Add(new IntrinsicKey("java.lang.System", "arraycopy", "(Ljava.lang.Object;ILjava.lang.Object;II)V"), System_arraycopy); intrinsics.Add(new IntrinsicKey("java.util.concurrent.atomic.AtomicReferenceFieldUpdater", "newUpdater", "(Ljava.lang.Class;Ljava.lang.Class;Ljava.lang.String;)Ljava.util.concurrent.atomic.AtomicReferenceFieldUpdater;"), AtomicReferenceFieldUpdater_newUpdater); -#if STATIC_COMPILER +#if IMPORTER intrinsics.Add(new IntrinsicKey("sun.reflect.Reflection", "getCallerClass", "()Ljava.lang.Class;"), Reflection_getCallerClass); intrinsics.Add(new IntrinsicKey("ikvm.internal.CallerID", "getCallerID", "()Likvm.internal.CallerID;"), CallerID_getCallerID); #endif intrinsics.Add(new IntrinsicKey("ikvm.runtime.Util", "getInstanceTypeFromClass", "(Ljava.lang.Class;)Lcli.System.Type;"), Util_getInstanceTypeFromClass); -#if STATIC_COMPILER +#if IMPORTER // this only applies to the core class library, so makes no sense in dynamic mode intrinsics.Add(new IntrinsicKey("java.lang.Class", "getPrimitiveClass", "(Ljava.lang.String;)Ljava.lang.Class;"), Class_getPrimitiveClass); - intrinsics.Add(new IntrinsicKey("java.lang.Class", "getDeclaredField", "(Ljava.lang.String;)Ljava.lang.reflect.Field;"), Class_getDeclaredField); #endif intrinsics.Add(new IntrinsicKey("java.lang.ThreadLocal", "", "()V"), ThreadLocal_new); intrinsics.Add(new IntrinsicKey("sun.misc.Unsafe", "ensureClassInitialized", "(Ljava.lang.Class;)V"), Unsafe_ensureClassInitialized); @@ -288,76 +290,6 @@ private static bool Class_desiredAssertionStatus(EmitIntrinsicContext eic) return false; } -#if STATIC_COMPILER - // this intrinsifies the following two patterns: - // unsafe.objectFieldOffset(XXX.class.getDeclaredField("xxx")); - // and - // Class k = XXX.class; - // unsafe.objectFieldOffset(k.getDeclaredField("xxx")); - // to avoid initializing the full reflection machinery at this point - private static bool Class_getDeclaredField(EmitIntrinsicContext eic) - { - if (eic.Caller.DeclaringType.GetClassLoader() != CoreClasses.java.lang.Object.Wrapper.GetClassLoader()) - { - // we can only do this optimization when compiling the trusted core classes - return false; - } - TypeWrapper fieldClass; - if (eic.MatchRange(-2, 4) - && eic.Match(-2, NormalizedByteCode.__ldc) - && eic.Match(-1, NormalizedByteCode.__ldc_nothrow) - && eic.Match(1, NormalizedByteCode.__invokevirtual)) - { - // unsafe.objectFieldOffset(XXX.class.getDeclaredField("xxx")); - fieldClass = eic.GetClassLiteral(-2); - } - else if (eic.MatchRange(-5, 7) - && eic.Match(-5, NormalizedByteCode.__ldc) - && eic.Match(-4, NormalizedByteCode.__astore) - && eic.Match(-3, NormalizedByteCode.__getstatic) - && eic.Match(-2, NormalizedByteCode.__aload, eic.Code[eic.OpcodeIndex - 4].NormalizedArg1) - && eic.Match(-1, NormalizedByteCode.__ldc_nothrow) - && eic.Match(1, NormalizedByteCode.__invokevirtual)) - { - // Class k = XXX.class; - // unsafe.objectFieldOffset(k.getDeclaredField("xxx")); - fieldClass = eic.GetClassLiteral(-5); - } - else - { - return false; - } - FieldWrapper field = null; - string fieldName = eic.GetStringLiteral(-1); - foreach (FieldWrapper fw in fieldClass.GetFields()) - { - if (fw.Name == fieldName) - { - if (field != null) - { - return false; - } - field = fw; - } - } - if (field == null || field.IsStatic) - { - return false; - } - ClassFile.ConstantPoolItemMI cpi = eic.GetMethodref(1); - if (cpi.Class == "sun.misc.Unsafe" && cpi.Name == "objectFieldOffset" && cpi.Signature == "(Ljava.lang.reflect.Field;)J") - { - MethodWrapper mw = ClassLoaderWrapper.LoadClassCritical("sun.misc.Unsafe") - .GetMethodWrapper("objectFieldOffset", "(Ljava.lang.Class;Ljava.lang.String;)J", false); - mw.Link(); - mw.EmitCallvirt(eic.Emitter); - eic.PatchOpCode(1, NormalizedByteCode.__nop); - return true; - } - return false; - } -#endif - private static bool IsSafeForGetClassOptimization(TypeWrapper tw) { // because of ghost arrays, we don't optimize if both types are either java.lang.Object or an array @@ -382,24 +314,29 @@ private static bool Double_doubleToRawLongBits(EmitIntrinsicContext eic) return true; } - private static bool Double_longBitsToDouble(EmitIntrinsicContext eic) + static bool Double_longBitsToDouble(EmitIntrinsicContext eic) { EmitConversion(eic.Emitter, typeofDoubleConverter, "ToDouble"); return true; } - private static void EmitConversion(CodeEmitter ilgen, Type converterType, string method) + static void EmitConversion(CodeEmitter ilgen, Type converterType, string method) { - CodeEmitterLocal converter = ilgen.UnsafeAllocTempLocal(converterType); + var converter = ilgen.UnsafeAllocTempLocal(converterType); ilgen.Emit(OpCodes.Ldloca, converter); ilgen.Emit(OpCodes.Call, converterType.GetMethod(method)); } - private static bool System_arraycopy(EmitIntrinsicContext eic) + /// + /// Calls to System.arraycopy can be replaced with directly optimized versions. + /// + /// + /// + static bool System_arraycopy(EmitIntrinsicContext eic) { // if the array arguments on the stack are of a known array type, we can redirect to an optimized version of arraycopy. - TypeWrapper dst_type = eic.GetStackTypeWrapper(0, 2); - TypeWrapper src_type = eic.GetStackTypeWrapper(0, 4); + var dst_type = eic.GetStackTypeWrapper(0, 2); + var src_type = eic.GetStackTypeWrapper(0, 4); if (!dst_type.IsUnloadable && dst_type.IsArray && dst_type == src_type) { switch (dst_type.Name[1]) @@ -426,9 +363,9 @@ private static bool System_arraycopy(EmitIntrinsicContext eic) // use the fast version if the exact destination type is known // (in that case the "dst_type == src_type" above should // be changed to "src_type.IsAssignableTo(dst_type)". - TypeWrapper elemtw = dst_type.ElementTypeWrapper; + var elemtw = dst_type.ElementTypeWrapper; // note that IsFinal returns true for array types, so we have to be careful! - if (!elemtw.IsArray && elemtw.IsFinal) + if (elemtw.IsArray == false && elemtw.IsFinal) { eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_fast); } @@ -446,14 +383,14 @@ private static bool System_arraycopy(EmitIntrinsicContext eic) } } - private static bool AtomicReferenceFieldUpdater_newUpdater(EmitIntrinsicContext eic) + static bool AtomicReferenceFieldUpdater_newUpdater(EmitIntrinsicContext eic) { return AtomicReferenceFieldUpdaterEmitter.Emit(eic.Context, eic.Caller.DeclaringType, eic.Emitter, eic.ClassFile, eic.OpcodeIndex, eic.Code, eic.Flags); } -#if STATIC_COMPILER +#if IMPORTER - private static bool Reflection_getCallerClass(EmitIntrinsicContext eic) + static bool Reflection_getCallerClass(EmitIntrinsicContext eic) { if (eic.Caller.HasCallerID) { @@ -513,50 +450,52 @@ private static bool CallerID_getCallerID(EmitIntrinsicContext eic) #endif - private static bool Util_getInstanceTypeFromClass(EmitIntrinsicContext eic) + static bool Util_getInstanceTypeFromClass(EmitIntrinsicContext eic) { - if (eic.MatchRange(-1, 2) - && eic.Match(-1, NormalizedByteCode.__ldc)) + if (eic.MatchRange(-1, 2) && eic.Match(-1, NormalizedByteCode.__ldc)) { - TypeWrapper tw = eic.GetClassLiteral(-1); - if (!tw.IsUnloadable) + var tw = eic.GetClassLiteral(-1); + if (tw.IsUnloadable == false) { eic.Emitter.Emit(OpCodes.Pop); if (tw.IsRemapped && tw.IsFinal) - { eic.Emitter.Emit(OpCodes.Ldtoken, tw.TypeAsTBD); - } else - { eic.Emitter.Emit(OpCodes.Ldtoken, tw.TypeAsBaseType); - } + eic.Emitter.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod); return true; } } + return false; } -#if STATIC_COMPILER - private static bool Class_getPrimitiveClass(EmitIntrinsicContext eic) +#if IMPORTER + + static bool Class_getPrimitiveClass(EmitIntrinsicContext eic) { eic.Emitter.Emit(OpCodes.Pop); eic.Emitter.Emit(OpCodes.Ldnull); - MethodWrapper mw = CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("", "(Lcli.System.Type;)V", false); + var mw = CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("", "(Lcli.System.Type;)V", false); mw.Link(); mw.EmitNewobj(eic.Emitter); return true; } + #endif - private static bool ThreadLocal_new(EmitIntrinsicContext eic) + /// + /// Replaces calls to ThreadLocal.new with the direct initialization of the ThreadLocal. + /// + /// + /// + static bool ThreadLocal_new(EmitIntrinsicContext eic) { // it is only valid to replace a ThreadLocal instantiation by our ThreadStatic based version, if we can prove that the instantiation only happens once // (which is the case when we're in and there aren't any branches that lead to the current position) - if (!eic.Caller.IsClassInitializer) - { + if (eic.Caller.IsClassInitializer == false) return false; - } #if CLASSGC if (JVM.classUnloading) { @@ -565,23 +504,24 @@ private static bool ThreadLocal_new(EmitIntrinsicContext eic) } #endif for (int i = 0; i <= eic.OpcodeIndex; i++) - { if ((eic.Flags[i] & InstructionFlags.BranchTarget) != 0) - { return false; - } - } + eic.Emitter.Emit(OpCodes.Newobj, eic.Context.DefineThreadLocalType()); return true; } - private static bool Unsafe_ensureClassInitialized(EmitIntrinsicContext eic) + /// + /// Replaces calls to Unsafe.ensureClassInitialized with direct calls to run the class constructor. + /// + /// + /// + static bool Unsafe_ensureClassInitialized(EmitIntrinsicContext eic) { - if (eic.MatchRange(-1, 2) - && eic.Match(-1, NormalizedByteCode.__ldc)) + if (eic.MatchRange(-1, 2) && eic.Match(-1, NormalizedByteCode.__ldc)) { - TypeWrapper classLiteral = eic.GetClassLiteral(-1); - if (!classLiteral.IsUnloadable) + var classLiteral = eic.GetClassLiteral(-1); + if (classLiteral.IsUnloadable == false) { eic.Emitter.Emit(OpCodes.Pop); eic.Emitter.EmitNullCheck(); @@ -589,76 +529,81 @@ private static bool Unsafe_ensureClassInitialized(EmitIntrinsicContext eic) return true; } } + return false; } + /// + /// Returns true if the given specifies an array type suitable for an unsafe operation. + /// + /// + /// internal static bool IsSupportedArrayTypeForUnsafeOperation(TypeWrapper tw) { - return tw.IsArray - && !tw.IsGhostArray - && !tw.ElementTypeWrapper.IsPrimitive - && !tw.ElementTypeWrapper.IsNonPrimitiveValueType; + return tw.IsArray && !tw.IsGhostArray && !tw.ElementTypeWrapper.IsPrimitive && !tw.ElementTypeWrapper.IsNonPrimitiveValueType; } - private static bool Unsafe_putObject(EmitIntrinsicContext eic) + static bool Unsafe_putObject(EmitIntrinsicContext eic) { return Unsafe_putObjectImpl(eic, false); } - private static bool Unsafe_putOrderedObject(EmitIntrinsicContext eic) + static bool Unsafe_putOrderedObject(EmitIntrinsicContext eic) { return Unsafe_putObjectImpl(eic, false); } - private static bool Unsafe_putObjectVolatile(EmitIntrinsicContext eic) + static bool Unsafe_putObjectVolatile(EmitIntrinsicContext eic) { return Unsafe_putObjectImpl(eic, true); } - private static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool membarrier) + static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool isVolatile) { - TypeWrapper tw = eic.GetStackTypeWrapper(0, 2); - if (IsSupportedArrayTypeForUnsafeOperation(tw) - && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper)) + var tw = eic.GetStackTypeWrapper(0, 2); + + // check for non-primitive array case + if (IsSupportedArrayTypeForUnsafeOperation(tw) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper)) { - CodeEmitterLocal value = eic.Emitter.AllocTempLocal(tw.ElementTypeWrapper.TypeAsLocalOrStackType); - CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal array = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + var value = eic.Emitter.AllocTempLocal(tw.ElementTypeWrapper.TypeAsLocalOrStackType); + var index = eic.Emitter.AllocTempLocal(Types.Int32); + var array = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Stloc, value); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); eic.Emitter.Emit(OpCodes.Stloc, index); eic.Emitter.Emit(OpCodes.Stloc, array); EmitConsumeUnsafe(eic); + eic.Emitter.Emit(OpCodes.Ldloc, array); eic.Emitter.Emit(OpCodes.Ldloc, index); eic.Emitter.Emit(OpCodes.Ldloc, value); + eic.Emitter.Emit(OpCodes.Stelem_Ref); + + if (isVolatile) + eic.Emitter.EmitMemoryBarrier(); + eic.Emitter.ReleaseTempLocal(array); eic.Emitter.ReleaseTempLocal(index); eic.Emitter.ReleaseTempLocal(value); - eic.Emitter.Emit(OpCodes.Stelem_Ref); - if (membarrier) - { - eic.Emitter.EmitMemoryBarrier(); - } eic.NonLeaf = false; return true; } - if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 - || (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0) - { + + if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 || (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0) return false; - } - if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) - && eic.Match(-2, NormalizedByteCode.__getstatic)) + + if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) && eic.Match(-2, NormalizedByteCode.__getstatic)) { - FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(-2)); - if (fw != null - && (!fw.IsFinal || (!fw.IsStatic && eic.Caller.Name == "") || (fw.IsStatic && eic.Caller.Name == "")) - && fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) - && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) - && (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 2))) + var fw = GetUnsafeField(eic, eic.GetFieldref(-2)); + if (fw != null && + (!fw.IsFinal || (!fw.IsStatic && eic.Caller.Name == "") || (fw.IsStatic && eic.Caller.Name == "")) && + fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) && + eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) && + (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 2))) { - CodeEmitterLocal value = eic.Emitter.AllocTempLocal(fw.FieldTypeWrapper.TypeAsLocalOrStackType); + var value = eic.Emitter.AllocTempLocal(fw.FieldTypeWrapper.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, value); eic.Emitter.Emit(OpCodes.Pop); // discard offset field if (fw.IsStatic) @@ -668,114 +613,138 @@ private static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool membarri } else { - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(fw.DeclaringType.TypeAsLocalOrStackType); - eic.Emitter.Emit(OpCodes.Stloc, obj); + var target = eic.Emitter.AllocTempLocal(fw.DeclaringType.TypeAsLocalOrStackType); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.ReleaseTempLocal(obj); + eic.Emitter.Emit(OpCodes.Ldloc, target); + eic.Emitter.ReleaseTempLocal(target); } + eic.Emitter.Emit(OpCodes.Ldloc, value); eic.Emitter.ReleaseTempLocal(value); + // note that we assume the CLR memory model where all writes are ordered, // so we don't need a volatile store or a memory barrier and putOrderedObject // is typically used with a volatile field, so to avoid the memory barrier, // we don't use FieldWrapper.EmitSet(), but emit the store directly eic.Emitter.Emit(fw.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fw.GetField()); - if (membarrier) - { + if (isVolatile) eic.Emitter.EmitMemoryBarrier(); - } + eic.NonLeaf = false; return true; } } + return false; } - private static bool Unsafe_getObjectVolatile(EmitIntrinsicContext eic) + /// + /// Replaces calls to Unsafe.getObjectVolatile against an array with inline code. + /// + /// + /// + static bool Unsafe_getObjectVolatile(EmitIntrinsicContext eic) { // the check here must be kept in sync with the hack in MethodAnalyzer.AnalyzeTypeFlow() - TypeWrapper tw = eic.GetStackTypeWrapper(0, 1); + var tw = eic.GetStackTypeWrapper(0, 1); if (IsSupportedArrayTypeForUnsafeOperation(tw)) { - CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + var offset = eic.Emitter.AllocTempLocal(Types.Int32); + var target = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); - eic.Emitter.Emit(OpCodes.Stloc, index); - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, offset); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldloc, index); - eic.Emitter.ReleaseTempLocal(obj); - eic.Emitter.ReleaseTempLocal(index); + + eic.Emitter.Emit(OpCodes.Ldloc, target); + eic.Emitter.Emit(OpCodes.Ldloc, offset); eic.Emitter.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType.GetElementType()); eic.Emitter.Emit(OpCodes.Volatile); eic.Emitter.Emit(OpCodes.Ldind_Ref); + // remove the redundant checkcast that usually follows - if (eic.Code[eic.OpcodeIndex + 1].NormalizedOpCode == NormalizedByteCode.__checkcast - && tw.ElementTypeWrapper.IsAssignableTo(eic.ClassFile.GetConstantPoolClassType(eic.Code[eic.OpcodeIndex + 1].Arg1))) - { + if (eic.Code[eic.OpcodeIndex + 1].NormalizedOpCode == NormalizedByteCode.__checkcast && tw.ElementTypeWrapper.IsAssignableTo(eic.ClassFile.GetConstantPoolClassType(eic.Code[eic.OpcodeIndex + 1].Arg1))) eic.PatchOpCode(1, NormalizedByteCode.__nop); - } + + eic.Emitter.ReleaseTempLocal(target); + eic.Emitter.ReleaseTempLocal(offset); eic.NonLeaf = false; return true; } + return false; } - private static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic) + /// + /// Replaces a call to Unsafe.compareAndSwapObject with IL that directly conducts the operation. + /// + /// + /// + static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic) { - TypeWrapper tw = eic.GetStackTypeWrapper(0, 3); - if (IsSupportedArrayTypeForUnsafeOperation(tw) - && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper) - && eic.GetStackTypeWrapper(0, 1).IsAssignableTo(tw.ElementTypeWrapper)) + var tw = eic.GetStackTypeWrapper(0, 3); + + // target object is an array type + // convert offset to int32 index + // directly compare/exchange value + if (IsSupportedArrayTypeForUnsafeOperation(tw) && + eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper) && + eic.GetStackTypeWrapper(0, 1).IsAssignableTo(tw.ElementTypeWrapper)) { - Type type = tw.TypeAsLocalOrStackType.GetElementType(); - CodeEmitterLocal update = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + var type = tw.TypeAsLocalOrStackType.GetElementType(); + var update = eic.Emitter.AllocTempLocal(type); + var expect = eic.Emitter.AllocTempLocal(type); + var offset = eic.Emitter.AllocTempLocal(Types.Int32); + var target = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); - eic.Emitter.Emit(OpCodes.Stloc, index); - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, offset); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldloc, index); + + // emit new call site + eic.Emitter.Emit(OpCodes.Ldloc, target); + eic.Emitter.Emit(OpCodes.Ldloc, offset); eic.Emitter.Emit(OpCodes.Ldelema, type); eic.Emitter.Emit(OpCodes.Ldloc, update); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type)); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Ceq); - eic.Emitter.ReleaseTempLocal(obj); - eic.Emitter.ReleaseTempLocal(index); + + eic.Emitter.ReleaseTempLocal(target); + eic.Emitter.ReleaseTempLocal(offset); eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return true; } - if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 - || (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0 - || (eic.Flags[eic.OpcodeIndex - 2] & InstructionFlags.BranchTarget) != 0) - { + + if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 || + (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0 || + (eic.Flags[eic.OpcodeIndex - 2] & InstructionFlags.BranchTarget) != 0) return false; - } - if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) - && (eic.Match(-2, NormalizedByteCode.__aload) || eic.Match(-2, NormalizedByteCode.__aconst_null)) - && eic.Match(-3, NormalizedByteCode.__getstatic)) + + if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) && + (eic.Match(-2, NormalizedByteCode.__aload) || eic.Match(-2, NormalizedByteCode.__aconst_null)) && + eic.Match(-3, NormalizedByteCode.__getstatic)) { - FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(-3)); - if (fw != null - && fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) - && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) - && eic.GetStackTypeWrapper(0, 1).IsAssignableTo(fw.FieldTypeWrapper) - && (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 3))) + var fw = GetUnsafeField(eic, eic.GetFieldref(-3)); + if (fw != null && + fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) && + eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) && + eic.GetStackTypeWrapper(0, 1).IsAssignableTo(fw.FieldTypeWrapper) && + (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 3))) { - Type type = fw.FieldTypeWrapper.TypeAsLocalOrStackType; - CodeEmitterLocal update = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(type); + var type = fw.FieldTypeWrapper.TypeAsLocalOrStackType; + var update = eic.Emitter.AllocTempLocal(type); + var expect = eic.Emitter.AllocTempLocal(type); eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Pop); // discard index @@ -787,13 +756,14 @@ private static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic) } else { - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + var obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, obj); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, obj); eic.Emitter.ReleaseTempLocal(obj); eic.Emitter.Emit(OpCodes.Ldflda, fw.GetField()); } + eic.Emitter.Emit(OpCodes.Ldloc, update); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type)); @@ -805,91 +775,108 @@ private static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic) return true; } } + // stack layout at call site: // 4 Unsafe (receiver) - // 3 Object (obj) + // 3 Object (target) // 2 long (offset) // 1 Object (expect) // 0 Object (update) - TypeWrapper twUnsafe = eic.GetStackTypeWrapper(0, 4); + var twUnsafe = eic.GetStackTypeWrapper(0, 4); if (twUnsafe == VerifierTypeWrapper.Null) - { return false; - } + for (int i = 0; ; i--) { if ((eic.Flags[eic.OpcodeIndex + i] & InstructionFlags.BranchTarget) != 0) - { return false; - } + if (eic.GetStackTypeWrapper(i, 0) == twUnsafe) { // the pattern we recognize is: // aload // getstatic - if (eic.Match(i, NormalizedByteCode.__aload) && eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType - && eic.Match(i + 1, NormalizedByteCode.__getstatic)) + if (eic.Match(i, NormalizedByteCode.__aload) && + eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType && + eic.Match(i + 1, NormalizedByteCode.__getstatic)) { - FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); + var fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); if (fw != null && !fw.IsStatic && fw.DeclaringType == eic.Caller.DeclaringType) { - Type type = fw.FieldTypeWrapper.TypeAsLocalOrStackType; - CodeEmitterLocal update = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + var update = eic.Emitter.AllocTempLocal(fw.FieldTypeWrapper.TypeAsLocalOrStackType); + var expect = eic.Emitter.AllocTempLocal(fw.FieldTypeWrapper.TypeAsLocalOrStackType); + var target = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Pop); // discard offset - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldflda, fw.GetField()); - eic.Emitter.Emit(OpCodes.Ldloc, update); - eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type)); + + // emit new call site + eic.Emitter.Emit(OpCodes.Ldloc, target); eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Ceq); + eic.Emitter.Emit(OpCodes.Ldloc, update); + fw.EmitUnsafeCompareAndSwap(eic.Emitter); + eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return true; } } + return false; } } } - private static bool Unsafe_getAndSetObject(EmitIntrinsicContext eic) + /// + /// Replaces calls to Unsafe.getAndSetObject against an array with inline code. + /// + /// + /// + static bool Unsafe_getAndSetObject(EmitIntrinsicContext eic) { - TypeWrapper tw = eic.GetStackTypeWrapper(0, 2); - if (IsSupportedArrayTypeForUnsafeOperation(tw) - && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper)) + var tw = eic.GetStackTypeWrapper(0, 2); + if (IsSupportedArrayTypeForUnsafeOperation(tw) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper)) { - Type type = tw.TypeAsLocalOrStackType.GetElementType(); - CodeEmitterLocal newValue = eic.Emitter.AllocTempLocal(type); - CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + var type = tw.TypeAsLocalOrStackType.GetElementType(); + var newValue = eic.Emitter.AllocTempLocal(type); + var offset = eic.Emitter.AllocTempLocal(Types.Int32); + var target = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Stloc, newValue); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); - eic.Emitter.Emit(OpCodes.Stloc, index); - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, offset); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldloc, index); + + // emit new call + eic.Emitter.Emit(OpCodes.Ldloc, target); + eic.Emitter.Emit(OpCodes.Ldloc, offset); eic.Emitter.Emit(OpCodes.Ldelema, type); eic.Emitter.Emit(OpCodes.Ldloc, newValue); eic.Emitter.Emit(OpCodes.Call, MakeExchange(type)); - eic.Emitter.ReleaseTempLocal(obj); - eic.Emitter.ReleaseTempLocal(index); + + eic.Emitter.ReleaseTempLocal(target); + eic.Emitter.ReleaseTempLocal(offset); eic.Emitter.ReleaseTempLocal(newValue); eic.NonLeaf = false; return true; } + return false; } - private static bool Unsafe_compareAndSwapInt(EmitIntrinsicContext eic) + /// + /// Replaces a call to Unsafe.compareAndSwapInt with IL that directly conducts the operation. + /// + /// + /// + static bool Unsafe_compareAndSwapInt(EmitIntrinsicContext eic) { // stack layout at call site: // 4 Unsafe (receiver) @@ -897,49 +884,49 @@ private static bool Unsafe_compareAndSwapInt(EmitIntrinsicContext eic) // 2 long (offset) // 1 int (expect) // 0 int (update) - TypeWrapper twUnsafe = eic.GetStackTypeWrapper(0, 4); + + var twUnsafe = eic.GetStackTypeWrapper(0, 4); if (twUnsafe == VerifierTypeWrapper.Null) - { return false; - } + for (int i = 0; ; i--) { if ((eic.Flags[eic.OpcodeIndex + i] & InstructionFlags.BranchTarget) != 0) - { return false; - } + if (eic.GetStackTypeWrapper(i, 0) == twUnsafe) { // the pattern we recognize is: // aload // getstatic - if (eic.Match(i, NormalizedByteCode.__aload) && eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType - && eic.Match(i + 1, NormalizedByteCode.__getstatic)) + if (eic.Match(i, NormalizedByteCode.__aload) && + eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType && + eic.Match(i + 1, NormalizedByteCode.__getstatic)) { - FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); + var fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); if (fw != null && !fw.IsStatic && fw.DeclaringType == eic.Caller.DeclaringType) { - CodeEmitterLocal update = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(Types.Int32); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + var update = eic.Emitter.AllocTempLocal(Types.Int32); + var expect = eic.Emitter.AllocTempLocal(Types.Int32); + var target = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Pop); // discard offset - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldflda, fw.GetField()); - eic.Emitter.Emit(OpCodes.Ldloc, update); + eic.Emitter.Emit(OpCodes.Ldloc, target); eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Call, InterlockedMethods.CompareExchangeInt32); - eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Ceq); + eic.Emitter.Emit(OpCodes.Ldloc, update); + fw.EmitUnsafeCompareAndSwap(eic.Emitter); + eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return true; } } + return false; } } @@ -995,7 +982,12 @@ private static bool Unsafe_getAndAddInt(EmitIntrinsicContext eic) } } - private static bool Unsafe_compareAndSwapLong(EmitIntrinsicContext eic) + /// + /// Replaces a call to Unsafe.compareAndSwapLong with IL that directly conducts the operation. + /// + /// + /// + static bool Unsafe_compareAndSwapLong(EmitIntrinsicContext eic) { // stack layout at call site: // 4 Unsafe (receiver) @@ -1003,49 +995,52 @@ private static bool Unsafe_compareAndSwapLong(EmitIntrinsicContext eic) // 2 long (offset) // 1 long (expect) // 0 long (update) - TypeWrapper twUnsafe = eic.GetStackTypeWrapper(0, 4); + + var twUnsafe = eic.GetStackTypeWrapper(0, 4); if (twUnsafe == VerifierTypeWrapper.Null) - { return false; - } + for (int i = 0; ; i--) { if ((eic.Flags[eic.OpcodeIndex + i] & InstructionFlags.BranchTarget) != 0) - { return false; - } + if (eic.GetStackTypeWrapper(i, 0) == twUnsafe) { // the pattern we recognize is: // aload // getstatic - if (eic.Match(i, NormalizedByteCode.__aload) && eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType - && eic.Match(i + 1, NormalizedByteCode.__getstatic)) + if (eic.Match(i, NormalizedByteCode.__aload) && + eic.GetStackTypeWrapper(i + 1, 0) == eic.Caller.DeclaringType && + eic.Match(i + 1, NormalizedByteCode.__getstatic)) { - FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); + var fw = GetUnsafeField(eic, eic.GetFieldref(i + 1)); if (fw != null && !fw.IsStatic && fw.DeclaringType == eic.Caller.DeclaringType) { - CodeEmitterLocal update = eic.Emitter.AllocTempLocal(Types.Int64); - CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(Types.Int64); - CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + var update = eic.Emitter.AllocTempLocal(Types.Int64); + var expect = eic.Emitter.AllocTempLocal(Types.Int64); + var target = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); + + // consume existing call site eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Pop); // discard offset - eic.Emitter.Emit(OpCodes.Stloc, obj); + eic.Emitter.Emit(OpCodes.Stloc, target); EmitConsumeUnsafe(eic); - eic.Emitter.Emit(OpCodes.Ldloc, obj); - eic.Emitter.Emit(OpCodes.Ldflda, fw.GetField()); - eic.Emitter.Emit(OpCodes.Ldloc, update); - eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Call, InterlockedMethods.CompareExchangeInt64); + + // emit new call site + eic.Emitter.Emit(OpCodes.Ldloc, target); eic.Emitter.Emit(OpCodes.Ldloc, expect); - eic.Emitter.Emit(OpCodes.Ceq); + eic.Emitter.Emit(OpCodes.Ldloc, update); + fw.EmitUnsafeCompareAndSwap(eic.Emitter); + eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return true; } } + return false; } } @@ -1056,9 +1051,9 @@ internal static MethodInfo MakeExchange(Type type) return InterlockedMethods.ExchangeOfT.MakeGenericMethod(type); } - private static void EmitConsumeUnsafe(EmitIntrinsicContext eic) + static void EmitConsumeUnsafe(EmitIntrinsicContext eic) { -#if STATIC_COMPILER +#if IMPORTER if (eic.Caller.DeclaringType.GetClassLoader() == CoreClasses.java.lang.Object.Wrapper.GetClassLoader()) { // we're compiling the core library (which is obviously trusted), so we don't need to check @@ -1072,7 +1067,7 @@ private static void EmitConsumeUnsafe(EmitIntrinsicContext eic) } } - private static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.ConstantPoolItemFieldref field) + static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.ConstantPoolItemFieldref field) { if (eic.Caller.DeclaringType.GetClassLoader() != CoreClasses.java.lang.Object.Wrapper.GetClassLoader()) { @@ -1086,7 +1081,7 @@ private static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.C if (field.GetField().DeclaringType == eic.Caller.DeclaringType) { // now look inside the static initializer to see if we can found out what field it refers to - foreach (ClassFile.Method method in eic.ClassFile.Methods) + foreach (var method in eic.ClassFile.Methods) { if (method.IsClassInitializer) { @@ -1111,22 +1106,22 @@ private static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.C */ for (int i = 0; i < method.Instructions.Length; i++) { - if (method.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic - && eic.ClassFile.GetFieldref(method.Instructions[i].Arg1) == field) + if (method.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic && + eic.ClassFile.GetFieldref(method.Instructions[i].Arg1) == field) { - if (MatchInvokeVirtual(eic, ref method.Instructions[i - 1], "sun.misc.Unsafe", "objectFieldOffset", "(Ljava.lang.reflect.Field;)J") - && MatchInvokeVirtual(eic, ref method.Instructions[i - 2], "java.lang.Class", "getDeclaredField", "(Ljava.lang.String;)Ljava.lang.reflect.Field;") - && MatchLdc(eic, ref method.Instructions[i - 3], ClassFile.ConstantType.String) - && (method.Instructions[i - 4].NormalizedOpCode == NormalizedByteCode.__aload || method.Instructions[i - 4].NormalizedOpCode == NormalizedByteCode.__ldc) - && method.Instructions[i - 5].NormalizedOpCode == NormalizedByteCode.__getstatic && eic.ClassFile.GetFieldref(method.Instructions[i - 5].Arg1).Signature == "Lsun.misc.Unsafe;") + if (MatchInvokeVirtual(eic, ref method.Instructions[i - 1], "sun.misc.Unsafe", "objectFieldOffset", "(Ljava.lang.reflect.Field;)J") && + MatchInvokeVirtual(eic, ref method.Instructions[i - 2], "java.lang.Class", "getDeclaredField", "(Ljava.lang.String;)Ljava.lang.reflect.Field;") && + MatchLdc(eic, ref method.Instructions[i - 3], ClassFile.ConstantType.String) && + (method.Instructions[i - 4].NormalizedOpCode == NormalizedByteCode.__aload || method.Instructions[i - 4].NormalizedOpCode == NormalizedByteCode.__ldc) && + method.Instructions[i - 5].NormalizedOpCode == NormalizedByteCode.__getstatic && eic.ClassFile.GetFieldref(method.Instructions[i - 5].Arg1).Signature == "Lsun.misc.Unsafe;") { if (method.Instructions[i - 4].NormalizedOpCode == NormalizedByteCode.__ldc) { if (eic.ClassFile.GetConstantPoolClassType(method.Instructions[i - 4].Arg1) == eic.Caller.DeclaringType) { - string fieldName = eic.ClassFile.GetConstantPoolConstantString(method.Instructions[i - 3].Arg1); + var fieldName = eic.ClassFile.GetConstantPoolConstantString(method.Instructions[i - 3].Arg1); FieldWrapper fw = null; - foreach (FieldWrapper fw1 in eic.Caller.DeclaringType.GetFields()) + foreach (var fw1 in eic.Caller.DeclaringType.GetFields()) { if (fw1.Name == fieldName) { @@ -1141,21 +1136,24 @@ private static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.C } } } + return fw; } + return null; } + // search backward for the astore that corresponds to the aload (of the class object) for (int j = i - 6; j > 0; j--) { - if (method.Instructions[j].NormalizedOpCode == NormalizedByteCode.__astore - && method.Instructions[j].Arg1 == method.Instructions[i - 4].Arg1 - && MatchLdc(eic, ref method.Instructions[j - 1], ClassFile.ConstantType.Class) - && eic.ClassFile.GetConstantPoolClassType(method.Instructions[j - 1].Arg1) == eic.Caller.DeclaringType) + if (method.Instructions[j].NormalizedOpCode == NormalizedByteCode.__astore && + method.Instructions[j].Arg1 == method.Instructions[i - 4].Arg1 && + MatchLdc(eic, ref method.Instructions[j - 1], ClassFile.ConstantType.Class) && + eic.ClassFile.GetConstantPoolClassType(method.Instructions[j - 1].Arg1) == eic.Caller.DeclaringType) { - string fieldName = eic.ClassFile.GetConstantPoolConstantString(method.Instructions[i - 3].Arg1); + var fieldName = eic.ClassFile.GetConstantPoolConstantString(method.Instructions[i - 3].Arg1); FieldWrapper fw = null; - foreach (FieldWrapper fw1 in eic.Caller.DeclaringType.GetFields()) + foreach (var fw1 in eic.Caller.DeclaringType.GetFields()) { if (fw1.Name == fieldName) { @@ -1170,46 +1168,50 @@ private static FieldWrapper GetUnsafeField(EmitIntrinsicContext eic, ClassFile.C } } } + return fw; } } + break; } } } + break; } } } + return null; } - private static bool MatchInvokeVirtual(EmitIntrinsicContext eic, ref Instruction instr, string clazz, string name, string sig) + static bool MatchInvokeVirtual(EmitIntrinsicContext eic, ref Instruction instr, string clazz, string name, string sig) { return MatchInvoke(eic, ref instr, NormalizedByteCode.__invokevirtual, clazz, name, sig); } - private static bool MatchInvokeStatic(EmitIntrinsicContext eic, int offset, string clazz, string name, string sig) + static bool MatchInvokeStatic(EmitIntrinsicContext eic, int offset, string clazz, string name, string sig) { return MatchInvoke(eic, ref eic.Code[eic.OpcodeIndex + offset], NormalizedByteCode.__invokestatic, clazz, name, sig); } - private static bool MatchInvoke(EmitIntrinsicContext eic, ref Instruction instr, NormalizedByteCode opcode, string clazz, string name, string sig) + static bool MatchInvoke(EmitIntrinsicContext eic, ref Instruction instr, NormalizedByteCode opcode, string clazz, string name, string sig) { if (instr.NormalizedOpCode == opcode) { - ClassFile.ConstantPoolItemMI method = eic.ClassFile.GetMethodref(instr.Arg1); - return method.Class == clazz - && method.Name == name - && method.Signature == sig; + var method = eic.ClassFile.GetMethodref(instr.Arg1); + return method.Class == clazz && method.Name == name && method.Signature == sig; } + return false; } - private static bool MatchLdc(EmitIntrinsicContext eic, ref Instruction instr, ClassFile.ConstantType constantType) + static bool MatchLdc(EmitIntrinsicContext eic, ref Instruction instr, ClassFile.ConstantType constantType) { - return (instr.NormalizedOpCode == NormalizedByteCode.__ldc || instr.NormalizedOpCode == NormalizedByteCode.__ldc_nothrow) - && eic.ClassFile.GetConstantPoolConstantType(instr.NormalizedArg1) == constantType; + return (instr.NormalizedOpCode == NormalizedByteCode.__ldc || instr.NormalizedOpCode == NormalizedByteCode.__ldc_nothrow) && eic.ClassFile.GetConstantPoolConstantType(instr.NormalizedArg1) == constantType; } + } + } diff --git a/src/IKVM.Runtime/tracer.cs b/src/IKVM.Runtime/tracer.cs index 107cb4926a..f55baffabe 100644 --- a/src/IKVM.Runtime/tracer.cs +++ b/src/IKVM.Runtime/tracer.cs @@ -22,201 +22,204 @@ Jeroen Frijters */ using System; -using System.Threading; -using System.Diagnostics; using System.Collections.Generic; -using System.Text.RegularExpressions; using System.Configuration; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Threading; namespace IKVM.Internal { - public static class Tracer - { - public readonly static TraceSwitch Compiler = new TraceSwitch("compiler", "Static Compiler"); - public readonly static TraceSwitch FxBug = new TraceSwitch("fxbug", ".NET Framework bug related events"); - public readonly static TraceSwitch ClassLoading = new TraceSwitch("classloading", "Class loading"); - public readonly static TraceSwitch Verifier = new TraceSwitch("verifier", "Bytecode Verifier"); - public readonly static TraceSwitch Runtime = new TraceSwitch("runtime", "Miscellaneous runtime events"); - public readonly static TraceSwitch Jni = new TraceSwitch("jni", "JNI"); - // public readonly static TraceSwitch Methods = new TraceSwitch("methods", "Method Trace"); - private readonly static Dictionary allTraceSwitches = new Dictionary(); - - private readonly static List methodtraces = new List(); - - private sealed class MyTextWriterTraceListener : TextWriterTraceListener - { - internal MyTextWriterTraceListener(System.IO.TextWriter tw) - : base(tw) - { - } - - public override void Fail(string message) - { - this.WriteLine("Assert.Fail: " + message); - this.WriteLine(new StackTrace(true).ToString()); - base.Fail(message); - } - - public override void Fail(string message, string detailMessage) - { - this.WriteLine("Assert.Fail: " + message + ".\n" + detailMessage); - this.WriteLine(new StackTrace(true).ToString()); - base.Fail(message, detailMessage); - } - } - - static Tracer() - { - try - { - Init(); - } - catch(System.Security.SecurityException) - { - } - } - - private static void Init() - { - allTraceSwitches[Compiler.DisplayName] = Compiler; - allTraceSwitches[FxBug.DisplayName] = FxBug; - allTraceSwitches[ClassLoading.DisplayName] = ClassLoading; - allTraceSwitches[Verifier.DisplayName] = Verifier; - allTraceSwitches[Runtime.DisplayName] = Runtime; - allTraceSwitches[Jni.DisplayName] = Jni; - - try - { - Trace.AutoFlush = true; -#if !STUB_GENERATOR - /* If the app config file gives some method trace - add it */ - string trace = ConfigurationManager.AppSettings["Traced Methods"]; - if(trace != null) - { - methodtraces.Add(trace); - } + + public static class Tracer + { + + public readonly static TraceSwitch Compiler = new TraceSwitch("compiler", "Static Compiler"); + public readonly static TraceSwitch FxBug = new TraceSwitch("fxbug", ".NET Framework bug related events"); + public readonly static TraceSwitch ClassLoading = new TraceSwitch("classloading", "Class loading"); + public readonly static TraceSwitch Verifier = new TraceSwitch("verifier", "Bytecode Verifier"); + public readonly static TraceSwitch Runtime = new TraceSwitch("runtime", "Miscellaneous runtime events"); + public readonly static TraceSwitch Jni = new TraceSwitch("jni", "JNI"); + + readonly static Dictionary allTraceSwitches = new Dictionary(); + readonly static List methodtraces = new List(); + + sealed class MyTextWriterTraceListener : TextWriterTraceListener + { + + /// + /// Initializes a new instance. + /// + /// + internal MyTextWriterTraceListener(System.IO.TextWriter tw) : + base(tw) + { + + } + + public override void Fail(string message) + { + WriteLine($"Assert.Fail: {message}"); + WriteLine(new StackTrace(true).ToString()); + base.Fail(message); + } + + public override void Fail(string message, string detailMessage) + { + WriteLine($"Assert.Fail: {message}.\n{detailMessage}"); + WriteLine(new StackTrace(true).ToString()); + base.Fail(message, detailMessage); + } + + } + + /// + /// Initializes the static instance. + /// + static Tracer() + { + try + { + Init(); + } + catch (System.Security.SecurityException) + { + + } + } + + private static void Init() + { + allTraceSwitches[Compiler.DisplayName] = Compiler; + allTraceSwitches[FxBug.DisplayName] = FxBug; + allTraceSwitches[ClassLoading.DisplayName] = ClassLoading; + allTraceSwitches[Verifier.DisplayName] = Verifier; + allTraceSwitches[Runtime.DisplayName] = Runtime; + allTraceSwitches[Jni.DisplayName] = Jni; + +#if NETFRAMEWORK + + try + { + Trace.AutoFlush = true; + +#if !EXPORTER + /* If the app config file gives some method trace - add it */ + var trace = ConfigurationManager.AppSettings["Traced Methods"]; + if (trace != null) + methodtraces.Add(trace); #endif - } - catch(ConfigurationException) - { - // app.config is malformed, ignore - } - } - - [Conditional("DEBUG")] - public static void EnableTraceForDebug() - { - SetTraceLevel("*", TraceLevel.Verbose); - } - - public static void EnableTraceConsoleListener() - { - Trace.Listeners.Add(new MyTextWriterTraceListener(Console.Error)); - } - - public static void HandleMethodTrace(string name) - { - methodtraces.Add(name); - } - - public static bool IsTracedMethod(string name) - { - if(methodtraces.Count == 0) - { - return false; - } - IEnumerator e = methodtraces.GetEnumerator(); - while(e.MoveNext()) - { - try - { - if(Regex.IsMatch(name, e.Current)) - { - return true; - } - } - catch(Exception x) - { - Tracer.Warning(Tracer.Compiler,"Regular Expression match failure: " + e.Current + ":" + x); - } - } - return false; - } - - public static void SetTraceLevel(string name) - { - string[] trace = name.Split('='); - System.Diagnostics.TraceLevel level = System.Diagnostics.TraceLevel.Verbose; - if(trace.Length == 2) - { - level = (System.Diagnostics.TraceLevel)Enum.Parse(typeof(System.Diagnostics.TraceLevel), trace[1], true); - } - SetTraceLevel(trace[0], level); - } - - public static void SetTraceLevel(string name, TraceLevel level) - { - if(name == "*") - { - foreach(TraceSwitch ts in allTraceSwitches.Values) - { - ts.Level = level; - } - } - else - { - TraceSwitch ts; - if(allTraceSwitches.TryGetValue(name, out ts)) - { - ts.Level = level; - } - else - { - Console.Error.WriteLine("Warning: Invalid traceswitch name: {0}", name); - } - } - } - - private static void WriteLine(string message, object[] p) - { - if(p.Length > 0) - { - message = string.Format(message, p); - } - Trace.WriteLine(string.Format("[{0:HH':'mm':'ss'.'fffff} {1}] {2}", DateTime.Now, Thread.CurrentThread.Name, message)); - } - - [Conditional("TRACE")] - public static void Info(TraceSwitch traceSwitch, string message, params object[] p) - { - if(traceSwitch.TraceInfo) - { - WriteLine(message, p); - } - } - - [Conditional("TRACE")] - public static void MethodInfo(string message) - { - Trace.WriteLine(string.Format("[{0:HH':'mm':'ss'.'fffff} {1}] {2}", DateTime.Now, Thread.CurrentThread.Name, message)); - } - - [Conditional("TRACE")] - public static void Warning(TraceSwitch traceSwitch, string message, params object[] p) - { - if(traceSwitch.TraceWarning) - { - WriteLine(message, p); - } - } - - [Conditional("TRACE")] - public static void Error(TraceSwitch traceSwitch, string message, params object[] p) - { - if(traceSwitch.TraceError) - { - WriteLine(message, p); - } - } - } + } + catch (ConfigurationException) + { + // app.config is malformed, ignore + } +#endif + } + + [Conditional("DEBUG")] + public static void EnableTraceForDebug() + { + SetTraceLevel("*", TraceLevel.Verbose); + } + + /// + /// Enables trace output to to the console. + /// + public static void EnableTraceConsoleListener() + { + //Trace.Listeners.Add(new MyTextWriterTraceListener(Console.Error)); + } + + public static void HandleMethodTrace(string name) + { + methodtraces.Add(name); + } + + public static bool IsTracedMethod(string name) + { + foreach (var trace in methodtraces) + { + try + { + if (Regex.IsMatch(name, trace)) + return true; + } + catch (Exception x) + { + Tracer.Warning(Tracer.Compiler, $"Regular Expression match failure: {trace}:{x}"); + } + } + + return false; + } + + public static void SetTraceLevel(string name) + { + var trace = name.Split('='); + var level = TraceLevel.Verbose; + + if (trace.Length == 2) + level = (TraceLevel)Enum.Parse(typeof(TraceLevel), trace[1], true); + + SetTraceLevel(trace[0], level); + } + + public static void SetTraceLevel(string name, TraceLevel level) + { + if (name == "*") + { + foreach (var ts in allTraceSwitches.Values) + ts.Level = level; + } + else + { + if (allTraceSwitches.TryGetValue(name, out var ts)) + { + ts.Level = level; + } + else + { + Console.Error.WriteLine("Warning: Invalid traceswitch name: {0}", name); + } + } + } + + static void WriteLine(string message, object[] p) + { + if (p.Length > 0) + message = string.Format(message, p); + + Trace.WriteLine($"[{DateTime.Now:HH':'mm':'ss'.'fffff} {Thread.CurrentThread.Name}] {message}"); + } + + [Conditional("TRACE")] + public static void Info(TraceSwitch traceSwitch, string message, params object[] p) + { + if (traceSwitch.TraceInfo) + WriteLine(message, p); + } + + [Conditional("TRACE")] + public static void MethodInfo(string message) + { + Trace.WriteLine($"[{DateTime.Now:HH':'mm':'ss'.'fffff} {Thread.CurrentThread.Name}] {message}"); + } + + [Conditional("TRACE")] + public static void Warning(TraceSwitch traceSwitch, string message, params object[] p) + { + if (traceSwitch.TraceWarning) + WriteLine(message, p); + } + + [Conditional("TRACE")] + public static void Error(TraceSwitch traceSwitch, string message, params object[] p) + { + if (traceSwitch.TraceError) + WriteLine(message, p); + } + + } + } diff --git a/src/IKVM.Runtime/verifier.cs b/src/IKVM.Runtime/verifier.cs index 3c125fb90b..c60a838995 100644 --- a/src/IKVM.Runtime/verifier.cs +++ b/src/IKVM.Runtime/verifier.cs @@ -25,15 +25,22 @@ Jeroen Frijters using System.Collections.Generic; using System.Diagnostics; +using IKVM.ByteCode; using IKVM.Internal; +#if IMPORTER +using IKVM.Tools.Importer; +#endif + using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry; using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags; sealed class InstructionState { + private struct LocalStoreSites { + private int[] data; private int count; private bool shared; @@ -102,12 +109,14 @@ internal static void MarkShared(LocalStoreSites[] localStoreSites) } } } - private TypeWrapper[] stack; - private int stackSize; - private int stackEnd; - private TypeWrapper[] locals; - private bool unitializedThis; + + TypeWrapper[] stack; + int stackSize; + int stackEnd; + TypeWrapper[] locals; + bool unitializedThis; internal bool changed = true; + private enum ShareFlags : byte { None = 0, @@ -409,7 +418,7 @@ private static TypeWrapper FindCommonBaseTypeHelper(TypeWrapper t1, TypeWrapper private static bool HasMissingBaseType(Stack st) { -#if STATIC_COMPILER +#if IMPORTER if (st.Pop().IsUnloadable) { // we have a missing type in base class hierarchy @@ -801,7 +810,7 @@ internal static TypeWrapper PopTypeImpl(TypeWrapper baseType, TypeWrapper type) private static bool HasMissingBaseType(TypeWrapper tw) { -#if STATIC_COMPILER +#if IMPORTER for (TypeWrapper baseTypeWrapper; (baseTypeWrapper = tw.BaseTypeWrapper) != null; tw = baseTypeWrapper) { if (baseTypeWrapper.IsUnloadable) @@ -2587,7 +2596,7 @@ private static void OptimizationPass(CodeInfo codeInfo, ClassFile classFile, Cla && instructions[i + 1].TargetIndex > i && (flags[i + 1] & InstructionFlags.BranchTarget) == 0) { - ConstantFieldWrapper field = classFile.GetFieldref(instructions[i].Arg1).GetField() as ConstantFieldWrapper; + var field = classFile.GetFieldref(instructions[i].Arg1).GetField() as ConstantFieldWrapper; if (field != null && field.FieldTypeWrapper == PrimitiveTypeWrapper.BOOLEAN && (bool)field.GetConstantValue()) { // We know the branch will always be taken, so we replace the getstatic/ifne by a goto. @@ -2744,23 +2753,22 @@ private void PatchLdcMethodHandle(ref ClassFile.Method.Instruction instr) { SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "tried to access class {0} from class {1}", cpi.Class, wrapper.Name); } - else if (cpi.Kind == ClassFile.RefKind.invokeVirtual + else if (cpi.Kind == ReferenceKind.InvokeVirtual && cpi.GetClassType() == CoreClasses.java.lang.invoke.MethodHandle.Wrapper && (cpi.Name == "invoke" || cpi.Name == "invokeExact")) { // it's allowed to use ldc to create a MethodHandle invoker } - else if (cpi.Member == null - || cpi.Member.IsStatic != (cpi.Kind == ClassFile.RefKind.getStatic || cpi.Kind == ClassFile.RefKind.putStatic || cpi.Kind == ClassFile.RefKind.invokeStatic)) + else if (cpi.Member == null || cpi.Member.IsStatic != (cpi.Kind == ReferenceKind.GetStatic || cpi.Kind == ReferenceKind.PutStatic || cpi.Kind == ReferenceKind.InvokeStatic)) { HardError err; string msg; switch (cpi.Kind) { - case ClassFile.RefKind.getField: - case ClassFile.RefKind.getStatic: - case ClassFile.RefKind.putField: - case ClassFile.RefKind.putStatic: + case ReferenceKind.GetField: + case ReferenceKind.GetStatic: + case ReferenceKind.PutField: + case ReferenceKind.PutStatic: err = HardError.NoSuchFieldError; msg = cpi.Name; break; @@ -3542,7 +3550,7 @@ private void ConditionalPatchNoClassDefFoundError(ref ClassFile.Method.Instructi private void SetHardError(ClassLoaderWrapper classLoader, ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args) { string text = string.Format(message, args); -#if STATIC_COMPILER +#if IMPORTER Message msg; switch (hardError) { @@ -3864,7 +3872,7 @@ private void PatchFieldAccess(TypeWrapper wrapper, MethodWrapper mw, ref ClassFi } if (false && cpi.GetFieldType() != field.FieldTypeWrapper && !cpi.GetFieldType().IsUnloadable & !field.FieldTypeWrapper.IsUnloadable) { -#if STATIC_COMPILER +#if IMPORTER StaticCompiler.LinkageError("Field \"{2}.{3}\" is of type \"{0}\" instead of type \"{1}\" as expected by \"{4}\"", field.FieldTypeWrapper, cpi.GetFieldType(), cpi.GetClassType().Name, cpi.Name, wrapper.Name); #endif SetHardError(wrapper.GetClassLoader(), ref instr, HardError.LinkageError, "Loader constraints violated: {0}.{1}", field.DeclaringType.Name, field.Name); @@ -3930,7 +3938,7 @@ private string CheckLoaderConstraints(ClassFile.ConstantPoolItemMI cpi, MethodWr if (cpi.GetRetType() != mw.ReturnType && cpi.GetRetType().Name != mw.ReturnType.Name && !cpi.GetRetType().IsUnloadable && !mw.ReturnType.IsUnloadable) #endif { -#if STATIC_COMPILER +#if IMPORTER StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a return type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", mw.ReturnType, cpi.GetRetType(), cpi.GetClassType().Name, cpi.Name, cpi.Signature, classFile.Name); #endif return "Loader constraints violated (return type): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature; @@ -3945,7 +3953,7 @@ private string CheckLoaderConstraints(ClassFile.ConstantPoolItemMI cpi, MethodWr if (here[i] != there[i] && here[i].Name != there[i].Name && !here[i].IsUnloadable && !there[i].IsUnloadable) #endif { -#if STATIC_COMPILER +#if IMPORTER StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a argument type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", there[i], here[i], cpi.GetClassType().Name, cpi.Name, cpi.Signature, classFile.Name); #endif return "Loader constraints violated (arg " + i + "): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature; diff --git a/src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj b/src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj new file mode 100644 index 0000000000..a08ebb273c --- /dev/null +++ b/src/IKVM.Tests.Java/IKVM.Tests.Java.msbuildproj @@ -0,0 +1,30 @@ + + + + + + + + + net461;netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java b/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java new file mode 100644 index 0000000000..0722252e0a --- /dev/null +++ b/src/IKVM.Tests.Java/java/lang/ClassLoaderTests.java @@ -0,0 +1,59 @@ +package ikvm.tests.java.java.lang; + +import java.net.*; + +@cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.Annotation() +public class ClassLoaderTests { + + static class Foo extends ClassLoader { + @Override + protected void finalize() { + System.out.println(""); + } + } + + static class Bar extends Foo { + @Override + protected void finalize() { + super.finalize(); + System.out.println(""); + } + } + + static class ShouldPreventUninitializedParentClass { + static ClassLoader loader; + + public static void run() throws Exception { + try { + new ClassLoader(null) { + @Override + protected void finalize() { + loader = this; + } + }; + } catch (SecurityException exc) { + + } + + System.gc(); + System.runFinalization(); + + if (loader != null) { + try { + URLClassLoader child = URLClassLoader.newInstance(new URL[0], loader); + throw new RuntimeException("child class loader created"); + } catch (SecurityException se) { + // test passed + } + } else { + // test passed + } + } + } + + @cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Annotation() + public void shouldPreventUninitializedParent() throws Exception { + ShouldPreventUninitializedParentClass.run(); + } + +} diff --git a/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java b/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java new file mode 100644 index 0000000000..460fe18d80 --- /dev/null +++ b/src/IKVM.Tests.Java/java/nio/channels/AsynchronousServerSocketChannelTests.java @@ -0,0 +1,162 @@ +package ikvm.tests.java.java.nio.channels; + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Random; + +@cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.Annotation() +public class AsynchronousServerSocketChannelTests { + + static final Random rand = new Random(); + + @cli.Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Annotation() + public void canReadAndWriteStreams() throws Exception { + // establish loopback connection + AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), port); + AsynchronousSocketChannel ch1 = AsynchronousSocketChannel.open(); + ch1.connect(isa).get(); + AsynchronousSocketChannel ch2 = listener.accept().get(); + + // start thread to write to stream + Writer writer = new Writer(Channels.newOutputStream(ch1)); + Thread writerThread = new Thread(writer); + writerThread.start(); + + // start thread to read from stream + Reader reader = new Reader(Channels.newInputStream(ch2)); + Thread readerThread = new Thread(reader); + readerThread.start(); + + // wait for threads to complete + writerThread.join(); + readerThread.join(); + + // shutdown listener + listener.close(); + + // check that reader received what we expected + if (reader.total() != writer.total()) + throw new RuntimeException("Unexpected number of bytes read"); + if (reader.hash() != writer.hash()) + throw new RuntimeException("Hash incorrect for bytes read"); + + // channels should be closed + if (ch1.isOpen() || ch2.isOpen()) + throw new RuntimeException("Channels should be closed"); + } + + static class Reader implements Runnable { + + private final InputStream in; + private volatile int total; + private volatile int hash; + + Reader(InputStream in) { + this.in = in; + } + + public void run() { + try { + int n; + do { + // random offset/len + byte[] buf = new byte[128 + rand.nextInt(128)]; + int len, off; + if (rand.nextBoolean()) { + len = buf.length; + off = 0; + n = in.read(buf); + } else { + len = 1 + rand.nextInt(64); + off = rand.nextInt(64); + n = in.read(buf, off, len); + } + if (n > len) + throw new RuntimeException("Too many bytes read"); + if (n > 0) { + total += n; + for (int i=0; i 0); + in.close(); + } catch (IOException x) { + x.printStackTrace(); + } + } + + int total() { + return total; + } + + int hash() { + return hash; + } + + } + + static class Writer implements Runnable { + + private final OutputStream out; + private final int total; + private volatile int hash; + + Writer(OutputStream out) { + this.out = out; + this.total = 50*1000 + rand.nextInt(50*1000); + } + + public void run() { + hash = 0; + int rem = total; + try { + do { + byte[] buf = new byte[1 + rand.nextInt(rem)]; + int off, len; + + // write random bytes + if (rand.nextBoolean()) { + off = 0; + len = buf.length; + } else { + off = rand.nextInt(buf.length); + int r = buf.length - off; + len = (r <= 1) ? 1 : (1 + rand.nextInt(r)); + } + for (int i=0; i 0); + + // close stream when done + out.close(); + + } catch (IOException x) { + x.printStackTrace(); + } + } + + int total() { + return total; + } + + int hash() { + return hash; + } + + } + +} diff --git a/src/IKVM.Tests.Util/IKVM.Tests.Util.csproj b/src/IKVM.Tests.Util/IKVM.Tests.Util.csproj index fc5e08f1ce..8569fca04d 100644 --- a/src/IKVM.Tests.Util/IKVM.Tests.Util.csproj +++ b/src/IKVM.Tests.Util/IKVM.Tests.Util.csproj @@ -1,6 +1,6 @@  - - + + net461;netcoreapp3.1 diff --git a/src/IKVM.Tests.Util/InMemoryCompiler.cs b/src/IKVM.Tests.Util/InMemoryCompiler.cs index a7505b45e9..f1d3d2d106 100644 --- a/src/IKVM.Tests.Util/InMemoryCompiler.cs +++ b/src/IKVM.Tests.Util/InMemoryCompiler.cs @@ -2,6 +2,12 @@ using System.Collections.Concurrent; using System.IO; +using java.io; +using java.lang; +using java.util; + +using javax.tools; + namespace IKVM.Tests.Util { @@ -11,31 +17,29 @@ namespace IKVM.Tests.Util public class InMemoryCompiler { - readonly ConcurrentDictionary classes = new(); + readonly ConcurrentDictionary streams = new(); /// /// Captures the output files from the compilation process. /// - class InMemoryForwardingJavaFileManager : global::javax.tools.ForwardingJavaFileManager + class InMemoryForwardingJavaFileManager : ForwardingJavaFileManager { - readonly ConcurrentDictionary classes; - /// /// Class loader which reads from the output class files. /// - class InMemoryForwardingJavaFileManagerClassLoader : global::java.lang.ClassLoader + class InMemoryForwardingJavaFileManagerClassLoader : ClassLoader { - readonly ConcurrentDictionary classes; + readonly ConcurrentDictionary streams; /// /// Initializes a new instance. /// - /// - public InMemoryForwardingJavaFileManagerClassLoader(ConcurrentDictionary classes) + /// + public InMemoryForwardingJavaFileManagerClassLoader(ConcurrentDictionary streams) { - this.classes = classes ?? throw new ArgumentNullException(nameof(classes)); + this.streams = streams ?? throw new ArgumentNullException(nameof(streams)); } /// @@ -43,12 +47,12 @@ public InMemoryForwardingJavaFileManagerClassLoader(ConcurrentDictionary /// /// - protected override global::java.lang.Class findClass(string className) + protected override Class findClass(string className) { - if (classes.TryGetValue(className, out var bos)) + if (streams.TryGetValue(className, out var stream)) { - var b = bos.toByteArray(); - return base.defineClass(className, b, 0, b.Length); + var b = stream.toByteArray(); + return defineClass(className, b, 0, b.Length); } return null; @@ -59,20 +63,20 @@ public InMemoryForwardingJavaFileManagerClassLoader(ConcurrentDictionary /// Represents an output file to be written into. /// - class InMemoryClassFileObject : global::javax.tools.SimpleJavaFileObject + class InMemoryClassFileObject : SimpleJavaFileObject { - readonly ConcurrentDictionary classes; + readonly ConcurrentDictionary streams; readonly string className; /// /// Initializes a new instance. /// - /// - public InMemoryClassFileObject(ConcurrentDictionary files, string className, global::javax.tools.JavaFileObject.Kind kind) : - base(global::java.net.URI.create("string:///" + className.Replace('.', '/') + kind), kind) + /// + public InMemoryClassFileObject(ConcurrentDictionary streams, string className, JavaFileObject.Kind kind) : + base(java.net.URI.create("string:///" + className.Replace('.', '/') + kind), kind) { - this.classes = files ?? throw new ArgumentNullException(nameof(files)); + this.streams = streams ?? throw new ArgumentNullException(nameof(streams)); this.className = className ?? throw new ArgumentNullException(nameof(className)); } @@ -80,22 +84,26 @@ public InMemoryClassFileObject(ConcurrentDictionary /// - public override global::java.io.OutputStream openOutputStream() + public override OutputStream openOutputStream() { - return classes.GetOrAdd(className, _ => new global::java.io.ByteArrayOutputStream()); + return streams.GetOrAdd(className, _ => new ByteArrayOutputStream()); } } + readonly ConcurrentDictionary streams; + readonly ClassLoader classLoader; + /// /// Initializes a new instance. /// /// - /// - public InMemoryForwardingJavaFileManager(global::javax.tools.JavaFileManager manager, ConcurrentDictionary classes) : + /// + public InMemoryForwardingJavaFileManager(JavaFileManager manager, ConcurrentDictionary streams) : base(manager) { - this.classes = classes ?? throw new ArgumentNullException(nameof(classes)); + this.streams = streams ?? throw new ArgumentNullException(nameof(streams)); + this.classLoader = new InMemoryForwardingJavaFileManagerClassLoader(streams); } /// @@ -103,9 +111,9 @@ public InMemoryForwardingJavaFileManager(global::javax.tools.JavaFileManager man /// /// /// - public override global::java.lang.ClassLoader getClassLoader(global::javax.tools.JavaFileManager.Location location) + public override ClassLoader getClassLoader(JavaFileManager.Location location) { - return new InMemoryForwardingJavaFileManagerClassLoader(classes); + return classLoader; } /// @@ -116,9 +124,9 @@ public InMemoryForwardingJavaFileManager(global::javax.tools.JavaFileManager man /// /// /// - public override global::javax.tools.JavaFileObject getJavaFileForOutput(global::javax.tools.JavaFileManager.Location location, string className, global::javax.tools.JavaFileObject.Kind kind, global::javax.tools.FileObject sibling) + public override JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, string className, JavaFileObject.Kind kind, FileObject sibling) { - return new InMemoryClassFileObject(classes, className, kind); + return new InMemoryClassFileObject(streams, className, kind); } } @@ -126,7 +134,7 @@ public InMemoryForwardingJavaFileManager(global::javax.tools.JavaFileManager man /// /// Represents an input file to be read from. /// - class InMemorySourceFileObject : global::javax.tools.SimpleJavaFileObject + class InMemorySourceFileObject : SimpleJavaFileObject { readonly InMemoryCodeUnit unit; @@ -135,8 +143,8 @@ class InMemorySourceFileObject : global::javax.tools.SimpleJavaFileObject /// Initializes a new instance. /// /// - public InMemorySourceFileObject(InMemoryCodeUnit unit, global::java.net.URI uri) : - base(uri, global::javax.tools.JavaFileObject.Kind.SOURCE) + public InMemorySourceFileObject(InMemoryCodeUnit unit, java.net.URI uri) : + base(uri, JavaFileObject.Kind.SOURCE) { this.unit = unit ?? throw new ArgumentNullException(nameof(unit)); } @@ -146,7 +154,7 @@ public InMemorySourceFileObject(InMemoryCodeUnit unit, global::java.net.URI uri) /// /// /// - public override global::java.lang.CharSequence getCharContent(bool ignoreEncodingErrors) + public override CharSequence getCharContent(bool ignoreEncodingErrors) { return unit.Code; } @@ -154,8 +162,8 @@ public InMemorySourceFileObject(InMemoryCodeUnit unit, global::java.net.URI uri) } readonly InMemoryCodeUnit[] units; - readonly global::javax.tools.JavaFileManager files; - readonly global::javax.tools.JavaCompiler compiler; + readonly JavaFileManager files; + readonly JavaCompiler compiler; /// /// Initializes a new instance. @@ -165,10 +173,10 @@ public InMemorySourceFileObject(InMemoryCodeUnit unit, global::java.net.URI uri) /// public InMemoryCompiler(InMemoryCodeUnit[] source) { - this.compiler = global::javax.tools.ToolProvider.getSystemJavaCompiler() ?? throw new Exception(); + this.compiler = ToolProvider.getSystemJavaCompiler() ?? throw new System.Exception(); this.units = source ?? throw new ArgumentNullException(nameof(source)); - this.files = new InMemoryForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null), classes); + this.files = new InMemoryForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null), streams); } /// @@ -177,10 +185,10 @@ public InMemoryCompiler(InMemoryCodeUnit[] source) public void Compile() { // add each unit as a source item - var l = new global::java.util.ArrayList(); + var l = new java.util.ArrayList(); foreach (var unit in units) { - var uri = global::java.net.URI.create("string:///" + unit.Name.Replace('.', '/') + global::javax.tools.JavaFileObject.Kind.SOURCE.extension); + var uri = java.net.URI.create("string:///" + unit.Name.Replace('.', '/') + JavaFileObject.Kind.SOURCE.extension); var src = new InMemorySourceFileObject(unit, uri); l.add(src); } @@ -188,10 +196,21 @@ public void Compile() // get compiler and invoke if (l.size() > 0) { - var d = new global::javax.tools.DiagnosticCollector(); - var s = compiler.getTask(null, files, d, null, null, l).call(); + var o = new ArrayList(); + o.add("-verbose"); + o.add("-g"); + + var d = new DiagnosticCollector(); + var s = compiler.getTask(null, files, d, o, null, l).call(); if (s.booleanValue() == false) - throw new Exception(); + { + var m = new StringBuilder(); + var a = d.getDiagnostics(); + for (int i = 0; i < a.size(); i++) + m.append(((Diagnostic)a.get(i)).getMessage(Locale.US)); + + throw new System.Exception(m.toString()); + } } } @@ -200,13 +219,13 @@ public void Compile() /// /// /// - /// - public global::java.lang.Class GetClass(string className) + /// + public Class GetClass(string className) { var cld = files.getClassLoader(null); var cls = cld.loadClass(className); if (cls == null) - throw new global::java.lang.ClassNotFoundException("Class returned by ClassLoader was null!"); + throw new ClassNotFoundException("Class returned by ClassLoader was null!"); return cls; } @@ -217,7 +236,7 @@ public void Compile() /// public void WriteJar(string path) { - using var jar = File.OpenWrite(path); + using var jar = System.IO.File.OpenWrite(path); WriteJar(jar); } @@ -228,7 +247,7 @@ public void WriteJar(string path) public void WriteJar(Stream stream) { // write to temporary stream - var buf = new global::java.io.ByteArrayOutputStream(); + var buf = new ByteArrayOutputStream(); WriteJar(buf); stream.Write(buf.toByteArray(), 0, buf.size()); } @@ -237,11 +256,11 @@ public void WriteJar(Stream stream) /// Writes the compiled classes to a JAR on the given stream. /// /// - public void WriteJar(global::java.io.OutputStream stream) + public void WriteJar(OutputStream stream) { - var man = new global::java.util.jar.Manifest(); - man.getMainAttributes().put(global::java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0"); - var jar = new global::java.util.jar.JarOutputStream(stream, man); + var man = new java.util.jar.Manifest(); + man.getMainAttributes().put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0"); + var jar = new java.util.jar.JarOutputStream(stream, man); WriteJar(jar); jar.close(); } @@ -250,11 +269,11 @@ public void WriteJar(global::java.io.OutputStream stream) /// Writes the compiled classes to a JAR. /// /// - public void WriteJar(global::java.util.jar.JarOutputStream jar) + public void WriteJar(java.util.jar.JarOutputStream jar) { - foreach (var i in classes) + foreach (var i in streams) { - var e = new global::java.util.jar.JarEntry(i.Key.Replace(".", "/") + ".class"); + var e = new java.util.jar.JarEntry(i.Key.Replace(".", "/") + ".class"); jar.putNextEntry(e); i.Value.writeTo(jar); jar.closeEntry(); diff --git a/src/IKVM.Tests/IKVM.Tests.csproj b/src/IKVM.Tests/IKVM.Tests.csproj index 964742a1aa..9765a62d2f 100644 --- a/src/IKVM.Tests/IKVM.Tests.csproj +++ b/src/IKVM.Tests/IKVM.Tests.csproj @@ -1,40 +1,39 @@  - - + + net461;netcoreapp3.1;net6.0 true true false + 11 - - + - - + - + PreserveNewest - + - + @@ -81,61 +80,72 @@ - + TargetFramework=net461 RuntimeIdentifier=win7-x64 ikvmc\net461\win7-x64 - + TargetFramework=net461 RuntimeIdentifier=win7-x64 ikvmstub\net461\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=win7-x64 ikvmc\netcoreapp3.1\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=win7-x64 ikvmstub\netcoreapp3.1\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-x64 ikvmc\netcoreapp3.1\linux-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-x64 ikvmstub\netcoreapp3.1\linux-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm ikvmc\netcoreapp3.1\linux-arm - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm ikvmstub\netcoreapp3.1\linux-arm - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 ikvmc\netcoreapp3.1\linux-arm64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-arm64 ikvmstub\netcoreapp3.1\linux-arm64 + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmc\netcoreapp3.1\osx-x64 + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmstub\netcoreapp3.1\osx-x64 + + diff --git a/src/IKVM.Tests/JNI/JniTests.cs b/src/IKVM.Tests/JNI/JniTests.cs index 33d7c7f5c5..1ae1930a63 100644 --- a/src/IKVM.Tests/JNI/JniTests.cs +++ b/src/IKVM.Tests/JNI/JniTests.cs @@ -226,7 +226,7 @@ public void GetDoubleField() class StaticTestObject { - public static object nullObjectField; + public static object nullObjectField = null; public static object objectField; public static string stringField; public static bool booleanField; diff --git a/src/IKVM.Tests/Java/com/sun/management/OperatingSystemMXBeanTests.cs b/src/IKVM.Tests/Java/com/sun/management/OperatingSystemMXBeanTests.cs new file mode 100644 index 0000000000..48404fd5f8 --- /dev/null +++ b/src/IKVM.Tests/Java/com/sun/management/OperatingSystemMXBeanTests.cs @@ -0,0 +1,83 @@ +using System.Runtime.InteropServices; + +using FluentAssertions; + +using java.lang.management; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.com.sun.management +{ + + [TestClass] + public class OperatingSystemMXBeanTests + { + + static readonly global::com.sun.management.OperatingSystemMXBean mbean = (global::com.sun.management.OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean(); + + [TestMethod] + public void CanGetArch() + { + mbean.getArch().Should().NotBeNullOrEmpty(); + } + + [TestMethod] + public void CanGetAvailableProcessors() + { + mbean.getAvailableProcessors().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetProcessCpuLoad() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + mbean.getProcessCpuLoad().Should().Be(-1); + } + + [TestMethod] + public void CanGetProcessCpuTime() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + mbean.getProcessCpuTime().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetSystemLoadAverage() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + mbean.getProcessCpuLoad().Should().Be(-1); + } + + [TestMethod] + public void CanGetTotalPhysicalMemorySize() + { + mbean.getTotalPhysicalMemorySize().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetFreePhysicalMemorySize() + { + mbean.getFreePhysicalMemorySize().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetTotalSwapSpaceSize() + { + mbean.getTotalSwapSpaceSize().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetFreeSwapSpaceSize() + { + mbean.getFreeSwapSpaceSize().Should().BeGreaterOrEqualTo(1); + } + + [TestMethod] + public void CanGetCommittedVirtualMemorySize() + { + mbean.getCommittedVirtualMemorySize().Should().BeGreaterOrEqualTo(1); + } + + } + +} diff --git a/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs b/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs index 33d6e343fd..a5093a7f16 100644 --- a/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs +++ b/src/IKVM.Tests/Java/ikvm/runtime/AssemblyClassLoaderTests.cs @@ -52,7 +52,7 @@ public async Task Setup() { Runtime = Path.Combine("lib", tfm, "IKVM.Runtime.dll"), ResponseFile = $"{n}_ikvmc.rsp", - Input = { "helloworld-2.0.jar" }, + Input = { Path.Combine("ext", "helloworld-2.0.jar") }, Assembly = $"helloworld_{n}", Version = "1.0.0.0", NoStdLib = true, diff --git a/src/IKVM.Tests/Java/ikvm/util/EnumeratorIteratorTests.cs b/src/IKVM.Tests/Java/ikvm/util/EnumeratorIteratorTests.cs new file mode 100644 index 0000000000..f94c85084c --- /dev/null +++ b/src/IKVM.Tests/Java/ikvm/util/EnumeratorIteratorTests.cs @@ -0,0 +1,32 @@ +using FluentAssertions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.ikvm.util +{ + + [TestClass] + public class EnumeratorIteratorTests + { + + [TestMethod] + public void CanIterate() + { + var a = new[] { 1, 2, 3, 4 }; + var i = new global::ikvm.util.EnumeratorIterator(a.GetEnumerator()); + + var t = 0; + var c = 0; + while (i.hasNext()) + { + c++; + t += (int)i.next(); + } + + c.Should().Be(4); + t.Should().Be(1 + 2 + 3 + 4); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/io/FileTests.cs b/src/IKVM.Tests/Java/java/io/FileTests.cs index 4155c77b13..c5fc5f3dbc 100644 --- a/src/IKVM.Tests/Java/java/io/FileTests.cs +++ b/src/IKVM.Tests/Java/java/io/FileTests.cs @@ -13,14 +13,14 @@ public class FileTests { [TestMethod] - public void Can_create_file() + public void CanCreateFile() { var f = new global::java.io.File(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); f.createNewFile().Should().BeTrue(); } [TestMethod] - public void Can_write_file() + public void CanWriteFile() { var w = new global::java.io.FileWriter("test.txt"); w.write("TEST"); @@ -28,7 +28,7 @@ public void Can_write_file() } [TestMethod] - public void Can_read_file() + public void CanReadFile() { var w = new global::java.io.FileWriter("test.txt"); w.write("TEST"); @@ -60,6 +60,15 @@ public void ShouldRemoveDotDotDotFromCanonicalizedPath() new global::java.io.File(@"/usr/../usr/lib").getCanonicalPath().Should().Be(@"/usr/lib"); } + [TestMethod] + public void CanGetPathSeperator() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + global::java.io.File.pathSeparator.Should().Be(";"); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + global::java.io.File.pathSeparator.Should().Be(":"); + } + } } diff --git a/src/IKVM.Tests/Java/java/io/RandomAccessFileTests.cs b/src/IKVM.Tests/Java/java/io/RandomAccessFileTests.cs new file mode 100644 index 0000000000..9c93f25d35 --- /dev/null +++ b/src/IKVM.Tests/Java/java/io/RandomAccessFileTests.cs @@ -0,0 +1,42 @@ +using System.Security.Cryptography; + +using java.io; +using java.lang; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.io +{ + + [TestClass] + public class RandomAccessFileTests + { + + [TestMethod] + public void ShouldReturnNegativeOneAtEof() + { + // generate temporary file + var p = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName()); + var c = new byte[1024]; + RandomNumberGenerator.Create().GetBytes(c); + System.IO.File.WriteAllBytes(p, c); + + var buf = new byte[128]; + int n; + using var raf = new RandomAccessFile(new File(p), "r"); + + // read until end + while (true) + { + n = raf.read(buf, 0, buf.Length); + if (n <= 0) + break; + } + + if (n != -1) + throw new RuntimeException("Expected -1 for EOF, got " + n); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/io/SerializableTests.cs b/src/IKVM.Tests/Java/java/io/SerializableTests.cs new file mode 100644 index 0000000000..99845b832b --- /dev/null +++ b/src/IKVM.Tests/Java/java/io/SerializableTests.cs @@ -0,0 +1,62 @@ +using IKVM.Tests.Util; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.io +{ + + [TestClass] + public class SerializableTests + { + + [TestMethod] + public void CanSerializeNonPublicInterfaceDynamic() + { + var source = """ + +import java.io.*; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; + +public class NonPublicInterface { + + static class Handler implements InvocationHandler, Serializable { + public Object invoke(Object obj, Method meth, Object[] args) { + return null; + } + } + + public static final String nonPublicInterfaceName = "java.util.zip.ZipConstants"; + + public void main() throws Exception { + Class nonPublic = Class.forName(nonPublicInterfaceName); + if (Modifier.isPublic(nonPublic.getModifiers())) { + throw new Error("Interface " + nonPublicInterfaceName + " is public and need to be changed!"); + } + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream oout = new ObjectOutputStream(bout); + oout.writeObject(Proxy.newProxyInstance(nonPublic.getClassLoader(), new Class[]{ nonPublic }, new Handler())); + oout.close(); + ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); + oin.readObject(); + } +} + +"""; + var unit = new InMemoryCodeUnit("NonPublicInterface", source); + var compiler = new InMemoryCompiler(new[] { unit }); + compiler.Compile(); + + // create an instance of the test class + var clazz = compiler.GetClass("NonPublicInterface"); + var ctor = clazz.getConstructor(); + var test = (dynamic)ctor.newInstance(System.Array.Empty()); + test.main(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/ClassLoaderTests.cs b/src/IKVM.Tests/Java/java/lang/ClassLoaderTests.cs new file mode 100644 index 0000000000..d3cfaa3de4 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ClassLoaderTests.cs @@ -0,0 +1,6 @@ +namespace IKVM.Tests.Java.java.lang +{ + + + +} diff --git a/src/IKVM.Tests/Java/java/lang/ClassTests.cs b/src/IKVM.Tests/Java/java/lang/ClassTests.cs index ce987dc9e4..a86e1a0f44 100644 --- a/src/IKVM.Tests/Java/java/lang/ClassTests.cs +++ b/src/IKVM.Tests/Java/java/lang/ClassTests.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using IKVM.Tests.Util; +using java.nio.file; +using java.util; +using java.util.function; + using Microsoft.VisualStudio.TestTools.UnitTesting; namespace IKVM.Tests.Java.java.lang @@ -18,7 +20,7 @@ public class ClassTests { [TestMethod] - public void Can_get_method_and_invoke() + public void CanGetMethodAndInvoke() { var s = new StreamReader(typeof(ClassTests).Assembly.GetManifestResourceStream("IKVM.Tests.Java.java.lang.ClassTests.java")).ReadToEnd(); var f = new InMemoryCodeUnit("ikvm.tests.java.lang.ClassTests", s); @@ -32,6 +34,50 @@ public void Can_get_method_and_invoke() m.invoke(null, new object[] { new string[] { "TEST" } }); } + [TestMethod] + public void CanSetFieldSetAccessibleOnBase() + { + var bcp = global::java.lang.System.getProperty("sun.boot.class.path").Split(global::java.io.File.pathSeparatorChar); + var all = bcp.SelectMany(i => EnumerateClassFiles(Paths.get(i))).ToList(); + + if (all.Count < 100) + throw new Exception("Expected more classes."); + + foreach (var file in all.Take(256)) + { + if (file.StartsWith("WrapperGenerator")) + continue; + + var c = global::java.lang.Class.forName(file.Replace('/', '.').Substring(0, file.Length - 6), false, null); + if (c == null) + throw new Exception("Could not load BCP class by name."); + + foreach (var f in c.getDeclaredFields()) + { + f.setAccessible(false); + f.setAccessible(true); + } + } + } + + /// + /// Returns an enumerable over all of the class files within the specified root path. + /// + /// + /// + IEnumerable EnumerateClassFiles(global::java.nio.file.Path path) + { + var file = path.toFile(); + if (file.canRead() == false || file.isDirectory() == false) + return Enumerable.Empty(); + + return Files.walk(path) + .filter(new DelegatePredicate(p => p.getFileName().toString().EndsWith(".class"))) + .map(new DelegateFunction(p => path.relativize(p))) + .map(new DelegateFunction(p => p.toString().Replace(global::java.io.File.separatorChar, '/'))) + .AsEnumerable(); + } + } } diff --git a/src/IKVM.Tests/Java/java/lang/ExceptionTests.cs b/src/IKVM.Tests/Java/java/lang/ExceptionTests.cs new file mode 100644 index 0000000000..d5cfac6d51 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ExceptionTests.cs @@ -0,0 +1,88 @@ +using FluentAssertions; + +using IKVM.Tests.Util; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class ExceptionTests + { + + class TestException : global::java.lang.Exception + { + + + + } + + [TestMethod] + public void CanThrowAndCatchCustomException() + { + TestException e; + + try + { + throw new TestException(); + } + catch (TestException _) + { + e = _; + } + + e.Should().NotBeNull(); + } + + [TestMethod] + public void CanGetStackTraceOfThrownException() + { + TestException e; + + try + { + throw new TestException(); + } + catch (TestException _) + { + e = _; + } + + e.Should().NotBeNull(); + var t = e.getStackTrace(); + } + + [TestMethod] + public void CanPrintStackTraceOfThrownExceptionFromDynamicCompiler() + { + // compile the java test code on the fly + var source = """ +package ikvm.tests.java.java.lang; + +public final class CanGetStackTraceOfThrownExceptionFromDynamicCompilerTest { + + public void main() { + try { + throw new Exception(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} +"""; + var unit = new InMemoryCodeUnit("ikvm.tests.java.java.lang.CanGetStackTraceOfThrownExceptionFromDynamicCompilerTest", source); + var compiler = new InMemoryCompiler(new[] { unit }); + compiler.Compile(); + + // create an instance of the test class + var clazz = compiler.GetClass("ikvm.tests.java.java.lang.CanGetStackTraceOfThrownExceptionFromDynamicCompilerTest"); + var ctor = clazz.getConstructor(); + var test = (dynamic)ctor.newInstance(System.Array.Empty()); + test.main(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/MethodInvokeTests.cs b/src/IKVM.Tests/Java/java/lang/MethodInvokeTests.cs new file mode 100644 index 0000000000..458a87493c --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/MethodInvokeTests.cs @@ -0,0 +1,153 @@ +using FluentAssertions; + +using java.lang; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class MethodInvokeTests + { + + static class StaticTestObject + { + + public static void TestVoidReturnType() + { + + } + + public static void TestIntArgVoidReturnType(int i) + { + i.Should().Be(1); + } + + public static void TestBoxedIntArgVoidReturnType(object i) + { + i.Should().BeOfType(); + i.Should().Be(1); + } + + public static void TestStringArgVoidReturnType(string i) + { + i.Should().Be("TEST"); + } + + public static bool TestPrimitiveReturnType() + { + return true; + } + + } + + [TestMethod] + public void CanInvokeStaticMethodWithVoidReturnType() + { + var m = ((Class)typeof(StaticTestObject)).getDeclaredMethod("TestVoidReturnType"); + var r = m.invoke(null); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeStaticMethodWithIntArgVoidReturnType() + { + var m = ((Class)typeof(StaticTestObject)).getDeclaredMethod("TestIntArgVoidReturnType", typeof(int)); + var r = m.invoke(null, new Integer(1)); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeStaticMethodWithBoxedIntArgVoidReturnType() + { + var m = ((Class)typeof(StaticTestObject)).getDeclaredMethod("TestBoxedIntArgVoidReturnType", typeof(object)); + var r = m.invoke(null, 1); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeStaticMethodWithStringArgVoidReturnType() + { + var m = ((Class)typeof(StaticTestObject)).getDeclaredMethod("TestStringArgVoidReturnType", typeof(string)); + var r = m.invoke(null, "TEST"); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeStaticMethodWithPrimitiveReturnType() + { + var m = ((Class)typeof(StaticTestObject)).getDeclaredMethod("TestPrimitiveReturnType"); + var r = (Boolean)m.invoke(null); + r.booleanValue().Should().BeTrue(); + } + + class TestObject + { + + public static void TestVoidReturnType() + { + + } + + public bool TestPrimitiveReturnType() + { + return true; + } + + } + + [TestMethod] + public void CanInvokeMethodWithVoidReturnType() + { + var m = ((Class)typeof(TestObject)).getDeclaredMethod("TestVoidReturnType"); + var o = new TestObject(); + var r = m.invoke(o); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeMethodWithPrimitiveReturnType() + { + var m = ((Class)typeof(TestObject)).getDeclaredMethod("TestPrimitiveReturnType"); + var o = new TestObject(); + var r = (Boolean)m.invoke(o); + r.booleanValue().Should().BeTrue(); + } + + struct TestValue + { + + public static void TestVoidReturnType() + { + + } + + public bool TestPrimitiveReturnType() + { + return true; + } + + } + + [TestMethod] + public void CanInvokeValueMethodWithVoidReturnType() + { + var m = ((Class)typeof(TestValue)).getDeclaredMethod("TestVoidReturnType"); + var o = new TestValue(); + var r = m.invoke(o); + r.Should().BeNull(); + } + + [TestMethod] + public void CanInvokeValueMethodWithPrimitiveReturnType() + { + var m = ((Class)typeof(TestValue)).getDeclaredMethod("TestPrimitiveReturnType"); + var o = new TestValue(); + var r = (Boolean)m.invoke(o); + r.booleanValue().Should().BeTrue(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/ObjectTests.cs b/src/IKVM.Tests/Java/java/lang/ObjectTests.cs new file mode 100644 index 0000000000..97a7c7ca61 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ObjectTests.cs @@ -0,0 +1,14 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class ObjectTests + { + + + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/ProcessTests.cs b/src/IKVM.Tests/Java/java/lang/ProcessTests.cs new file mode 100644 index 0000000000..999780c229 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ProcessTests.cs @@ -0,0 +1,57 @@ +using System.Runtime.InteropServices; + +using FluentAssertions; + +using java.io; +using java.lang; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class ProcessTests + { + + [TestMethod] + public void CanReadFromInputStream() + { + string[] c; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + c = new[] { "cmd.exe", "/c", "echo hello" }; + else + c = new[] { "/bin/sh", "-c", "echo hello" }; + + var b = new ProcessBuilder(c); + var p = b.start(); + + var r = new BufferedReader(new InputStreamReader(p.getInputStream())); + var l = r.readLine(); + p.waitFor(); + + l.Should().Be("hello"); + } + + [TestMethod] + public void CanReadFromErrorStream() + { + string[] c; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + c = new[] { "cmd.exe", "/c", "echo hello>&2" }; + else + c = new[] { "/bin/sh", "-c", "echo hello>&2" }; + + var b = new ProcessBuilder(c); + var p = b.start(); + + var r = new BufferedReader(new InputStreamReader(p.getErrorStream())); + var l = r.readLine(); + p.waitFor(); + + l.Should().Be("hello"); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/RuntimeTests.cs b/src/IKVM.Tests/Java/java/lang/RuntimeTests.cs new file mode 100644 index 0000000000..e6be3b7c3d --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/RuntimeTests.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using FluentAssertions; + +using java.io; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class RuntimeTests + { + + /// + /// Reads all the text from the input stream until ended. + /// + /// + /// + Task ReadAllText(InputStream s) => Task.Run(() => + { + try + { + var m = new ByteArrayOutputStream(); + var i = 0; + while ((i = s.read()) >= 0) + m.write(i); + + return m.toString(); + } + catch + { + return null; + } + }); + + /// + /// Tests a variety of ways to execute the "CD" command using Runtime.exec. Each of these should print the current directory path. + /// + [TestMethod] + public void ExecCanRunWindowsCommands() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == false) + return; + + // generate some test scripts in the working directory + System.IO.File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "cdcmd.cmd"), "@echo off\r\nCD\r\n"); + System.IO.File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "cdbat.bat"), "@echo off\r\nCD\r\n"); + + var systemRoot = Environment.GetEnvironmentVariable("SystemRoot") ?? Environment.GetEnvironmentVariable("WINDIR"); + var systemDirW = Path.Combine(systemRoot, "System32"); + var systemDirM = systemDirW.Replace('\\', '/'); + var tests = new[] + { + "cmd", + "cmd.exe", + systemDirW + "\\cmd.exe", + systemDirW + "\\cmd", + systemDirM + "/cmd.exe", + systemDirM + "/cmd", + "/" + systemDirM + "/cmd", + "cdcmd.cmd", "./cdcmd.cmd", ".\\cdcmd.cmd", + "cdbat.bat", "./cdbat.bat", ".\\cdbat.bat", + }; + + foreach (var t in tests) + { + var p = global::java.lang.Runtime.getRuntime().exec(new[] { t, "/C", "CD" }); + + // start two threads to read from both streams to prevent dead locks + var ir = ReadAllText(p.getInputStream()); + var er = ReadAllText(p.getErrorStream()); + + // obtain string results + var it = ir.GetAwaiter().GetResult(); + var et = er.GetAwaiter().GetResult(); + + it.Should().Be(Environment.CurrentDirectory + Environment.NewLine); + } + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/SystemTests.cs b/src/IKVM.Tests/Java/java/lang/SystemTests.cs index f428835c65..3878cb2cb0 100644 --- a/src/IKVM.Tests/Java/java/lang/SystemTests.cs +++ b/src/IKVM.Tests/Java/java/lang/SystemTests.cs @@ -1,4 +1,6 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +using System.Threading; using FluentAssertions; @@ -20,6 +22,27 @@ public void OsNameProperty() global::java.lang.System.getProperty("os.name").Should().StartWith("Linux"); } + [TestMethod] + public void CanWriteToStdOut() + { + global::java.lang.System.@out.println("TEST"); + } + + [TestMethod] + public void CanWriteToStdErr() + { + global::java.lang.System.err.println("TEST"); + } + + [TestMethod] + public void NanoTimeShouldReportAtLeastASecond() + { + var startTime = global::java.lang.System.nanoTime(); + Thread.Sleep(TimeSpan.FromSeconds(1.1)); + var totalTime = global::java.lang.System.nanoTime() - startTime; + totalTime.Should().BeGreaterOrEqualTo(1000000000L); + } + } } diff --git a/src/IKVM.Tests/Java/java/lang/ThreadTests.cs b/src/IKVM.Tests/Java/java/lang/ThreadTests.cs new file mode 100644 index 0000000000..b0f76e0c9b --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ThreadTests.cs @@ -0,0 +1,98 @@ +using System; + +using FluentAssertions; + +using java.lang; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class ThreadTests + { + + class TestRunnable : Runnable + { + + readonly Action action; + + /// + /// Initializes a new instance. + /// + /// + /// + public TestRunnable(Action action) + { + this.action = action ?? throw new ArgumentNullException(nameof(action)); + } + + public void run() + { + action(); + } + + } + + [TestMethod] + public void CanJoinThreads() + { + var t = new Thread[16]; + for (var i = 0; i < t.Length; i++) + t[i] = new Thread(new TestRunnable(() => Thread.sleep(1000))); + for (var i = 0; i < t.Length; i++) + t[i].start(); + for (var i = 0; i < t.Length; i++) + t[i].join(); + } + + [TestMethod] + public void CanInterruptSleepingThread() + { + var e = (global::java.lang.Exception)null; + var t = new Thread(new TestRunnable(() => + { + try + { + Thread.sleep(10000); + } + catch (InterruptedException _) + { + e = _; + } + })); + t.start(); + Thread.sleep(1000); + t.interrupt(); + t.join(); + e.Should().BeOfType(); + } + + [TestMethod] + public void CanInterruptWaitingThread() + { + var e = (global::java.lang.Exception)null; + var t = new Thread(new TestRunnable(() => + { + try + { + var o = new global::java.lang.Object(); + lock (o) + o.wait(100000); + } + catch (InterruptedException _) + { + e = _; + } + })); + t.start(); + Thread.sleep(1000); + t.interrupt(); + t.join(); + e.Should().BeOfType(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/ThrowableTests.cs b/src/IKVM.Tests/Java/java/lang/ThrowableTests.cs new file mode 100644 index 0000000000..935273f56c --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ThrowableTests.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; + +using IKVM.Tests.Util; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang +{ + + [TestClass] + public class ThrowableTests + { + + global::java.lang.Class testClass; + + [TestInitialize] + public void Initialize() + { + var s = new StreamReader(typeof(ClassTests).Assembly.GetManifestResourceStream("IKVM.Tests.Java.java.lang.ThrowableTests.java")).ReadToEnd(); + var f = new InMemoryCodeUnit("ikvm.tests.java.lang.ThrowableTests", s); + var c = new InMemoryCompiler(new[] { f }); + c.Compile(); + testClass = c.GetClass("ikvm.tests.java.lang.ThrowableTests"); + if (testClass == null) + throw new Exception(); + } + + [TestMethod] + public void CanPrintStackTraceFromDynamic() + { + var m = testClass.getMethod("CanPrintStackTraceFromDynamic", new global::java.lang.Class[0]); + m.invoke(null, new object[0]); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/ThrowableTests.java b/src/IKVM.Tests/Java/java/lang/ThrowableTests.java new file mode 100644 index 0000000000..39c24acb78 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/ThrowableTests.java @@ -0,0 +1,14 @@ +package ikvm.tests.java.lang; + +public class ThrowableTests +{ + + public static void CanPrintStackTraceFromDynamic() { + try { + throw new Exception(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.class b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.class new file mode 100644 index 0000000000..f7b9754cea Binary files /dev/null and b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.class differ diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.java new file mode 100644 index 0000000000..5d4c0dec0e --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation1.java @@ -0,0 +1,12 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +@Target(ElementType.TYPE_USE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeAnnotation1 { + String value(); +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.class b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.class new file mode 100644 index 0000000000..023acbd622 Binary files /dev/null and b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.class differ diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.java new file mode 100644 index 0000000000..3a314a12c8 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotation2.java @@ -0,0 +1,12 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +@Target(ElementType.TYPE_USE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeAnnotation2 { + String value(); +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationArrayTest.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationArrayTest.java new file mode 100644 index 0000000000..2dec7e31b1 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationArrayTest.java @@ -0,0 +1,16 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +public abstract class TypeAnnotationArrayTest + extends @TypeAnnotation1("extends") @TypeAnnotation2("extends2") Object + implements @TypeAnnotation1("implements serializable") @TypeAnnotation2("implements2 serializable") Serializable, Readable, @TypeAnnotation1("implements cloneable") @TypeAnnotation2("implements2 cloneable") Cloneable { + + public @TypeAnnotation1("return4") Object @TypeAnnotation1("return1") [][] @TypeAnnotation1("return3")[] foo() { + return null; + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationClassTypeVarAndField.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationClassTypeVarAndField.java new file mode 100644 index 0000000000..4cf846f297 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationClassTypeVarAndField.java @@ -0,0 +1,26 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +public abstract class TypeAnnotationClassTypeVarAndField { + + @TypeAnnotation1("T1 field") @TypeAnnotation2("T2 field") T field1; + T field2; + @TypeAnnotation1("Object field") Object field3; + + public @TypeAnnotation1("t1") @TypeAnnotation2("t2") T foo(){ + return null; + } + + public M foo2() { + return null; + } + + public <@TypeAnnotation1("K") K extends Cloneable> K foo3() { + return null; + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTest.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTest.java new file mode 100644 index 0000000000..135d58007e --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTest.java @@ -0,0 +1,14 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +abstract class TypeAnnotationNestedTest { + + public @TypeAnnotation1("Outer") TypeAnnotationNestedTestOuter.@TypeAnnotation1("Inner")Inner @TypeAnnotation1("array")[] foo() { + return null; + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTestOuter.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTestOuter.java new file mode 100644 index 0000000000..4b3d181b46 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationNestedTestOuter.java @@ -0,0 +1,14 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +class TypeAnnotationNestedTestOuter { + + class Inner { + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.cs b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.cs new file mode 100644 index 0000000000..80fcf020a6 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.cs @@ -0,0 +1,160 @@ +using System.IO; +using System.Linq; + +using FluentAssertions; + +using IKVM.Tests.Util; + +using java.lang.reflect; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang.annotation +{ + + [TestClass] + public class TypeAnnotationTests + { + + static readonly string[] FilesToBuild = new[] + { + "TypeAnnotation1", + "TypeAnnotation2", + "TypeAnnotationArrayTest", + "TypeAnnotationClassTypeVarAndField", + "TypeAnnotationNestedTestOuter", + "TypeAnnotationNestedTest", + "TypeAnnotationWildcardTest" + }; + + static readonly string Package = "ikvm.tests.java.lang.annotation"; + static readonly string ResourcePrefix = "IKVM.Tests.Java.java.lang.annotation."; + + global::java.lang.Class TypeAnnotation1Class; + global::java.lang.reflect.Method TypeAnnotation1ValueMethod; + + global::java.lang.Class TypeAnnotation2Class; + global::java.lang.reflect.Method TypeAnnotation2ValueMethod; + + global::java.lang.Class TypeAnnotationArrayTestClass; + global::java.lang.Class TypeAnnotationNestedTestClass; + global::java.lang.Class TypeAnnotationClassTypeVarAndFieldClass; + global::java.lang.Class TypeAnnotationWildcardTestClass; + + + /// + /// Compiles some dynamic types. + /// + [TestInitialize] + public void Initialize() + { + var c = new InMemoryCompiler(FilesToBuild.Select(i => new InMemoryCodeUnit($"{Package}.{i}", new StreamReader(typeof(ClassTests).Assembly.GetManifestResourceStream($"{ResourcePrefix}{i}.java")).ReadToEnd())).ToArray()); + c.Compile(); + + TypeAnnotation1Class = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotation1"); + TypeAnnotation1ValueMethod = TypeAnnotation1Class.getDeclaredMethod("value"); + TypeAnnotation2Class = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotation2"); + TypeAnnotation2ValueMethod = TypeAnnotation2Class.getDeclaredMethod("value"); + + TypeAnnotationArrayTestClass = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotationArrayTest"); + TypeAnnotationNestedTestClass = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotationNestedTest"); + TypeAnnotationClassTypeVarAndFieldClass = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotationClassTypeVarAndField"); + TypeAnnotationWildcardTestClass = c.GetClass("ikvm.tests.java.lang.annotation.TypeAnnotationWildcardTest"); + } + + [TestMethod] + public void TestSuper() + { + ((global::java.lang.Class)typeof(object)).getAnnotatedSuperclass().Should().BeNull(); + ((global::java.lang.Class)typeof(global::java.lang.Class)).getAnnotatedSuperclass().getAnnotations().Length.Should().Be(0); + + var a = TypeAnnotationArrayTestClass.getAnnotatedSuperclass(); + var annos = a.getAnnotations(); + annos.Length.Should().Be(2); + + annos[0].annotationType().Should().Be(TypeAnnotation1Class); + ((string)TypeAnnotation1ValueMethod.invoke(annos[0])).Should().Be("extends"); + + annos[1].annotationType().Should().Be(TypeAnnotation2Class); + ((string)TypeAnnotation2ValueMethod.invoke(annos[1])).Should().Be("extends2"); + } + + + [TestMethod] + public void TestFields() + { + var f1 = TypeAnnotationClassTypeVarAndFieldClass.getDeclaredField("field1"); + var annos1 = f1.getAnnotatedType().getAnnotations(); + annos1.Length.Should().Be(2); + annos1[0].annotationType().Should().BeSameAs(TypeAnnotation1Class); + ((string)TypeAnnotation1ValueMethod.invoke(annos1[0])).Should().Be("T1 field"); + annos1[1].annotationType().Should().BeSameAs(TypeAnnotation2Class); + ((string)TypeAnnotation2ValueMethod.invoke(annos1[1])).Should().Be("T2 field"); + + var f2 = TypeAnnotationClassTypeVarAndFieldClass.getDeclaredField("field2"); + var annos2 = f2.getAnnotatedType().getAnnotations(); + annos2.Length.Should().Be(0); + + var f3 = TypeAnnotationClassTypeVarAndFieldClass.getDeclaredField("field3"); + var annos3 = f3.getAnnotatedType().getAnnotations(); + annos3.Length.Should().Be(1); + annos3[0].annotationType().Should().Be(TypeAnnotation1Class); + ((string)TypeAnnotation1ValueMethod.invoke(annos3[0])).Should().Be("Object field"); + } + + [TestMethod] + public void TestNested() + { + var m = TypeAnnotationNestedTestClass.getDeclaredMethod("foo", null); + var annos = m.getAnnotatedReturnType().getAnnotations(); + + annos.Length.Should().Be(1); + annos[0].annotationType().Should().BeSameAs(TypeAnnotation1Class); + ((string)TypeAnnotation1ValueMethod.invoke(annos[0])).Should().Be("array"); + + var t = m.getAnnotatedReturnType(); + t = ((AnnotatedArrayType)t).getAnnotatedGenericComponentType(); + annos = t.getAnnotations(); + annos.Length.Should().Be(1); + annos[0].annotationType().Should().BeSameAs(TypeAnnotation1Class); + ((string)TypeAnnotation1ValueMethod.invoke(annos[0])).Should().Be("Inner"); + } + + /// + /// Tests that we can retrieve the annotations applied to a wildcard parameterized type declaration on a field type. + /// Examples: + /// public Class<@TypeAnnotation1("1") ? extends @TypeAnnotation1("2") Annotation> f1; + /// public Class<@TypeAnnotation1("3") ? super @TypeAnnotation1("4") Annotation> f2; + /// + [TestMethod] + public void TestWildcardType() + { + var m = TypeAnnotationWildcardTestClass.getDeclaredMethod("foo", null); + var ret = m.getAnnotatedReturnType(); + + var t = ((AnnotatedParameterizedType)ret).getAnnotatedActualTypeArguments(); + t.Length.Should().Be(1); + ret = t[0]; + + var f = TypeAnnotationWildcardTestClass.getDeclaredField("f1"); + var w = (AnnotatedWildcardType)((AnnotatedParameterizedType)f.getAnnotatedType()).getAnnotatedActualTypeArguments()[0]; + t = w.getAnnotatedLowerBounds(); + t.Length.Should().Be(0); + t = w.getAnnotatedUpperBounds(); + t.Length.Should().Be(1); + var annos = t[0].getAnnotations(); + annos.Length.Should().Be(1); + ((string)TypeAnnotation1ValueMethod.invoke(annos[0])).Should().Be("2"); + + f = TypeAnnotationWildcardTestClass.getDeclaredField("f2"); + w = (AnnotatedWildcardType)((AnnotatedParameterizedType)f.getAnnotatedType()).getAnnotatedActualTypeArguments()[0]; + t = w.getAnnotatedUpperBounds(); + t.Length.Should().Be(0); + t = w.getAnnotatedLowerBounds(); + t.Length.Should().Be(1); + + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.java new file mode 100644 index 0000000000..fa55b600da --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationTests.java @@ -0,0 +1,11 @@ +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +abstract class TypeAnnotationArrayTest extends @TypeAnnotation1("extends") @TypeAnnotation2("extends2") Object + implements @TypeAnnotation1("implements serializable") @TypeAnnotation2("implements2 serializable") Serializable, + Readable, + @TypeAnno("implements cloneable") @TypeAnnotation2("implements2 cloneable") Cloneable { + public @TypeAnnotation1("return4") Object @TypeAnnotation1("return1") [][] @TypeAnnotation1("return3")[] foo() { return null; } +} diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.class b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.class new file mode 100644 index 0000000000..7d6e68f22b Binary files /dev/null and b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.class differ diff --git a/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.java b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.java new file mode 100644 index 0000000000..73dedfd36c --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/annotation/TypeAnnotationWildcardTest.java @@ -0,0 +1,18 @@ +package ikvm.tests.java.lang.annotation; + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +abstract class TypeAnnotationWildcardTest { + + public List foo() { + return null; + } + + public Class<@TypeAnnotation1("1") ? extends @TypeAnnotation1("2") Annotation> f1; + + public Class<@TypeAnnotation1("3") ? super @TypeAnnotation1("4") Annotation> f2; + +} diff --git a/src/IKVM.Tests/Java/java/lang/invoke/MethodHandleTests.cs b/src/IKVM.Tests/Java/java/lang/invoke/MethodHandleTests.cs new file mode 100644 index 0000000000..3d94c3deb8 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/invoke/MethodHandleTests.cs @@ -0,0 +1,60 @@ +using FluentAssertions; + +using global::java.lang; +using global::java.lang.invoke; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang.invoke +{ + + [TestClass] + public class MethodHandleTests + { + + [TestMethod] + [Ignore("#276")] + public void CanInvokeVirtualMethodExact() + { + var lookup = MethodHandles.lookup(); + var mt = MethodType.methodType((Class)typeof(string), new[] { (Class)typeof(char), (Class)typeof(char) }); + var mh = lookup.findVirtual((Class)typeof(string), "replace", mt); + var x = (string)mh.invokeExact(new object[] { "daddy", 'd', 'n' }); + x.Should().Be("nanny"); + } + + [TestMethod] + [Ignore("#276")] + public void CanInvokeVirtualMethodWithArguments() + { + var lookup = MethodHandles.lookup(); + var mt = MethodType.methodType((Class)typeof(string), new[] { (Class)typeof(char), (Class)typeof(char) }); + var mh = lookup.findVirtual((Class)typeof(string), "replace", mt); + ((string)mh.invokeWithArguments("sappy", 'p', 'v')).Should().Be("savvy"); + } + + [TestMethod] + public void CanInvokeStaticVarArgsMethod() + { + var lookup = MethodHandles.lookup(); + var mt = MethodType.methodType((Class)typeof(global::java.util.List), new[] { (Class)typeof(object[]) }); + var mh = lookup.findStatic(typeof(global::java.util.Arrays), "asList", mt); + mh.isVarargsCollector().Should().BeTrue(); + var x = mh.invoke("one", "two"); + x.Should().Be(global::java.util.Arrays.asList("one", "two")); + } + + [TestMethod] + [Ignore("#276")] + public void CanReverseArguments() + { + var lookup = MethodHandles.lookup(); + var mt = MethodType.methodType((Class)typeof(string), new[] { (Class)typeof(char), (Class)typeof(char) }); + var mh = lookup.findVirtual((Class)typeof(string), "replace", mt); + MethodHandles.permuteArguments(mh, mt, 1, 0); + mh.invoke("a", "b").Should().Be("ba"); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/management/ThreadMXBeanTests.cs b/src/IKVM.Tests/Java/java/lang/management/ThreadMXBeanTests.cs new file mode 100644 index 0000000000..8c5364f8b0 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/management/ThreadMXBeanTests.cs @@ -0,0 +1,53 @@ +using FluentAssertions; + +using java.lang; +using java.lang.management; +using java.util; + +using javax.management; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang.management +{ + + [TestClass] + public class ThreadMXBeanTests + { + + /// + /// Mirrored from jdk.javax/management/mxbean/ThreadMXBeanTest. + /// Ensures that invalid thread ID values passed to ThreadMXBean result in null info results. + /// + [TestMethod] + public void ShouldReturnNullForInvalidThreads() + { + var mbs = MBeanServerFactory.newMBeanServer(); + var tmb = ManagementFactory.getThreadMXBean(); + var smb = new StandardMBean(tmb, typeof(ThreadMXBean), true); + var on = new ObjectName("a:type=ThreadMXBean"); + mbs.registerMBean(smb, on); + + var proxy = (ThreadMXBean)JMX.newMXBeanProxy(mbs, on, typeof(ThreadMXBean)); + var ids1 = proxy.getAllThreadIds(); + + // add some random ids to the list so we'll get back null ThreadInfo + var ids2 = new long[ids1.Length + 10]; + global::java.lang.System.arraycopy(ids1, 0, ids2, 0, ids1.Length); + var r = new Random(); + for (var i = ids1.Length; i < ids2.Length; i++) + ids2[i] = Math.abs(r.nextLong()); + + // produces an exception if null values not handled + var info = proxy.getThreadInfo(ids2); + var sawNull = false; + foreach (var ti in info) + if (ti == null) + sawNull = true; + + sawNull.Should().BeTrue(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/lang/reflect/ProxyTests.cs b/src/IKVM.Tests/Java/java/lang/reflect/ProxyTests.cs new file mode 100644 index 0000000000..5e11fc5457 --- /dev/null +++ b/src/IKVM.Tests/Java/java/lang/reflect/ProxyTests.cs @@ -0,0 +1,45 @@ +using FluentAssertions; + +using java.lang; +using java.lang.reflect; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.lang.reflect +{ + + [TestClass] + public class ProxyTests + { + + class PublicInterfaceHandler : InvocationHandler + { + + public object invoke(object obj, Method meth, object[] args) + { + if (meth.getName() == "Run") + return "TEST"; + + throw new System.Exception(); + } + + } + + public interface PublicInterface + { + + string Run(); + + } + + [TestMethod] + public void CanProxyPublicInterface() + { + var c = (Class)typeof(PublicInterface); + var p = (PublicInterface)Proxy.newProxyInstance(c.getClassLoader(), new[] { c }, new PublicInterfaceHandler()); + p.Run().Should().Be("TEST"); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/net/DatagramSocketTests.cs b/src/IKVM.Tests/Java/java/net/DatagramSocketTests.cs index 75b9a99e03..06aa099105 100644 --- a/src/IKVM.Tests/Java/java/net/DatagramSocketTests.cs +++ b/src/IKVM.Tests/Java/java/net/DatagramSocketTests.cs @@ -93,11 +93,11 @@ public async Task CanSendAndReceive() serverSocket.receive(packet); received.Add(Encoding.UTF8.GetString(b, packet.getOffset(), packet.getLength())); } - catch (InterruptedIOException e) + catch (InterruptedIOException) { } - catch (SocketException e) + catch (SocketException) { } diff --git a/src/IKVM.Tests/Java/java/net/URLConnectionTests.cs b/src/IKVM.Tests/Java/java/net/URLConnectionTests.cs index 7e5eabf895..d082db939c 100644 --- a/src/IKVM.Tests/Java/java/net/URLConnectionTests.cs +++ b/src/IKVM.Tests/Java/java/net/URLConnectionTests.cs @@ -135,7 +135,7 @@ public void CanCloseKeepAliveStreamWithWrongContentLength() ow.write("123456789"); ow.flush(); } - catch (global::java.lang.Exception e) + catch (global::java.lang.Exception) { } @@ -143,10 +143,9 @@ public void CanCloseKeepAliveStreamWithWrongContentLength() { try { - if (ost != null) - ost.close(); + ost?.close(); } - catch (global::java.io.IOException e) + catch (global::java.io.IOException) { } @@ -200,4 +199,4 @@ public void ConnectToBeURLShouldThrowConnectException() } -} \ No newline at end of file +} diff --git a/src/IKVM.Tests/Java/java/net/URLTests.cs b/src/IKVM.Tests/Java/java/net/URLTests.cs index 1f5f7482e1..27cd7f4c9c 100644 --- a/src/IKVM.Tests/Java/java/net/URLTests.cs +++ b/src/IKVM.Tests/Java/java/net/URLTests.cs @@ -2,9 +2,9 @@ using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using javax.net.ssl; -using Newtonsoft.Json.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace IKVM.Tests.Java.java.net { @@ -14,14 +14,163 @@ public class URLTests { [TestMethod] - public void Can_fetch_http_url() + public void CanCreateHttpURL() + { + var u = new global::java.net.URL("http://www.site.com/"); + u.getProtocol().Should().Be("http"); + u.getHost().Should().Be("www.site.com"); + u.getPath().Should().Be("/"); + } + + [TestMethod] + public void CanCreateHttpsURL() + { + var u = new global::java.net.URL("https://www.site.com/"); + u.getProtocol().Should().Be("https"); + u.getHost().Should().Be("www.site.com"); + u.getPath().Should().Be("/"); + } + + [TestMethod] + public void CanCreateWindowsFileURL() + { + var u = new global::java.net.URL("file:///c:/dir"); + u.getProtocol().Should().Be("file"); + u.getHost().Should().Be(""); + u.getPath().Should().Be("/c:/dir"); + } + + [TestMethod] + public void CanCreateUnixFileURL() + { + var u = new global::java.net.URL("file:///tmp/foo"); + u.getProtocol().Should().Be("file"); + u.getHost().Should().Be(""); + u.getPath().Should().Be("/tmp/foo"); + } + + [TestMethod] + public void CanGetFromPlainURL() + { + var stm = new global::java.net.URL("http://http.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("http.badssl.com"); + } + + [TestMethod] + public void CanGetFromTLS10Url() + { + var stm = new global::java.net.URL("https://tls-v1-0.badssl.com:1010/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("tls-v1-0.badssl.com"); + } + + [TestMethod] + public void CanGetFromTLS11Url() + { + var stm = new global::java.net.URL("https://tls-v1-1.badssl.com:1011/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("tls-v1-1.badssl.com"); + } + + [TestMethod] + public void CanGetFromTLS12Url() + { + var stm = new global::java.net.URL("https://tls-v1-2.badssl.com:1012/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("tls-v1-2.badssl.com"); + } + + [TestMethod] + public void CanGetFromSha256Url() + { + var stm = new global::java.net.URL("https://sha256.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("sha256.badssl.com"); + } + + [TestMethod] + public void CanGetFromRsa2048Url() + { + var stm = new global::java.net.URL("https://rsa2048.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("rsa2048.badssl.com"); + } + + [TestMethod] + public void CanGetFromRsa8192Url() + { + var stm = new global::java.net.URL("https://rsa8192.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("rsa8192.badssl.com"); + } + + [TestMethod] + [Ignore] + public void CanGetFromEcc256Url() + { + var stm = new global::java.net.URL("https://ecc256.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("ecc256.badssl.com"); + } + + [TestMethod] + [Ignore] + public void CanGetFromEcc384Url() + { + var stm = new global::java.net.URL("https://ecc384.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("ecc384.badssl.com"); + } + + [TestMethod] + [Ignore] + public void CanGetFromDh480Url() + { + var stm = new global::java.net.URL("https://dh480.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("dh480.badssl.com"); + } + + [TestMethod] + [ExpectedException(typeof(SSLHandshakeException))] + public void CanGetFromDh512Url() + { + var stm = new global::java.net.URL("https://dh512.badssl.com/").openStream(); + var buf = new byte[8192]; + var bytesRead = stm.read(buf, 0, buf.Length); + var str = Encoding.UTF8.GetString(buf, 0, bytesRead); + str.Should().Contain("dh512.badssl.com"); + } + + [TestMethod] + public void CanGetFromDh1024Url() { - var stm = new global::java.net.URL("https://httpbin.org/anything?text=ECHO").openStream(); + var stm = new global::java.net.URL("https://dh1024.badssl.com/").openStream(); var buf = new byte[8192]; - var bytesRead = stm.read(buf, 0, 8192); + var bytesRead = stm.read(buf, 0, buf.Length); var str = Encoding.UTF8.GetString(buf, 0, bytesRead); - var json = JObject.Parse(str); - json["args"]["text"].Value().Should().Be("ECHO"); + str.Should().Contain("dh1024.badssl.com"); } } diff --git a/src/IKVM.Tests/Java/java/nio/channels/AsynchronousFileChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousFileChannelTests.cs new file mode 100644 index 0000000000..1df97fdf60 --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousFileChannelTests.cs @@ -0,0 +1,605 @@ +using System; +using System.Drawing; +using System.Runtime.ExceptionServices; +using System.Security.Cryptography; +using System.Security.Policy; +using System.Threading.Tasks; + +using FluentAssertions; + +using IKVM.Runtime.Vfs; + +using java.io; +using java.lang; +using java.nio; +using java.nio.channels; +using java.nio.file; +using java.util; +using java.util.concurrent; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using sun.nio.ch; + +namespace IKVM.Tests.Java.java.nio.channels +{ + + [TestClass] + public class AsynchronousFileChannelTests + { + + /// + /// Tests that we fail correctly attempting to create a file that already exists. + /// + /// + [TestMethod] + [ExpectedException(typeof(FileAlreadyExistsException))] + public void ShouldFailOnCreateNewExistingFile() + { + var f = new File("AsynchronousFileChannelTests_ShouldFailOnCreateExistingFile.txt"); + if (f.exists()) + f.delete(); + f.createNewFile(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); + c.close(); + } + + /// + /// Tests that we can overwrite an existing file. + /// + /// + [TestMethod] + public async Task CanCreateNewAndWrite() + { + var f = new File("AsynchronousFileChannelTests_CanCreateNewAndWrite.txt"); + if (f.exists()) + f.delete(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); + var b = ByteBuffer.allocate(1); + b.put(1); + b.flip(); + + var h = new AwaitableCompletionHandler(); + c.write(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + using var input = new FileInputStream(f); + var inputBuf = new byte[1]; + input.read(inputBuf); + inputBuf[0].Should().Be(1); + } + + /// + /// Tests that we can overwrite an existing file. + /// + /// + [TestMethod] + public async Task CanCreateAndWrite() + { + var f = new File("AsynchronousFileChannelTests_CanCreateAndWrite.txt"); + if (f.exists()) + f.delete(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + var b = ByteBuffer.allocate(1); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.put(1); + b.flip(); + + var h = new AwaitableCompletionHandler(); + c.write(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + using var input = new FileInputStream(f); + var inputBuf = new byte[1]; + input.read(inputBuf); + inputBuf[0].Should().Be(1); + } + + /// + /// Tests that we can overwrite an existing file. + /// + /// + [TestMethod] + public async Task CanCreateAndWriteFromDirectBuffer() + { + var f = new File("AsynchronousFileChannelTests_CanCreateAndWriteFromDirectBuffer.txt"); + if (f.exists()) + f.delete(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + var b = ByteBuffer.allocateDirect(1); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.put(1); + b.flip(); + + var h = new AwaitableCompletionHandler(); + c.write(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + using var input = new FileInputStream(f); + var inputBuf = new byte[1]; + input.read(inputBuf); + inputBuf[0].Should().Be(1); + } + + /// + /// Test that we can open in write/truncate, replace a file, and rewrite with a single byte. + /// + /// + [TestMethod] + public async Task CanTruncateAndWrite() + { + var f = new File("AsynchronousFileChannelTests_CanTruncateAndWrite.txt"); + if (f.exists()) + f.delete(); + f.createNewFile(); + + using var s = new FileOutputStream(f); + s.write(new byte[] { 2 }); + s.close(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); + var b = ByteBuffer.allocate(1); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.put(1); + b.flip(); + + var h = new AwaitableCompletionHandler(); + c.write(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + using var input = new FileInputStream(f); + var inputBuf = new byte[1]; + input.read(inputBuf); + inputBuf[0].Should().Be(1); + input.close(); + } + + /// + /// Tests that we can open in read mode and read a single byte. + /// + /// + [TestMethod] + public async Task CanRead() + { + var f = new File("AsynchronousFileChannelTests_CanRead.txt"); + if (f.exists()) + f.delete(); + + f.createNewFile(); + using var s = new FileOutputStream(f); + s.write(new byte[] { 1 }); + s.close(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.READ); + var b = ByteBuffer.allocate(1); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + + var h = new AwaitableCompletionHandler(); + c.read(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + b.flip(); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.get().Should().Be(1); + } + + /// + /// Tests that we can open in read mode and read a single byte. + /// + /// + [TestMethod] + public async Task CanReadIntoDirectBuffer() + { + var f = new File("AsynchronousFileChannelTests_CanReadIntoDirectBuffer.txt"); + if (f.exists()) + f.delete(); + + f.createNewFile(); + using var s = new FileOutputStream(f); + s.write(new byte[] { 1 }); + s.close(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.READ); + var b = ByteBuffer.allocateDirect(1); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.isDirect().Should().Be(true); + + var h = new AwaitableCompletionHandler(); + c.read(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(1); + c.close(); + + b.flip(); + b.capacity().Should().Be(1); + b.position().Should().Be(0); + b.limit().Should().Be(1); + b.get().Should().Be(1); + + var r = await Task.Run(() => + { + unsafe + { + var d = (DirectBuffer)b; + var span = new Span((byte*)(IntPtr)d.address(), b.capacity()); + return span[0] == 1; + } + }); + + r.Should().BeTrue(); + } + + /// + /// Tests that we can open in read mode and read a single byte. + /// + /// + [TestMethod] + public async Task CanLock() + { + var f = new File("AsynchronousFileChannelTests_CanLock.txt"); + if (f.exists()) + f.delete(); + + f.createNewFile(); + using var s = new FileOutputStream(f); + s.write(new byte[] { 1, 2 }); + s.close(); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.WRITE); + + var h = new AwaitableCompletionHandler(); + c.@lock(0, 2, false, null, h); + var l = await h; + + l.position().Should().Be(0); + l.size().Should().Be(2); + l.release(); + c.close(); + } + + [TestMethod] + public void TryLockShouldThrowOverlappingFileLockException() + { + var file = File.createTempFile("lockfile", null); + file.deleteOnExit(); + if (file.exists()) + file.delete(); + + file.createNewFile(); + using var s = new FileOutputStream(file); + s.write(new byte[] { 1, 2, 3, 4 }); + s.close(); + + var ch = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE); + ch.Should().NotBeNull(); + + var fl = (FileLock)ch.@lock().get(); + fl.Should().NotBeNull(); + fl.acquiredBy().Should().BeSameAs(ch); + + ch.Invoking(x => x.tryLock()).Should().Throw(); + + ch.close(); + fl.isValid().Should().BeFalse(); + } + + [TestMethod] + public void LockShouldThrowOverlappingFileLockException() + { + var file = File.createTempFile("lockfile", null); + file.deleteOnExit(); + if (file.exists()) + file.delete(); + + file.createNewFile(); + using var s = new FileOutputStream(file); + s.write(new byte[] { 1, 2, 3, 4 }); + s.close(); + + var ch = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE); + ch.Should().NotBeNull(); + + var fl = ch.tryLock(); + fl.Should().NotBeNull(); + fl.acquiredBy().Should().BeSameAs(ch); + + var awaiter = new AwaitableCompletionHandler(); + ch.Invoking(x => x.@lock(null, awaiter)).Should().Throw(); + + ch.close(); + fl.isValid().Should().BeFalse(); + } + + /// + /// Creates threads with the name set. + /// + class NamedThreadFactory : ThreadFactory + { + + readonly string name; + + /// + /// Initializes a new instance. + /// + /// + /// + public NamedThreadFactory(string name) + { + this.name = name ?? throw new ArgumentNullException(nameof(name)); + } + + public Thread newThread(Runnable r) + { + var t = new Thread(r, name); + t.setDaemon(true); + return t; + } + + } + + /// + /// Ensures that completions of the channel execute on the specified thread pool. + /// + /// + [TestMethod] + public async Task ShouldExecuteOnThreadPool() + { + Thread.currentThread().getName().Should().NotBe("ShouldExecuteOnThreadPool"); + + var f = new File("AsynchronousFileChannelTests_ShouldExecuteOnThreadPool.txt"); + if (f.exists()) + f.delete(); + + f.createNewFile(); + using var s = new FileOutputStream(f); + s.write(new byte[] { 1 }); + s.close(); + + var t = Executors.newSingleThreadExecutor(new NamedThreadFactory("ShouldExecuteOnThreadPool")); + using var c = AsynchronousFileChannel.open(f.toPath(), Collections.singleton(StandardOpenOption.READ), t); + var b = ByteBuffer.allocate(1); + + var h = new AwaitableCompletionHandler(); + c.read(b, 0, null, h); + var n = await h; + Thread.currentThread().getName().Should().Be("ShouldExecuteOnThreadPool"); + n.intValue().Should().Be(1); + c.close(); + + b.flip(); + b.get().Should().Be(1); + } + + /// + /// Importan that the asynchronous APIs can read from the VFS. + /// + /// + [TestMethod] + public async Task CanReadVfsAssemblyClass() + { + var f = new File(System.IO.Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly), "java", "lang", "Object.class")); + + using var c = AsynchronousFileChannel.open(f.toPath(), StandardOpenOption.READ); + var b = ByteBuffer.allocate(512); + b.capacity().Should().Be(512); + b.position().Should().Be(0); + b.limit().Should().Be(512); + + var h = new AwaitableCompletionHandler(); + c.read(b, 0, null, h); + var n = await h; + n.intValue().Should().Be(512); + c.close(); + + b.position().Should().Be(512); + b.flip(); + b.capacity().Should().Be(512); + b.position().Should().Be(0); + b.limit().Should().Be(512); + b.order(ByteOrder.BIG_ENDIAN); + var m = b.getInt(); + m.Should().Be(unchecked((int)0xCAFEBABE)); + } + + [TestMethod] + public void ShouldThrowClosedChannelExceptionOnRead() + { + var file = File.createTempFile("test", null); + file.deleteOnExit(); + if (file.exists()) + file.delete(); + + file.createNewFile(); + using var s = new FileOutputStream(file); + s.write(new byte[] { 1, 2, 3, 4 }); + s.close(); + + using var c = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ); + c.close(); + c.isOpen().Should().BeFalse(); + + var buf = ByteBuffer.allocateDirect(4); + c.Invoking(x => x.read(buf, 0L).get()).Should().Throw().Where(e => e.getCause() is ClosedChannelException); + } + + [TestMethod] + public void ShouldThrowClosedChannelExceptionOnWrite() + { + var file = File.createTempFile("test", null); + file.deleteOnExit(); + if (file.exists()) + file.delete(); + + file.createNewFile(); + using var s = new FileOutputStream(file); + s.write(new byte[] { 1, 2, 3, 4 }); + s.close(); + + using var c = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.WRITE); + c.close(); + c.isOpen().Should().BeFalse(); + + var buf = ByteBuffer.allocateDirect(4); + c.Invoking(x => x.write(buf, 0L).get()).Should().Throw().Where(e => e.getCause() is ClosedChannelException); + } + + [TestMethod] + public void ShouldThrowClosedChannelExceptionOnLock() + { + var file = File.createTempFile("test", null); + file.deleteOnExit(); + if (file.exists()) + file.delete(); + + file.createNewFile(); + using var s = new FileOutputStream(file); + s.write(new byte[] { 1, 2, 3, 4 }); + s.close(); + + using var c = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE); + c.close(); + c.isOpen().Should().BeFalse(); + + c.Invoking(x => x.@lock().get()).Should().Throw().Where(e => e.getCause() is ClosedChannelException); + } + + [TestMethod] + public void ShouldThrowAsynchronousCloseExceptionWhenClosedDuringWrite() + { + var rng = RandomNumberGenerator.Create(); + var rnd = new System.Random(); + + const int size = 1024 * 1024 * 64; + const int z = 8; + + var file = File.createTempFile("test", null); + file.deleteOnExit(); + + // rewrite file with random data + System.IO.File.Create(file.getPath()).Close(); + + using var c = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.SYNC); + + // generate N buffers to read data from + var buf = new ByteBuffer[z]; + var pos = new int[z]; + for (int i = 0; i < z; i++) + { + var d = ByteBuffer.allocateDirect(size); + var b = new byte[size]; + rng.GetBytes(b); + d.put(b); + d.flip(); + buf[i] = d; + pos[i] = rnd.Next(1, size); + } + + // start N async write requests + var result = new Future[z]; + for (int i = 0; i < z; i++) + result[i] = c.write(buf[i], pos[i]); + + // close channel while writing is ongoing + c.close(); + + // write operations should complete or fail with AsynchronousCloseException + for (int i = 0; i < z; i++) + { + try + { + result[i].get().Should().NotBeNull(); + } + catch (ExecutionException e) when (e.getCause() is AsynchronousCloseException) + { + // expected + } + } + } + + [TestMethod] + public void CanCancelDuringWrite() + { + var rng = RandomNumberGenerator.Create(); + var rnd = new System.Random(); + + const int size = 1024 * 1024 * 64; + const int z = 8; + + var file = File.createTempFile("test", null); + file.deleteOnExit(); + + // rewrite file with random data + System.IO.File.Create(file.getPath()).Close(); + + using var c = AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.SYNC); + + // generate N buffers to read data from + var buf = new ByteBuffer[z]; + var pos = new int[z]; + for (int i = 0; i < z; i++) + { + var d = ByteBuffer.allocateDirect(size); + var b = new byte[size]; + rng.GetBytes(b); + d.put(b); + d.flip(); + buf[i] = d; + pos[i] = rnd.Next(1, size); + } + + // start N async write requests + var result = new Future[z]; + for (int i = 0; i < z; i++) + result[i] = c.write(buf[i], pos[i]); + + // cancel channel while writing is ongoing + var cancelled = new bool[z]; + for (int i = 0; i < z; i++) + cancelled[i] = result[i].cancel(false); + + for (int i = 0; i < z; i++) + { + result[i].isDone().Should().BeTrue(); + result[i].isCancelled().Should().Be(cancelled[i]); + + try + { + result[i].get(); + cancelled[i].Should().BeFalse(); // if we didn't throw, we weren't cancelled + } + catch (CancellationException) + { + cancelled[i].Should().BeTrue(); // we did throw, so we should be cancelled + } + } + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Tests/Java/java/nio/channels/AsynchronousServerSocketChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousServerSocketChannelTests.cs new file mode 100644 index 0000000000..0c9019d5e4 --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousServerSocketChannelTests.cs @@ -0,0 +1,526 @@ +using System.Security.Cryptography; +using System.Threading.Tasks; + +using FluentAssertions; + +using java.lang; +using java.net; +using java.nio; +using java.nio.channels; +using java.util; +using java.util.concurrent; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.channels +{ + + [TestClass] + public class AsynchronousServerSocketChannelTests + { + + [TestMethod] + public void CanOpenAndClose() + { + using var c = AsynchronousServerSocketChannel.open(); + c.close(); + } + + [TestMethod] + public void CanBindAndClose() + { + using var ss = AsynchronousServerSocketChannel.open(); + ss.bind(new InetSocketAddress(0)); + ss.close(); + } + + [TestMethod] + public async Task CanSendAndReceiveData() + { + using var ss = AsynchronousServerSocketChannel.open(); + ss.bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)ss.getLocalAddress()).getPort(); + + var s = Task.Run(async () => + { + // wait for client to connect + var sc = (AsynchronousSocketChannel)ss.accept().get(); + + // wait for client to send data + var bb = ByteBuffer.allocate(1024); + var l = (Integer)sc.read(bb).get(); + + // respond with same data + bb.flip(); + bb.limit(l.intValue()); + sc.write(bb); + + // wait for client to finish + await Task.Delay(500); + sc.close(); + }); + + var cs = AsynchronousSocketChannel.open(); + cs.connect(new InetSocketAddress("127.0.0.1", port)).get(); + + // wait for client to send data + var bb = ByteBuffer.allocate(1024); + bb.putLong(0xDEADBEEF); + bb.limit(bb.position()); + bb.flip(); + cs.write(bb); + + // read the response and check that it matches what we sent + bb.clear(); + var n = ((Integer)cs.read(bb).get()).intValue(); + n.Should().Be(sizeof(long)); + bb.flip(); + bb.getLong().Should().Be(0xDEADBEEF); + + // wait for server to end + await s; + } + + [TestMethod] + public async Task CanSendAndReceiveDataWithDirectBuffers() + { + using var ss = AsynchronousServerSocketChannel.open(); + ss.bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)ss.getLocalAddress()).getPort(); + + var s = Task.Run(async () => + { + // wait for client to connect + var sc = (AsynchronousSocketChannel)ss.accept().get(); + + // wait for client to send data + var bb = ByteBuffer.allocateDirect(1024); + var l = (Integer)sc.read(bb).get(); + + // respond with same data + bb.flip(); + bb.limit(l.intValue()); + sc.write(bb); + + // wait for client to finish + await Task.Delay(500); + sc.close(); + }); + + var cs = AsynchronousSocketChannel.open(); + cs.connect(new InetSocketAddress("127.0.0.1", port)).get(); + + // wait for client to send data + var bb = ByteBuffer.allocateDirect(1024); + bb.putLong(0xDEADBEEF); + bb.limit(bb.position()); + bb.flip(); + cs.write(bb); + + // read the response and check that it matches what we sent + bb.clear(); + var n = ((Integer)cs.read(bb).get()).intValue(); + n.Should().Be(sizeof(long)); + bb.flip(); + bb.getLong().Should().Be(0xDEADBEEF); + + // wait for server to end + await s; + } + + [TestMethod] + public async Task CanSendAndReceiveDataWithMultipleBuffers() + { + using var ss = AsynchronousServerSocketChannel.open(); + ss.bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)ss.getLocalAddress()).getPort(); + + var s = Task.Run(async () => + { + // wait for client to connect + var sc = (AsynchronousSocketChannel)ss.accept().get(); + + // wait for client to send data + var bb1 = ByteBuffer.allocate(1024); + var bb2 = ByteBuffer.allocate(1024); + var h1 = new AwaitableCompletionHandler(); + sc.read(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h1); + var l1 = await h1; + + // respond with same data + bb1.limit(bb1.position()); + bb1.flip(); + bb2.limit(bb2.position()); + bb2.flip(); + var h2 = new AwaitableCompletionHandler(); + sc.write(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h2); + var l2 = await h2; + + // wait for client to finish + await Task.Delay(500); + sc.close(); + }); + + var cs = AsynchronousSocketChannel.open(); + cs.connect(new InetSocketAddress("127.0.0.1", port)).get(); + + var dat = new byte[1024]; + var rng = RandomNumberGenerator.Create(); + rng.GetBytes(dat); + + // wait for client to send data + var bb1 = ByteBuffer.allocate(1024); + bb1.put(dat); + bb1.flip(); + + var bb2 = ByteBuffer.allocate(1024); + bb2.put(dat); + bb2.flip(); + + var h1 = new AwaitableCompletionHandler(); + cs.write(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h1); + var w = (await h1).longValue(); + w.Should().Be(2048); + + // read the response and check that it matches what we sent + bb1.clear(); + bb2.clear(); + var h2 = new AwaitableCompletionHandler(); + cs.read(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h2); + var r = (await h2).longValue(); + r.Should().Be(2048); + + bb1.flip(); + bb2.flip(); + + var aa1 = new byte[bb1.limit()]; + bb1.get(aa1); + + var aa2 = new byte[bb2.limit()]; + bb2.get(aa2); + + // check that the result equals the random data + aa1.Should().BeEquivalentTo(dat); + aa2.Should().BeEquivalentTo(dat); + + // wait for server to end + await s; + } + + [TestMethod] + public async Task CanSendAndReceiveDataWithMultipleDirectBuffers() + { + using var ss = AsynchronousServerSocketChannel.open(); + ss.bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)ss.getLocalAddress()).getPort(); + + var s = Task.Run(async () => + { + // wait for client to connect + var sc = (AsynchronousSocketChannel)ss.accept().get(); + + // wait for client to send data + var bb1 = ByteBuffer.allocateDirect(1024); + var bb2 = ByteBuffer.allocateDirect(1024); + var h1 = new AwaitableCompletionHandler(); + sc.read(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h1); + var l1 = await h1; + + // respond with same data + bb1.limit(bb1.position()); + bb1.flip(); + bb2.limit(bb2.position()); + bb2.flip(); + var h2 = new AwaitableCompletionHandler(); + sc.write(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h2); + var l2 = await h2; + + // wait for client to finish + await Task.Delay(500); + sc.close(); + }); + + var cs = AsynchronousSocketChannel.open(); + cs.connect(new InetSocketAddress("127.0.0.1", port)).get(); + + var dat = new byte[1024]; + var rng = RandomNumberGenerator.Create(); + rng.GetBytes(dat); + + // wait for client to send data + var bb1 = ByteBuffer.allocateDirect(1024); + bb1.put(dat); + bb1.limit(bb1.position()); + bb1.flip(); + + var bb2 = ByteBuffer.allocateDirect(1024); + bb2.put(dat); + bb2.limit(bb2.position()); + bb2.flip(); + + var h1 = new AwaitableCompletionHandler(); + cs.write(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h1); + var w = (await h1).longValue(); + w.Should().Be(2048); + + // read the response and check that it matches what we sent + bb1.clear(); + bb2.clear(); + var h2 = new AwaitableCompletionHandler(); + cs.read(new[] { bb1, bb2 }, 0, 2, 0, TimeUnit.MILLISECONDS, null, h2); + var r = (await h2).longValue(); + r.Should().Be(2048); + + bb1.limit(bb1.position()); + bb1.flip(); + bb2.limit(bb2.position()); + bb2.flip(); + + var aa1 = new byte[bb1.limit()]; + bb1.get(aa1); + + var aa2 = new byte[bb2.limit()]; + bb2.get(aa2); + + // check that the result equals the random data + aa1.Should().BeEquivalentTo(dat); + aa2.Should().BeEquivalentTo(dat); + + // wait for server to end + await s; + } + + [TestMethod] + [ExpectedException(typeof(AcceptPendingException))] + public void ShouldThrowAcceptPending() + { + var l = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + var h = new AwaitableCompletionHandler(); + l.accept(null, h); + l.accept(); + } + + [TestMethod] + public void CanReadAndWriteWithStreamsSimple() + { + using var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + var port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); + var sendChannel = AsynchronousSocketChannel.open(); + sendChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)).get(); + var recvChannel = (AsynchronousSocketChannel)listener.accept().get(); + + var sendStream = Channels.newOutputStream(sendChannel); + + var sendBuffer = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + sendStream.write(sendBuffer); + sendStream.close(); + + var recvStream = Channels.newInputStream(recvChannel); + var recvBuffer = new byte[4]; + recvStream.read(recvBuffer); + recvStream.close(); + + recvBuffer.Should().BeEquivalentTo(sendBuffer); + } + + [TestMethod] + public void CanReadAndWriteWithStreamsOffset() + { + using var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + var port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); + var sendChannel = AsynchronousSocketChannel.open(); + sendChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)).get(); + var recvChannel = (AsynchronousSocketChannel)listener.accept().get(); + + // send middle two bytes + var sendStream = Channels.newOutputStream(sendChannel); + var sendBuffer = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + sendStream.write(sendBuffer, 1, 2); + sendStream.close(); + + // receve into middle two bytes + var recvStream = Channels.newInputStream(recvChannel); + var recvBuffer = new byte[] { 0x01, 0x00, 0x00, 0x04 }; + recvStream.read(recvBuffer, 1, 2); + recvStream.close(); + + recvBuffer.Should().BeEquivalentTo(sendBuffer); + } + + [TestMethod] + public void CanReadAndWriteWithStreams() + { + var rand = new Random(); + + using var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); + var isa = new InetSocketAddress(InetAddress.getLocalHost(), port); + var ch1 = AsynchronousSocketChannel.open(); + ch1.connect(isa).get(); + var ch2 = (AsynchronousSocketChannel)listener.accept().get(); + + // thread to write random data + var writeStream = Channels.newOutputStream(ch1); + int writeSize = 50 * 1000 + rand.nextInt(50 * 1000); + int writeHash = 0; + var writeTask = Task.Run(() => + { + int rem = writeSize; + + do + { + byte[] buf = new byte[1 + rand.nextInt(rem)]; + int off, len; + + // write random bytes + if (rand.nextBoolean()) + { + off = 0; + len = buf.Length; + } + else + { + off = rand.nextInt(buf.Length); + int r = buf.Length - off; + len = (r <= 1) ? 1 : (1 + rand.nextInt(r)); + } + + for (int i = 0; i < len; i++) + { + byte value = (byte)rand.nextInt(256); + buf[off + i] = value; + writeHash = writeHash ^ value; + } + + if ((off == 0) && (len == buf.Length)) + { + writeStream.write(buf); + } + else + { + writeStream.write(buf, off, len); + } + + rem -= len; + } while (rem > 0); + + // close stream when done + writeStream.close(); + }); + + // thread to read random data + var readStream = Channels.newInputStream(ch2); + int readSize = 0; + int readHash = 0; + var readTask = Task.Run(() => + { + int n; + + do + { + // random offset/len + byte[] buf = new byte[128 + rand.nextInt(128)]; + int len, off; + if (rand.nextBoolean()) + { + len = buf.Length; + off = 0; + n = readStream.read(buf); + } + else + { + len = 1 + rand.nextInt(64); + off = rand.nextInt(64); + n = readStream.read(buf, off, len); + } + + if (n > len) + throw new RuntimeException("Too many bytes read"); + + if (n > 0) + { + readSize += n; + for (int i = 0; i < n; i++) + { + int value = buf[off + i]; + readHash = readHash ^ value; + } + } + } while (n > 0); + + readStream.close(); + }); + + writeTask.GetAwaiter().GetResult(); + readTask.GetAwaiter().GetResult(); + + // shutdown listener + listener.close(); + + // check that reader received what we expected + if (readSize != writeSize) + throw new RuntimeException("Unexpected number of bytes read"); + if (readHash != writeHash) + throw new RuntimeException("Hash incorrect for bytes read"); + + // channels should be closed + if (ch1.isOpen() || ch2.isOpen()) + throw new RuntimeException("Channels should be closed"); + } + + [TestMethod] + [ExpectedException(typeof(AcceptPendingException))] + public void ShouldThrowAcceptPendingExceptionOnAccept() + { + var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + var accept = new AwaitableCompletionHandler(); + listener.accept(null, accept); + listener.accept(); + listener.close(); + } + + [TestMethod] + [ExpectedException(typeof(AsynchronousCloseException))] + public async Task ShouldThrowAsynchronousCloseExceptionForAcceptDuringAccept() + { + var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + + // initiate an outstanding accept + var accept = new AwaitableCompletionHandler(); + listener.accept(null, accept); + listener.close(); + + // complete first acceptance, which should throw nested AsynchronousCloseException + try + { + await accept; + } + catch (ExecutionException e) + { + throw e.getCause(); + } + } + + [TestMethod] + [ExpectedException(typeof(ClosedChannelException))] + public void ShouldThrowClosedChannelExceptionForAcceptAfterClose() + { + var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + + // initiate an outstanding accept + listener.close(); + + // try accept while closed + try + { + listener.accept().get(); + } + catch (ExecutionException e) + { + throw e.getCause(); + } + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/channels/AsynchronousSocketChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousSocketChannelTests.cs new file mode 100644 index 0000000000..813398ca4e --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/channels/AsynchronousSocketChannelTests.cs @@ -0,0 +1,241 @@ +using FluentAssertions; + +using java.io; +using java.lang; +using java.net; +using java.nio; +using java.nio.channels; +using java.util.concurrent; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.channels +{ + + [TestClass] + public class AsynchronousSocketChannelTests + { + + /// + /// Copied from similar test in OpenJDK. Should be cleaned up to just be a simple send/receive loop. + /// + /// + [TestMethod] + public void TestStressLoopback() + { + // setup listener + var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + var lh = InetAddress.getLocalHost(); + var remote = new InetSocketAddress(lh, port); + + // create sources and sinks + var ch = AsynchronousSocketChannel.open(); + ch.connect(remote).get(); + var src = new Source(ch); + var dst = new Sink((AsynchronousByteChannel)listener.accept().get()); + + // start the sink and source + dst.start(); + src.Start(); + + // let the test run for a while + Thread.sleep(20 * 1000); + + // wait until everyone is done + var failed = false; + + var sent = src.Finish(); + var recv = dst.Finish(); + + if (recv != sent) + failed = true; + + if (failed) + throw new RuntimeException("Test failed - see log for details"); + } + + sealed class Source + { + + readonly AsynchronousByteChannel channel; + readonly ByteBuffer sentBuffer; + long bytesSent; + bool finished; + + internal Source(AsynchronousByteChannel channel) + { + this.channel = channel; + this.sentBuffer = ByteBuffer.allocate(1024); + } + + internal void Start() + { + sentBuffer.position(0); + sentBuffer.limit(sentBuffer.capacity()); + channel.write(sentBuffer, null, new Completion(this)); + } + + internal long Finish() + { + finished = true; + WaitUntilClosed(channel); + return bytesSent; + } + + private class Completion : CompletionHandler + { + + private readonly Source source; + + public Completion(Source @this) + { + this.source = @this; + } + + public void completed(Integer nwrote, object _) + { + source.bytesSent += nwrote.intValue(); + + if (source.finished) + { + CloseUnchecked(source.channel); + } + else + { + source.sentBuffer.position(0); + source.sentBuffer.limit(source.sentBuffer.capacity()); + source.channel.write(source.sentBuffer, null, this); + } + } + + void CompletionHandler.completed(object result, object attachment) => completed(result as Integer, attachment); + + public void failed(System.Exception exc, object attachment) + { + (exc as Throwable)?.printStackTrace(); + CloseUnchecked(source.channel); + } + } + } + + /** + * Read bytes from a channel until EOF is received. + */ + sealed class Sink + { + + readonly AsynchronousByteChannel channel; + readonly ByteBuffer readBuffer; + long bytesRead; + + internal Sink(AsynchronousByteChannel channel) + { + this.channel = channel; + this.readBuffer = ByteBuffer.allocate(1024); + } + + internal void start() + { + channel.read(readBuffer, null, new Completion(this)); + } + + internal long Finish() + { + WaitUntilClosed(channel); + return bytesRead; + } + + class Completion : CompletionHandler + { + + readonly Sink sink; + + public Completion(Sink @this) + { + this.sink = @this; + } + + public void completed(Integer nread, object _) + { + if (nread.intValue() < 0) + { + CloseUnchecked(sink.channel); + } + else + { + sink.bytesRead += nread.intValue(); + sink.readBuffer.clear(); + sink.channel.read(sink.readBuffer, null, this); + } + } + + void CompletionHandler.completed(object result, object attachment) => completed(result as Integer, attachment); + + public void failed(System.Exception exc, object att) + { + (exc as Throwable)?.printStackTrace(); + CloseUnchecked(sink.channel); + } + + } + + } + + static void WaitUntilClosed(Channel ch) + { + while (ch.isOpen()) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + + } + } + } + + static void CloseUnchecked(Channel ch) + { + try + { + ch.close(); + } + catch (IOException e) + { + + } + } + + [TestMethod] + public void ShouldTimeoutOnRead() + { + // listen for connection + using var listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + var port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); + + // initiate connection + var sendChannel = AsynchronousSocketChannel.open(); + sendChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)).get(); + + // accept connection + var recvChannel = (AsynchronousSocketChannel)listener.accept().get(); + + // start a read + var buf = ByteBuffer.allocate(512); + var hnd = new AwaitableCompletionHandler(); + sendChannel.read(buf, 1, TimeUnit.SECONDS, null, hnd); + + // after 2 seconds should have thrown timeout + Thread.sleep(2000); + hnd.GetAwaiter().Invoking(a => a.GetResult()).Should().Throw(); + + // the next attempt should thrown an unspecified runtime exception + sendChannel.Invoking(c => c.read(buf)).Should().Throw(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/channels/DatagramChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/DatagramChannelTests.cs index e5c4795ee9..7998d8f698 100644 --- a/src/IKVM.Tests/Java/java/nio/channels/DatagramChannelTests.cs +++ b/src/IKVM.Tests/Java/java/nio/channels/DatagramChannelTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using java.net; +using java.nio; using java.nio.channels; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -42,6 +43,41 @@ public void CanBindToSamePortWithReuse() ch2.bind(ch.getLocalAddress()); } + [TestMethod] + public void CanSendWhileConnected() + { + using var sndChannel = DatagramChannel.open(); + sndChannel.socket().bind(null); + + var address = InetAddress.getLocalHost(); + if (address.isLoopbackAddress()) + address = InetAddress.getLoopbackAddress(); + + var sender = new InetSocketAddress(address, sndChannel.socket().getLocalPort()); + + using var rcvChannel = DatagramChannel.open(); + rcvChannel.socket().bind(null); + var receiver = new InetSocketAddress(address, rcvChannel.socket().getLocalPort()); + + rcvChannel.connect(sender); + sndChannel.connect(receiver); + + var bb = ByteBuffer.allocate(256); + bb.put(System.Text.Encoding.ASCII.GetBytes("hello")); + bb.flip(); + + var sent = sndChannel.send(bb, receiver); + bb.clear(); + rcvChannel.receive(bb); + bb.flip(); + + var cb = System.Text.Encoding.ASCII.GetString(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining()); + cb.Should().Be("hello"); + + rcvChannel.close(); + sndChannel.close(); + } + } } \ No newline at end of file diff --git a/src/IKVM.Tests/Java/java/nio/channels/FileChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/FileChannelTests.cs index 07981be9ea..1d7c8a5a45 100644 --- a/src/IKVM.Tests/Java/java/nio/channels/FileChannelTests.cs +++ b/src/IKVM.Tests/Java/java/nio/channels/FileChannelTests.cs @@ -1,4 +1,14 @@ -using System.IO; +using System.Security.Cryptography; + +using FluentAssertions; + +using java.io; +using java.lang; +using java.nio; +using java.nio.channels; +using java.nio.charset; +using java.nio.file; +using java.util; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -9,18 +19,285 @@ namespace IKVM.Tests.Java.java.nio.channels public class FileChannelTests { + static RandomNumberGenerator rng = RandomNumberGenerator.Create(); + + void WriteRandomBytes(File f, int len) + { + var buf = new byte[len]; + rng.GetBytes(buf); + using var s = new FileOutputStream(f); + s.write(buf); + } + [TestMethod] - public void Can_open_filechannel_with_write_and_truncate() + public void CanTruncateAndWrite() { - var f = new global::java.io.File(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); + var f = new File("a.txt"); f.createNewFile(); - var o = new global::java.util.HashSet(); - o.add(global::java.nio.file.StandardOpenOption.WRITE); - o.add(global::java.nio.file.StandardOpenOption.TRUNCATE_EXISTING); - var c = global::java.nio.channels.FileChannel.open(f.toPath(), o); + var o = new HashSet(); + o.add(StandardOpenOption.WRITE); + o.add(StandardOpenOption.TRUNCATE_EXISTING); + var c = FileChannel.open(f.toPath(), o); + var b = ByteBuffer.allocate(1); + b.put(1); + b.flip(); + c.write(b); c.close(); } + [TestMethod] + public void CanSetPosition() + { + var generator = new Random(); + var blah = Files.createTempFile("blah", null); + blah.toFile().deleteOnExit(); + + // write lines of numbers + using (var awriter = Files.newBufferedWriter(blah, Charset.forName("8859_1"))) + { + for (int i = 0; i < 4000; i++) + { + awriter.write(i.ToString().PadLeft(4, '0')); + awriter.newLine(); + } + } + + for (int i = 0; i < 10; i++) + { + // try to open with either FileChannel.open or FileInputStream.getChannel + using var fc = generator.nextBoolean() ? FileChannel.open(blah, StandardOpenOption.READ) : new FileInputStream(blah.toFile()).getChannel(); + + for (int j = 0; j < 100; j++) + { + var newPos = generator.nextInt(1000); + fc.position(newPos); + fc.position().Should().Be(newPos); + } + } + + for (int i = 0; i < 10; i++) + { + // try to open with either FileChannel.open or FileOutputStream.getChannel + using var fc = generator.nextBoolean() ? FileChannel.open(blah, StandardOpenOption.APPEND) : new FileOutputStream(blah.toFile(), true).getChannel(); + + for (int j = 0; j < 10; j++) + { + // append should set position to end of file + fc.position().Should().Be(fc.size()); + + // append some data, next round will be open to a new position + var buf = new byte[generator.nextInt(100)]; + fc.write(ByteBuffer.wrap(buf)); + } + } + + Files.delete(blah); + } + + [TestMethod] + public void RandomAccessFileShouldRetainPosition() + { + var rand = new Random(); + + // generate temporary file + var path = File.createTempFile("blah", null); + path.deleteOnExit(); + + var buff = new byte[1024]; + RandomNumberGenerator.Create().GetBytes(buff); + System.IO.File.WriteAllBytes(path.toString(), buff); + + using var raf = new RandomAccessFile(path.toString(), "rw"); + using var channel = raf.getChannel(); + + for (int x = 0; x < 100; x++) + { + var offset = rand.nextInt(1000); + + // write some data from the middle of the file + var bb = ByteBuffer.allocate(4); + + // write known sequence out + for (byte i = 0; i < 4; i++) + bb.put(i); + + bb.flip(); + + var originalPosition = channel.position(); + + int totalWritten = 0; + while (totalWritten < 4) + { + int written = channel.write(bb, offset); + if (written < 0) + throw new Exception("Write failed"); + + totalWritten += written; + } + + long newPosition = channel.position(); + newPosition.Should().Be(originalPosition); + + // attempt to read sequence back in + bb = ByteBuffer.allocateDirect(4); + originalPosition = channel.position(); + int totalRead = 0; + while (totalRead < 4) + { + int read = channel.read(bb, offset); + if (read < 0) + throw new Exception("Read failed"); + + totalRead += read; + } + + newPosition = channel.position(); + newPosition.Should().Be(originalPosition); + + for (byte i = 0; i < 4; i++) + { + if (bb.get(i) != i) + throw new Exception("Write test failed"); + } + } + + path.delete(); + } + + [TestMethod] + public void CanTransferFromFileInputStreamToFileOutputStream() + { + var srcPath = File.createTempFile("src", null); + srcPath.deleteOnExit(); + WriteRandomBytes(srcPath, 1024); + + var dstPath = File.createTempFile("dst", null); + dstPath.deleteOnExit(); + + using var srcStream = new FileInputStream(srcPath); + using var srcChannel = srcStream.getChannel(); + + using var dstStream = new FileOutputStream(dstPath); + using var dstChannel = dstStream.getChannel(); + + var n = dstChannel.transferFrom(srcChannel, 0, int.MaxValue); + + var f1 = Files.readAllBytes(srcPath.toPath()); + var f2 = Files.readAllBytes(dstPath.toPath()); + Arrays.equals(f1, f2).Should().BeTrue(); + } + + [TestMethod] + public void CanTransferToFileInputStreamFromFileOutputStream() + { + var srcPath = File.createTempFile("src", null); + srcPath.deleteOnExit(); + WriteRandomBytes(srcPath, 1024); + + var dstPath = File.createTempFile("dst", null); + dstPath.deleteOnExit(); + + using var srcStream = new FileInputStream(srcPath); + using var srcChannel = srcStream.getChannel(); + + using var dstStream = new FileOutputStream(dstPath); + using var dstChannel = dstStream.getChannel(); + + var n = srcChannel.transferTo(0, int.MaxValue, dstChannel); + + var f1 = Files.readAllBytes(srcPath.toPath()); + var f2 = Files.readAllBytes(dstPath.toPath()); + Arrays.equals(f1, f2).Should().BeTrue(); + } + + [TestMethod] + public void CanTransferToFileOutputStreamFromRandomAccessFile() + { + var srcPath = File.createTempFile("src", null); + srcPath.deleteOnExit(); + WriteRandomBytes(srcPath, 1024); + + var dstPath = File.createTempFile("dst", null); + dstPath.deleteOnExit(); + + using var srcFile = new RandomAccessFile(srcPath, "r"); + using var srcChannel = srcFile.getChannel(); + + using var dstStream = new FileOutputStream(dstPath); + using var dstChannel = dstStream.getChannel(); + + var n = srcChannel.transferTo(0, int.MaxValue, dstChannel); + + var f1 = Files.readAllBytes(srcPath.toPath()); + var f2 = Files.readAllBytes(dstPath.toPath()); + Arrays.equals(f1, f2).Should().BeTrue(); + } + + [TestMethod] + public void CanTransferToRandomAccessFileFromFileOutputStream() + { + var srcPath = File.createTempFile("src", null); + srcPath.deleteOnExit(); + WriteRandomBytes(srcPath, 1024); + + var dstPath = File.createTempFile("dst", null); + dstPath.deleteOnExit(); + + using var srcStream = new FileInputStream(srcPath); + using var srcChannel = srcStream.getChannel(); + + using var dstFile = new RandomAccessFile(dstPath, "rw"); + using var dstChannel = dstFile.getChannel(); + + var n = srcChannel.transferTo(0, int.MaxValue, dstChannel); + + var f1 = Files.readAllBytes(srcPath.toPath()); + var f2 = Files.readAllBytes(dstPath.toPath()); + Arrays.equals(f1, f2).Should().BeTrue(); + } + + [TestMethod] + public void CanTransferToRandomAccessFileFromRandomAccessFile() + { + var srcPath = File.createTempFile("src", null); + srcPath.deleteOnExit(); + WriteRandomBytes(srcPath, 1024); + + var dstPath = File.createTempFile("dst", null); + dstPath.deleteOnExit(); + + using var srcFile = new RandomAccessFile(srcPath, "r"); + using var srcChannel = srcFile.getChannel(); + + using var dstFile = new RandomAccessFile(dstPath, "rw"); + using var dstChannel = dstFile.getChannel(); + + var n = srcChannel.transferTo(0, int.MaxValue, dstChannel); + + var f1 = Files.readAllBytes(srcPath.toPath()); + var f2 = Files.readAllBytes(dstPath.toPath()); + Arrays.equals(f1, f2).Should().BeTrue(); + } + + [TestMethod] + public void CanReadAndWriteMemoryMappedFile() + { + var file = File.createTempFile("src", null); + file.deleteOnExit(); + WriteRandomBytes(file, 1024); + + using var mmap = new RandomAccessFile(file, "rw"); + var buff = mmap.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024); + + for (int i = 0; i < 1024; i++) + buff.put((byte)'A'); + + buff.flip(); + + for (int i = 0; i < 1024; i++) + buff.get(i).Should().Be((byte)'A'); + } + } } diff --git a/src/IKVM.Tests/Java/java/nio/channels/SelectorTests.cs b/src/IKVM.Tests/Java/java/nio/channels/SelectorTests.cs index c73763919d..935b4601c9 100644 --- a/src/IKVM.Tests/Java/java/nio/channels/SelectorTests.cs +++ b/src/IKVM.Tests/Java/java/nio/channels/SelectorTests.cs @@ -21,107 +21,6 @@ namespace IKVM.Tests.Java.java.nio.channels public class SelectorTests { - [TestMethod] - public async Task Can_use_selector_to_answer_client_requests() - { - // NIO currently fails on Linux - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return; - - var cancellationTokenSource = new CancellationTokenSource(); - var messages = new List(); - - // server runs and waits for messages from clients - var serverTask = Task.Run(() => - { - var server = ServerSocketChannel.open(); - var serverAddr = new InetSocketAddress("0.0.0.0", 42341); - server.bind(serverAddr); - server.configureBlocking(false); - var ops = server.validOps(); - cancellationTokenSource.Token.Register(() => server.close()); - - var selector = Selector.open(); - var server1Key = server.register(selector, ops, null); - - while (cancellationTokenSource.Token.IsCancellationRequested == false) - { - selector.select(); - var keys = selector.selectedKeys(); - var iter = keys.iterator(); - while (iter.hasNext()) - { - try - { - var key = (SelectionKey)iter.next(); - - if (key.isAcceptable()) - { - var client = server.accept(); - client.configureBlocking(false); - client.register(selector, SelectionKey.OP_READ); - } - else if (key.isReadable()) - { - var client = (SocketChannel)key.channel(); - var buffer = ByteBuffer.allocate(256); - client.read(buffer); - var data = (byte[])buffer.array(); - var term = Array.FindIndex(data, i => i == 0x00); - var result = Encoding.UTF8.GetString(data, 0, term); - messages.Add(result); - - if (result == "BYE") - client.close(); - } - } - catch (Exception e) - { - - } - - iter.remove(); - } - } - - server.close(); - }); - - await Task.Delay(100); - - // client sends 3 messages and then BYE to the server - var clientTask = Task.Run(() => - { - var addr = new InetSocketAddress("127.0.0.1", 42341); - var sock = SocketChannel.open(addr); - - foreach (var i in new[] { "MESSAGEA", "MESSAGEB", "MESSAGEC", "BYE" }) - { - var data = new byte[Encoding.UTF8.GetByteCount(i) + 1]; - Encoding.UTF8.GetBytes(i).CopyTo(data, 0); - data[data.Length - 1] = 0x00; - var buffer = ByteBuffer.wrap(data); - sock.write(buffer); - buffer.clear(); - Thread.Sleep(500); - } - - sock.close(); - }); - - await Task.Delay(5000); - cancellationTokenSource.Cancel(); - - await clientTask; - await serverTask; - - messages.Should().HaveCount(4); - messages[0].Should().Be("MESSAGEA"); - messages[1].Should().Be("MESSAGEB"); - messages[2].Should().Be("MESSAGEC"); - messages[3].Should().Be("BYE"); - } - } } diff --git a/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs new file mode 100644 index 0000000000..4c0df169b0 --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/channels/ServerSocketChannelTests.cs @@ -0,0 +1,202 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using FluentAssertions; + +using java.net; +using java.nio; +using java.nio.channels; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.channels +{ + + [TestClass] + public class ServerSocketChannelTests + { + + [TestMethod] + public void CanOpenAndClose() + { + using var ssc = ServerSocketChannel.open(); + ssc.configureBlocking(false); + ssc.close(); + } + + /// + /// Check that we can conduct a non-blocking accept. + /// + /// + [TestMethod] + public void NonBlockingAccept() + { + using var ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(0)); + ssc.configureBlocking(false); + var ss = ssc.socket(); + + // exception should be thrown when no connection is waiting + try + { + ss.accept(); + throw new Exception("Expected exception not thrown"); + } + catch (IllegalBlockingModeException) + { + // correct + } + + // no exception should be thrown when a connection is waiting + using var sc = SocketChannel.open(); + sc.configureBlocking(false); + sc.connect(ss.getLocalSocketAddress()); + Thread.Sleep(100); + ss.accept(); + } + + /// + /// Runs a server socket, spawns a client, and attempts to transfer some data. + /// + /// + [TestMethod] + public async Task CanReceiveBlocking() + { + var cancellationTokenSource = new CancellationTokenSource(); + var receive = ByteBuffer.allocate(sizeof(int) * 4); + + // server receives messages until cancelled + var serverTask = Task.Run(() => + { + // initialize server + using var server = ServerSocketChannel.open(); + var serverAddr = new InetSocketAddress(42341); + server.bind(serverAddr); + server.configureBlocking(true); + + // accept the first socket + var c = server.accept(); + + // read into receive buffer + while (c.read(receive) != -1 && cancellationTokenSource.Token.IsCancellationRequested == false) + continue; + }); + + // wait a second and write some messages to the server + await Task.Delay(1000); + using (var c = SocketChannel.open(new InetSocketAddress("127.0.0.1", 42341))) + { + foreach (var i in new[] { 1, 2, 3, 4 }) + { + var b = ByteBuffer.allocate(sizeof(int)); + b.putInt(i); + b.flip(); + c.write(b); + + // small delay to allow server to receive as multiple packets + await Task.Delay(100); + } + } + + // wait for the server to receive them and then exit + cancellationTokenSource.Cancel(); + await serverTask; + + // check that we received 4 ints + receive.flip(); + receive.getInt().Should().Be(1); + receive.getInt().Should().Be(2); + receive.getInt().Should().Be(3); + receive.getInt().Should().Be(4); + } + + /// + /// Runs a server socket, spawns a client, and attempts to transfer some data. + /// + /// + [TestMethod] + public async Task CanReceiveNonBlocking() + { + var cancellationTokenSource = new CancellationTokenSource(); + var receive = ByteBuffer.allocate(sizeof(int) * 4); + + // server receives messages until cancelled + var serverTask = Task.Run(() => + { + // initialize server + using var server = ServerSocketChannel.open(); + var serverAddr = new InetSocketAddress(42342); + server.bind(serverAddr); + server.configureBlocking(false); + + // begin selector + var selector = Selector.open(); + var serverKey = server.register(selector, server.validOps(), null); + + // continue until cancelled + while (cancellationTokenSource.Token.IsCancellationRequested == false) + { + selector.select(); + + var keys = selector.selectedKeys(); + var iter = keys.iterator(); + while (iter.hasNext()) + { + try + { + var k = (SelectionKey)iter.next(); + + if (k.isAcceptable()) + { + var c = server.accept(); + c.configureBlocking(false); + c.register(selector, SelectionKey.OP_READ); + } + else if (k.isReadable()) + { + var c = (SocketChannel)k.channel(); + var n = c.read(receive); + } + } + catch (Exception) + { + + } + + iter.remove(); + } + } + }); + + // wait a second and write some messages to the server + await Task.Delay(1000); + using (var c = SocketChannel.open(new InetSocketAddress("127.0.0.1", 42342))) + { + foreach (var i in new[] { 1, 2, 3, 4 }) + { + var b = ByteBuffer.allocate(sizeof(int)); + b.putInt(i); + b.flip(); + c.write(b); + + // small delay to allow server to receive as multiple packets + await Task.Delay(100); + } + } + + // wait for the server to receive them and then exit + cancellationTokenSource.Cancel(); + await serverTask; + + // check that we received 4 ints + receive.flip(); + receive.getInt().Should().Be(1); + receive.getInt().Should().Be(2); + receive.getInt().Should().Be(3); + receive.getInt().Should().Be(4); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/channels/SocketChannelTests.cs b/src/IKVM.Tests/Java/java/nio/channels/SocketChannelTests.cs new file mode 100644 index 0000000000..623fbc78ab --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/channels/SocketChannelTests.cs @@ -0,0 +1,148 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using java.io; +using java.net; +using java.nio; +using java.nio.channels; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.channels +{ + + [TestClass] + public class SocketChannelTests + { + + [TestMethod] + public async Task CanSendUrgentData() + { + var cancellationTokenSource = new CancellationTokenSource(); + var receive = ByteBuffer.allocate(1024); + + using var ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(0)); + + var sst = Task.Run(() => + { + // accept first connect + var sc = ssc.accept(); + + // read into receive buffer + while (sc.read(receive) != -1 && cancellationTokenSource.Token.IsCancellationRequested == false) + continue; + }); + + // wait a bit and write some messages to the server + await Task.Delay(100); + using (var sc = SocketChannel.open(new InetSocketAddress(((InetSocketAddress)ssc.getLocalAddress()).getPort()))) + { + sc.configureBlocking(false); + sc.socket().setOOBInline(false); + + foreach (var i in new byte[] { 1, 2, 3, 4 }) + { + sc.socket().sendUrgentData(i); + await Task.Delay(100); + } + } + + cancellationTokenSource.Cancel(); + await sst; + } + + /// + /// Meant to mirror a similar test in OpenJDK which checks for specific error messages. + /// java/nio/channels/SocketChannel/SendUrgentData.java + /// + /// + [TestMethod] + public void SendUrgentDataThrowsWhenBlocked() + { + using var ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(0)); + + using (var sc = SocketChannel.open(new InetSocketAddress(((InetSocketAddress)ssc.getLocalAddress()).getPort()))) + { + sc.configureBlocking(false); + sc.socket().setOOBInline(false); + + var bb = ByteBuffer.wrap(new byte[100 * 1000]); + var blocked = 0L; + var total = 0L; + + // write data until we block, indicating send buffers are full + var n = 0; + do + { + n = sc.write(bb); + if (n == 0) + { + if (++blocked == 10) + break; + + Thread.Sleep(100); + } + else + { + total += n; + bb.rewind(); + } + } while (n > 0); + + // attempt to sendUrgentData to socket that is blocked, and affirm resulting exception + var attempted = 0; + while (attempted < total) + { + bb.rewind(); + n = sc.write(bb); + attempted += bb.capacity(); + + var osName = global::java.lang.System.getProperty("os.name").ToLowerInvariant(); + + try + { + sc.socket().sendUrgentData(0); + } + catch (IOException ex) + { + if (osName.Contains("linux")) + { + if (!ex.getMessage().Contains("Socket buffer full")) + { + throw new Exception("Unexpected message", ex); + } + } + else if (osName.Contains("os x") || osName.Contains("mac")) + { + if (!ex.getMessage().Equals("No buffer space available")) + { + throw new Exception("Unexpected message", ex); + } + } + else if (osName.Contains("windows")) + { + if (!(ex is SocketException)) + { + throw new Exception("Unexpected exception", ex); + } + else if (!ex.getMessage().Contains("Resource temporarily unavailable")) + { + throw new Exception("Unexpected message", ex); + } + } + else + { + throw new Exception("Unexpected IOException", ex); + } + } + } + } + + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs b/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs new file mode 100644 index 0000000000..0b3b43329d --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/charset/CharsetTests.cs @@ -0,0 +1,20 @@ +using java.nio.charset; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.charset +{ + + [TestClass] + public class CharsetTests + { + + [TestMethod] + public void CanGetAvailableCharsets() + { + Charset.availableCharsets(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/file/FilesTests.cs b/src/IKVM.Tests/Java/java/nio/file/FilesTests.cs new file mode 100644 index 0000000000..d3d3debc38 --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/file/FilesTests.cs @@ -0,0 +1,107 @@ +using System.Linq; + +using FluentAssertions; + +using IKVM.Runtime.Vfs; + +using java.nio.file; +using java.nio.file.attribute; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.file +{ + + [TestClass] + public class FilesTests + { + + [TestMethod] + public void CanReadAllBytes() + { + var f = global::System.IO.Path.GetTempFileName(); + System.IO.File.WriteAllBytes(f, new byte[] { 1 }); + var b = Files.readAllBytes(Paths.get(f)); + b.Should().HaveCount(1); + b[0].Should().Be(1); + } + + [TestMethod] + public void CanWriteAllBytes() + { + var f = global::System.IO.Path.GetTempFileName(); + Files.write(Paths.get(f), new byte[] { 1 }, StandardOpenOption.WRITE); + var b = System.IO.File.ReadAllBytes(f); + b.Should().HaveCount(1); + b[0].Should().Be(1); + } + + [TestMethod] + public void CanGetSize() + { + var f = global::System.IO.Path.GetTempFileName(); + System.IO.File.WriteAllBytes(f, new byte[] { 1 }); + var s = Files.size(Paths.get(f)); + s.Should().Be(1); + } + + [TestMethod] + public void CanGetIsDirectory() + { + var d = System.IO.Path.GetTempPath(); + Files.isDirectory(Paths.get(d)).Should().Be(true); + var f = System.IO.Path.GetTempFileName(); + Files.isDirectory(Paths.get(f)).Should().Be(false); + } + + [TestMethod] + public void VfsAssemblyClassesDirectoryShouldBeDirectory() + { + var d = VfsTable.Default.GetAssemblyClassesPath(typeof(object).Assembly); + Files.isDirectory(Paths.get(d)).Should().Be(true); + } + + [TestMethod] + public void VfsAssemblyClassesDirectoryCanBeListed() + { + var d = VfsTable.Default.GetAssemblyClassesPath(typeof(object).Assembly); + var l = Files.list(Paths.get(d)).toArray(); + foreach (Path s in l) + System.Console.WriteLine(s); + } + + [TestMethod] + public void VfsAssemblyClassCanBeRead() + { + var f = System.IO.Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly), "java", "lang", "Class.class"); + var b = Files.readAllBytes(Paths.get(f)); + b.Length.Should().BeGreaterOrEqualTo(32); + } + + [TestMethod] + [ExpectedException(typeof(global::java.nio.file.AccessDeniedException))] + public void VfsAssemblyClassCanNotBeWritten() + { + var f = System.IO.Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly), "java", "lang", "Class.class"); + Files.write(Paths.get(f), new byte[] { 1 }); + } + + [TestMethod] + public void VfsAssemblyClassesDirectoryShouldHaveBasicAttributes() + { + var f = VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly); + var a = (BasicFileAttributes)Files.readAttributes(Paths.get(f), typeof(BasicFileAttributes)); + a.isDirectory().Should().BeTrue(); + } + + [TestMethod] + public void VfsAssemblyClassesDirectoryShouldBeWalkable() + { + var f = VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly); + var l = Files.walk(Paths.get(f)).toArray(); + l.Count().Should().BeGreaterOrEqualTo(16); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/nio/file/PathTests.cs b/src/IKVM.Tests/Java/java/nio/file/PathTests.cs new file mode 100644 index 0000000000..8d6c3a9f0f --- /dev/null +++ b/src/IKVM.Tests/Java/java/nio/file/PathTests.cs @@ -0,0 +1,53 @@ +using System.Runtime.InteropServices; + +using FluentAssertions; + +using java.nio.file; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.nio.file +{ + + [TestClass] + public class PathTests + { + + [DataTestMethod] + [DataRow("C:\\", "foo", "C:\\foo")] + [DataRow("C:\\", "D:\\bar", "D:\\bar")] + [DataRow("C:\\", "\\\\server\\share\\bar", "\\\\server\\share\\bar")] + [DataRow("C:\\", "C:foo", "C:\\foo")] + [DataRow("C:\\", "D:foo", "D:foo")] + [DataRow("C:\\", "", "C:\\")] + //[DataRow("C:", "foo", "C:foo")] + [DataRow("C:", "", "C:")] + public void CanResolveWindowsPaths(string path, string other, string expected) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == false) + return; + + Paths.get(path).resolve(other).ToString().Should().Be(expected); + } + + [DataTestMethod] + [DataRow("/tmp", "foo", "/tmp/foo")] + [DataRow("/tmp", "/foo", "/foo")] + [DataRow("/tmp", "", "/tmp")] + [DataRow("tmp", "foo", "tmp/foo")] + [DataRow("tmp", "/foo", "/foo")] + [DataRow("tmp", "", "tmp")] + [DataRow("", "", "")] + [DataRow("", "foo", "foo")] + [DataRow("", "/foo", "/foo")] + public void CanResolveUnixPaths(string path, string other, string expected) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return; + + Paths.get(path).resolve(other).ToString().Should().Be(expected); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/security/AlgorithmTests.cs b/src/IKVM.Tests/Java/java/security/AlgorithmTests.cs new file mode 100644 index 0000000000..0a2feba308 --- /dev/null +++ b/src/IKVM.Tests/Java/java/security/AlgorithmTests.cs @@ -0,0 +1,89 @@ +using System.Text; + +using FluentAssertions; + +using java.security; + +using javax.crypto; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.security +{ + + [TestClass] + public class AlgorithmTests + { + + [DataTestMethod] + [DataRow("DSA", 2048, "SHA1withDSA")] + [DataRow("DSA", 2048, "SHA256withDSA")] + [DataRow("RSA", 2048, "SHA1withRSA")] + [DataRow("RSA", 2048, "SHA256withRSA")] + //[DataRow("EC", 256, "SHA1withECDSA")] + //[DataRow("EC", 384, "SHA1withECDSA")] + //[DataRow("EC", 521, "SHA1withECDSA")] + //[DataRow("EC", 256, "SHA256withECDSA")] + //[DataRow("EC", 384, "SHA256withECDSA")] + //[DataRow("EC", 521, "SHA256withECDSA")] + public void CanCreateKeysAndSignAndVerify(string keyAlgorithm, int keySize, string signatureAlgorithm) + { + // we do this many times because differences in the values can make padding required or not + for (int i = 0; i < 16; i++) + { + var text = Encoding.UTF8.GetBytes("TEST"); + + // create a new key pair + var keyGenerator = KeyPairGenerator.getInstance(keyAlgorithm); + keyGenerator.initialize(keySize); + var keyPair = keyGenerator.generateKeyPair(); + var privateKey = keyPair.getPrivate(); + var publicKey = keyPair.getPublic(); + + // sign some test data with the private key + var signSignature = Signature.getInstance(signatureAlgorithm); + signSignature.initSign(privateKey); + signSignature.update(text); + var signatureText = signSignature.sign(); + + // verify the signature with the public key + var verifySignature = Signature.getInstance(signatureAlgorithm); + verifySignature.initVerify(publicKey); + verifySignature.update(text); + var verifyResult = verifySignature.verify(signatureText); + verifyResult.Should().BeTrue(); + } + } + + [DataTestMethod] + [DataRow("RSA", 2048, "RSA")] + public void CanEncryptAndDecrypt(string keyAlgorithm, int keySize, string cipherAlgorithm) + { + // we do this many times because differences in the values can make padding required or not + for (int i = 0; i < 16; i++) + { + var text = Encoding.UTF8.GetBytes("TEST"); + + // create a new key pair + var keyGenerator = KeyPairGenerator.getInstance(keyAlgorithm); + keyGenerator.initialize(keySize); + var keyPair = keyGenerator.generateKeyPair(); + var privateKey = keyPair.getPrivate(); + var publicKey = keyPair.getPublic(); + + // encrypt data with the given algorithm + var encrypt = Cipher.getInstance(cipherAlgorithm); + encrypt.init(Cipher.ENCRYPT_MODE, privateKey); + var encryptData = encrypt.doFinal(text); + + // decrypt data with the given algorithm + var decrypt = Cipher.getInstance(cipherAlgorithm); + decrypt.init(Cipher.DECRYPT_MODE, publicKey); + var decryptText = decrypt.doFinal(encryptData); + decryptText.Should().BeEquivalentTo(text); + } + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/security/KeystoreTests.cs b/src/IKVM.Tests/Java/java/security/KeystoreTests.cs new file mode 100644 index 0000000000..72add0da22 --- /dev/null +++ b/src/IKVM.Tests/Java/java/security/KeystoreTests.cs @@ -0,0 +1,80 @@ +using java.io; +using java.security; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.security +{ + + [TestClass] + public class KeystoreTests + { + + [DataTestMethod] + [DataRow("JKS", "jks")] + [DataRow("JCEKS", "jceks")] + [DataRow("PKCS12", "p12")] + public void CanInitKeyStore(string type, string ext) + { + var ks = KeyStore.getInstance(type); + ks.load(null, null); + + var dir = new File(System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CanInitKeyStore", type)); + if (System.IO.Directory.Exists(dir.getPath())) + System.IO.Directory.Delete(dir.getPath(), true); + dir.mkdirs(); + + using (var stream = new FileOutputStream(new File(dir, $"empty.{ext}"))) + ks.store(stream, "changeit".ToCharArray()); + } + + [DataTestMethod] + [DataRow("JKS", "jks")] + [DataRow("JCEKS", "jceks")] + [DataRow("PKCS12", "p12")] + public void CanLoadKeyStore(string type, string ext) + { + var dir = new File(System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CanLoadKeyStore", type)); + if (System.IO.Directory.Exists(dir.getPath())) + System.IO.Directory.Delete(dir.getPath(), true); + dir.mkdirs(); + + using (var stream = new FileOutputStream(new File(dir, $"empty.{ext}"))) + { + var ks = KeyStore.getInstance(type); + ks.load(null, null); + ks.store(stream, "changeit".ToCharArray()); + } + + using (var stream = new FileInputStream(new File(dir, $"empty.{ext}"))) + { + var ks = KeyStore.getInstance(type); + ks.load(stream, "changeit".ToCharArray()); + } + } + + [TestMethod] + public void CanLoadP12KeyStoreWithJKS() + { + var dir = new File(System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CanLoadP12KeyStoreWithJKS")); + if (System.IO.Directory.Exists(dir.getPath())) + System.IO.Directory.Delete(dir.getPath(), true); + dir.mkdirs(); + + using (var stream = new FileOutputStream(new File(dir, "empty.p12"))) + { + var ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + ks.store(stream, "changeit".ToCharArray()); + } + + using (var stream = new FileInputStream(new File(dir, "empty.p12"))) + { + var ks = KeyStore.getInstance("JKS"); + ks.load(stream, "changeit".ToCharArray()); + } + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Tests/Java/java/security/SecureRandomTests.cs b/src/IKVM.Tests/Java/java/security/SecureRandomTests.cs new file mode 100644 index 0000000000..cbf6b8b5e1 --- /dev/null +++ b/src/IKVM.Tests/Java/java/security/SecureRandomTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; + +using java.security; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.security +{ + + [TestClass] + public class SecureRandomTests + { + + [TestMethod] + public void CanGenerateBytes() + { + var random = new SecureRandom(); + var bytes = new byte[32]; + random.nextBytes(bytes); + } + + [TestMethod] + public void CanGenerateSeedBytes() + { + var random = new SecureRandom(); + var bytes = random.generateSeed(32); + bytes.Should().HaveCount(32); + } + + [TestMethod] + public void CanGetAndUseInstance() + { + var rnd = new SecureRandom(); + rnd.Should().NotBeNull(); + rnd.generateSeed(32).Should().HaveCount(32); + + var b = new byte[32]; + rnd.nextBytes(b); + + rnd.nextBoolean(); + rnd.nextInt(); + rnd.nextFloat(); + rnd.nextDouble(); + rnd.nextLong(); + } + + [TestMethod] + public void CanGetAndUseStrongInstance() + { + var rnd = SecureRandom.getInstanceStrong(); + rnd.Should().NotBeNull(); + rnd.generateSeed(32).Should().HaveCount(32); + + var b = new byte[32]; + rnd.nextBytes(b); + + rnd.nextBoolean(); + rnd.nextInt(); + rnd.nextLong(); + rnd.nextFloat(); + rnd.nextDouble(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/security/SecurityTests.cs b/src/IKVM.Tests/Java/java/security/SecurityTests.cs index 586ac38d81..b3699c9f78 100644 --- a/src/IKVM.Tests/Java/java/security/SecurityTests.cs +++ b/src/IKVM.Tests/Java/java/security/SecurityTests.cs @@ -10,7 +10,7 @@ public class SecurityTests { [TestMethod] - public void Can_list_providers() + public void CanGetProviders() { var l = global::java.security.Security.getProviders(); l.Should().HaveCount(8); diff --git a/src/IKVM.Tests/Java/java/time/ZoneIdTests.cs b/src/IKVM.Tests/Java/java/time/ZoneIdTests.cs new file mode 100644 index 0000000000..fabe1b204b --- /dev/null +++ b/src/IKVM.Tests/Java/java/time/ZoneIdTests.cs @@ -0,0 +1,25 @@ +using FluentAssertions; + +using java.time; +using java.util; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.time +{ + + [TestClass] + public class ZoneIdTests + { + + [TestMethod] + public void SystemDefaultZoneIdShouldMatchTimeZone() + { + var test1 = ZoneId.systemDefault(); + var test2 = TimeZone.getDefault(); + test1.getId().Should().Be(test2.getID()); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/util/concurrent/ConcurrentHashMapTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/ConcurrentHashMapTests.cs new file mode 100644 index 0000000000..9c6e3b66c9 --- /dev/null +++ b/src/IKVM.Tests/Java/java/util/concurrent/ConcurrentHashMapTests.cs @@ -0,0 +1,26 @@ +using FluentAssertions; + +using java.util.concurrent; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.util.concurrent +{ + + [TestClass] + public class ConcurrentHashMapTests + { + + [TestMethod] + public void CanPutIfAbsentAndGetOrDefault() + { + var h = new ConcurrentHashMap(); + var o = new object(); + h.clear(); + h.putIfAbsent(1, o); + h.getOrDefault(1, null).Should().BeSameAs(o); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicTests.cs new file mode 100644 index 0000000000..6f8a65da51 --- /dev/null +++ b/src/IKVM.Tests/Java/java/util/concurrent/atomic/AtomicTests.cs @@ -0,0 +1,129 @@ +using System.Linq; +using System.Threading.Tasks; + +using FluentAssertions; + +using IKVM.Attributes; + +using java.lang; +using java.util.concurrent.atomic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.util.concurrent.atomic +{ + + [TestClass] + public class AtomicTests + { + + [TestMethod] + public void CanLazySetBoolean() + { + var o = new AtomicBoolean(false); + Task.Run(() => o.lazySet(true)).Wait(); + o.get().Should().Be(true); + } + + [TestMethod] + public void CanLazySetInteger() + { + var o = new AtomicInteger(0); + Task.Run(() => o.lazySet(1)).Wait(); + o.get().Should().Be(1); + } + + [TestMethod] + public void CanLazySetLong() + { + var o = new AtomicLong(0); + Task.Run(() => o.lazySet(1)).Wait(); + o.get().Should().Be(1); + } + + class TestObject + { + + public volatile object oo = null; + public volatile string ss = null; + public volatile int ii = 0; + + [Modifiers(Modifiers.Volatile)] + public long ll = 0; + + } + + [TestMethod] + public void CanLazySetObjectField() + { + var t = new object(); + var u = AtomicReferenceFieldUpdater.newUpdater(typeof(TestObject), typeof(object), "oo"); + var o = new TestObject(); + Task.Run(() => u.lazySet(o, t)).Wait(); + o.oo.Should().BeSameAs(t); + } + + [TestMethod] + public void CanLazySetStringField() + { + var t = "TEST"; + var u = AtomicReferenceFieldUpdater.newUpdater(typeof(TestObject), typeof(string), "ss"); + var o = new TestObject(); + Task.Run(() => u.lazySet(o, t)).Wait(); + o.ss.Should().BeSameAs(t); + } + + [TestMethod] + public void CanLazySetIntegerField() + { + var u = AtomicIntegerFieldUpdater.newUpdater(typeof(TestObject), "ii"); + var o = new TestObject(); + Task.Run(() => u.lazySet(o, 1)).Wait(); + o.ii.Should().Be(1); + } + + [TestMethod] + public void CanLazySetLongField() + { + var u = AtomicLongFieldUpdater.newUpdater(typeof(TestObject), "ll"); + var o = new TestObject(); + Task.Run(() => u.lazySet(o, 1)).Wait(); + o.ll.Should().Be(1); + } + + [TestMethod] + public void CanLazySetReferenceArray() + { + var t = new object(); + var a = new AtomicReferenceArray(1); + Task.Run(() => a.lazySet(0, t)).Wait(); + a.get(0).Should().BeSameAs(t); + } + + [TestMethod] + public void CanLazySetIntegerArray() + { + var a = new AtomicIntegerArray(1); + Task.Run(() => a.lazySet(0, 1)).Wait(); + a.get(0).Should().Be(1); + } + + [TestMethod] + public void CanLazySetLongArray() + { + var a = new AtomicLongArray(1); + Task.Run(() => a.lazySet(0, 1)).Wait(); + a.get(0).Should().Be(1); + } + + [TestMethod] + public void CanStressAtomicInt() + { + var i = new AtomicInteger(0); + Enumerable.Range(0, 1024).AsParallel().WithDegreeOfParallelism(16).ForAll(_ => i.getAndIncrement()); + i.get().Should().Be(1024); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/util/concurrent/atomic/DoubleAdderTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/atomic/DoubleAdderTests.cs new file mode 100644 index 0000000000..c1b70b66f2 --- /dev/null +++ b/src/IKVM.Tests/Java/java/util/concurrent/atomic/DoubleAdderTests.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +using FluentAssertions; + +using java.util.concurrent.atomic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.util.concurrent.atomic +{ + + [TestClass] + public class DoubleAdderTests + { + + [TestMethod] + public void ShouldSumAcrossMultipleThreads() + { + var taskCount = 8; + var incrCount = 128; + var counter = new DoubleAdder(); + + void Action() + { + for (int i = 0; i < incrCount; i++) + counter.add(1d); + } + + var l = new List(); + for (int i = 0; i < taskCount; i++) + l.Add(Task.Run(Action)); + Task.WhenAll(l).Wait(); + + counter.sumThenReset().Should().Be(taskCount * incrCount); + counter.sum().Should().Be(0); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/util/concurrent/atomic/LongAdderTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/atomic/LongAdderTests.cs new file mode 100644 index 0000000000..08def5b0de --- /dev/null +++ b/src/IKVM.Tests/Java/java/util/concurrent/atomic/LongAdderTests.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +using FluentAssertions; + +using java.util.concurrent.atomic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.util.concurrent.atomic +{ + + [TestClass] + public class LongAdderTests + { + + [TestMethod] + public void ShouldSumAcrossMultipleThreads() + { + var taskCount = 8; + var incrCount = 128; + + var counter = new LongAdder(); + + void Action() + { + for (int i = 0; i < incrCount; i++) + counter.increment(); + } + + var l = new List(); + for (int i = 0; i < taskCount; i++) + l.Add(Task.Run(Action)); + Task.WhenAll(l).Wait(); + + counter.sumThenReset().Should().Be(taskCount * incrCount); + counter.sum().Should().Be(0); + } + + } + +} diff --git a/src/IKVM.Tests/Java/java/util/concurrent/locks/StampedLockTests.cs b/src/IKVM.Tests/Java/java/util/concurrent/locks/StampedLockTests.cs new file mode 100644 index 0000000000..7866a1a754 --- /dev/null +++ b/src/IKVM.Tests/Java/java/util/concurrent/locks/StampedLockTests.cs @@ -0,0 +1,97 @@ +using java.lang; +using java.util.concurrent; +using java.util.concurrent.locks; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.java.util.concurrent.locks +{ + + [TestClass] + public class StampedLockTests + { + + class ReadersUnlockAfterWriterUnlockContext + { + + const int RNUM = 2; + + readonly StampedLock sl = new StampedLock(); + volatile bool isDone; + + readonly CyclicBarrier iterationStart = new CyclicBarrier(RNUM + 1); + readonly CyclicBarrier readersHaveLocks = new CyclicBarrier(RNUM); + readonly CyclicBarrier writerHasLock = new CyclicBarrier(RNUM + 1); + + class Reader : Thread + { + + readonly ReadersUnlockAfterWriterUnlockContext owner; + + /// + /// Initializes a new instance. + /// + /// + /// + public Reader(ReadersUnlockAfterWriterUnlockContext owner, string name) : base(name) + { + this.owner = owner; + } + + public override void run() + { + while (!owner.isDone && !isInterrupted()) + { + try + { + owner.iterationStart.await(); + owner.writerHasLock.await(); + var rs = owner.sl.readLock(); + owner.readersHaveLocks.await(); + owner.sl.unlockRead(rs); + } + catch (System.Exception e) + { + throw new IllegalStateException(e); + } + } + } + + } + + public void Run() + { + for (int r = 0; r < RNUM; ++r) + new Reader(this, "r" + r).start(); + + int i; + for (i = 0; i < 1024; ++i) + { + try + { + iterationStart.await(); + var ws = sl.writeLock(); + writerHasLock.await(); + Thread.sleep(10); + sl.unlockWrite(ws); + } + catch (System.Exception e) + { + throw new IllegalStateException(e); + } + } + + isDone = true; + } + + } + + [TestMethod] + public void ReadersUnlockAfterWriterUnlock() + { + new ReadersUnlockAfterWriterUnlockContext().Run(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/javax/script/NashornAccessTests.cs b/src/IKVM.Tests/Java/javax/script/NashornAccessTests.cs new file mode 100644 index 0000000000..edf0f24068 --- /dev/null +++ b/src/IKVM.Tests/Java/javax/script/NashornAccessTests.cs @@ -0,0 +1,36 @@ +using FluentAssertions; + +using javax.script; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.javax.script +{ + + [TestClass] + public class NashornAccessTests + { + + class SharedObject + { + + public string publicString = "PublicString"; + + } + + static ScriptEngine e; + static SharedObject o; + + [TestInitialize] + public void Initialize() + { + var m = new ScriptEngineManager(); + e = m.getEngineByName("nashorn"); + o = new SharedObject(); + e.put("o", o); + e.eval("var SharedObject = Packages.cli.IKVM.Tests.Java.javax.script.NashornAccessTests$SharedObject;"); + } + + } + +} diff --git a/src/IKVM.Tests/Java/javax/tools/JavaCompilerTests.cs b/src/IKVM.Tests/Java/javax/tools/JavaCompilerTests.cs new file mode 100644 index 0000000000..0e21d3ca75 --- /dev/null +++ b/src/IKVM.Tests/Java/javax/tools/JavaCompilerTests.cs @@ -0,0 +1,53 @@ +using FluentAssertions; + +using IKVM.Tests.Util; + +using java.util; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.javax.tools +{ + + [TestClass] + public class JavaCompilerTests + { + + /// + /// We have encounted a few issues with loading resource bundles for javac. This ensures we can. + /// + [TestMethod] + public void CanGetJavaCompilerResourceBundle() + { + var b = ResourceBundle.getBundle("com.sun.tools.javac.resources.javac"); + var s = b.getString("javac.msg.bug"); + s.Should().NotBeEmpty(); + } + + /// + /// Checks that the compiler can handle nested lambda instances. These produce odd type names, and we have + /// hit issues with various parts of the system not respecting them. + /// + [TestMethod] + public void CanCompileLambda() + { + var s = """ +public class L1 { + public static class A { + private Runnable r = () -> { }; + } + public static class B { + private Runnable r = () -> { }; + } + private Runnable r = () -> { }; +} +"""; + + var f = new InMemoryCodeUnit("L1", s); + var c = new InMemoryCompiler(new[] { f }); + c.Compile(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/sun/misc/JavaSecurityAccessTests.cs b/src/IKVM.Tests/Java/sun/misc/JavaSecurityAccessTests.cs new file mode 100644 index 0000000000..11781fbf86 --- /dev/null +++ b/src/IKVM.Tests/Java/sun/misc/JavaSecurityAccessTests.cs @@ -0,0 +1,59 @@ +using FluentAssertions; + +using java.security; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using sun.misc; + +namespace IKVM.Tests.Java.sun.misc +{ + + [TestClass] + public class JavaSecurityAccessTests + { + + class NullDomainCombiner : DomainCombiner + { + + public ProtectionDomain[] combine(ProtectionDomain[] currentDomains, ProtectionDomain[] assignedDomains) + { + return currentDomains; + } + + } + + class DomainCombinerPrivilegedTestAction : PrivilegedAction + { + + readonly DomainCombiner dc; + + public DomainCombinerPrivilegedTestAction(DomainCombiner dc) + { + this.dc = dc; + } + + public object run() + { + return dc == AccessController.getContext().getDomainCombiner(); + } + + } + + /// + /// Make sure that JavaSecurityAccess.doIntersectionPrivilege() is not dropping the information about the domain combiner of the stack ACC. + /// Imported from jdk.java/security/ProtectionDomain/PreserveCombinerTests + /// + [TestMethod] + public void ShouldPreserveDomainCombiner() + { + var dc = new NullDomainCombiner(); + var saved = AccessController.getContext(); + var stack = new AccessControlContext(AccessController.getContext(), dc); + var ret = (bool)SharedSecrets.getJavaSecurityAccess().doIntersectionPrivilege(new DomainCombinerPrivilegedTestAction(dc), stack, saved); + ret.Should().BeTrue(); + } + + } + +} diff --git a/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs b/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs new file mode 100644 index 0000000000..76a0fea3cf --- /dev/null +++ b/src/IKVM.Tests/Java/sun/misc/UnsafeTests.cs @@ -0,0 +1,1504 @@ +using System; +using System.Runtime.InteropServices; + +using FluentAssertions; + +using java.lang; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using sun.misc; + +namespace IKVM.Tests.Java.sun.misc +{ + + [TestClass] + public class UnsafeTests + { + + static readonly Unsafe u; + + /// + /// Initializes the static instance. + /// + static UnsafeTests() + { + var f = ((Class)typeof(Unsafe)).getDeclaredField("theUnsafe"); + f.setAccessible(true); + u = (Unsafe)f.get(null); + } + + class TestObject + { + + public object objectField = null; + public string stringField = null; + public bool booleanField = false; + public byte byteField = 0; + public char charField = '\0'; + public short shortField = 0; + public int intField = 0; + public long longField = 0; + public float floatField = 0; + public double doubleField = 0; + + } + + [TestMethod] + public void CanSetObjectField() + { + var o = new TestObject(); + var t = new object(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("objectField")); + u.getObject(o, f).Should().BeSameAs(null); + u.putObject(o, f, t); + u.getObject(o, f).Should().BeSameAs(t); + o.objectField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStringField() + { + var o = new TestObject(); + var t = "TEST"; + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("stringField")); + u.getObject(o, f).Should().BeSameAs(null); + u.putObject(o, f, t); + u.getObject(o, f).Should().BeSameAs(t); + o.stringField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetBooleanField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("booleanField")); + u.getBoolean(o, f).Should().Be(false); + u.putBoolean(o, f, true); + u.getBoolean(o, f).Should().Be(true); + o.booleanField.Should().Be(true); + } + + [TestMethod] + public void CanSetByteField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("byteField")); + u.getByte(o, f).Should().Be(0); + u.putByte(o, f, 1); + u.getByte(o, f).Should().Be(1); + o.byteField.Should().Be(1); + } + + [TestMethod] + public void CanSetCharField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("charField")); + u.getChar(o, f).Should().Be('\0'); + u.putChar(o, f, 'A'); + u.getChar(o, f).Should().Be('A'); + o.charField.Should().Be('A'); + } + + [TestMethod] + public void CanSetShortField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("shortField")); + u.getShort(o, f).Should().Be(0); + u.putShort(o, f, 1); + u.getShort(o, f).Should().Be(1); + o.shortField.Should().Be(1); + } + + [TestMethod] + public void CanSetIntField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("intField")); + u.getInt(o, f).Should().Be(0); + u.putInt(o, f, 1); + u.getInt(o, f).Should().Be(1); + o.intField.Should().Be(1); + } + + [TestMethod] + public void CanSetLongField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("longField")); + u.getLong(o, f).Should().Be(0); + u.putLong(o, f, 1); + u.getLong(o, f).Should().Be(1); + o.longField.Should().Be(1); + } + + [TestMethod] + public void CanSetFloatField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("floatField")); + u.getFloat(o, f).Should().Be(0); + u.putFloat(o, f, 1); + u.getFloat(o, f).Should().Be(1); + o.floatField.Should().Be(1); + } + + [TestMethod] + public void CanSetDoubleField() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("doubleField")); + u.getDouble(o, f).Should().Be(0); + u.putDouble(o, f, 1); + u.getDouble(o, f).Should().Be(1); + o.doubleField.Should().Be(1); + } + + [TestMethod] + public void CanSetObjectFieldVolatile() + { + var o = new TestObject(); + var t = new object(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("objectField")); + u.getObjectVolatile(o, f).Should().BeSameAs(null); + u.putObjectVolatile(o, f, t); + u.getObjectVolatile(o, f).Should().BeSameAs(t); + o.objectField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStringFieldVolatile() + { + var o = new TestObject(); + var t = "TEST"; + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("stringField")); + u.getObjectVolatile(o, f).Should().BeSameAs(null); + u.putObjectVolatile(o, f, t); + u.getObjectVolatile(o, f).Should().BeSameAs(t); + o.stringField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetBooleanFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("booleanField")); + u.getBooleanVolatile(o, f).Should().Be(false); + u.putBooleanVolatile(o, f, true); + u.getBooleanVolatile(o, f).Should().Be(true); + o.booleanField.Should().Be(true); + } + + [TestMethod] + public void CanSetByteFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("byteField")); + u.getByteVolatile(o, f).Should().Be(0); + u.putByteVolatile(o, f, 1); + u.getByteVolatile(o, f).Should().Be(1); + o.byteField.Should().Be(1); + } + + [TestMethod] + public void CanSetCharFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("charField")); + u.getCharVolatile(o, f).Should().Be('\0'); + u.putCharVolatile(o, f, 'A'); + u.getCharVolatile(o, f).Should().Be('A'); + o.charField.Should().Be('A'); + } + + [TestMethod] + public void CanSetShortFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("shortField")); + u.getShortVolatile(o, f).Should().Be(0); + u.putShortVolatile(o, f, 1); + u.getShortVolatile(o, f).Should().Be(1); + o.shortField.Should().Be(1); + } + + [TestMethod] + public void CanSetIntFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("intField")); + u.getIntVolatile(o, f).Should().Be(0); + u.putIntVolatile(o, f, 1); + u.getIntVolatile(o, f).Should().Be(1); + o.intField.Should().Be(1); + } + + [TestMethod] + public void CanSetLongFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("longField")); + u.getLongVolatile(o, f).Should().Be(0); + u.putLongVolatile(o, f, 1); + u.getLongVolatile(o, f).Should().Be(1); + o.longField.Should().Be(1); + } + + [TestMethod] + public void CanSetFloatFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("floatField")); + u.getFloatVolatile(o, f).Should().Be(0); + u.putFloatVolatile(o, f, 1); + u.getFloatVolatile(o, f).Should().Be(1); + o.floatField.Should().Be(1); + } + + [TestMethod] + public void CanSetDoubleFieldVolatile() + { + var o = new TestObject(); + var f = u.objectFieldOffset(((Class)typeof(TestObject)).getField("doubleField")); + u.getDoubleVolatile(o, f).Should().Be(0); + u.putDoubleVolatile(o, f, 1); + u.getDoubleVolatile(o, f).Should().Be(1); + o.doubleField.Should().Be(1); + } + + class ReadOnlyTestObject + { + + public readonly object objectField = null; + public readonly string stringField = null; + public readonly bool booleanField = false; + public readonly byte byteField = 0; + public readonly char charField = '\0'; + public readonly short shortField = 0; + public readonly int intField = 0; + public readonly long longField = 0; + public readonly float floatField = 0; + public readonly double doubleField = 0; + + } + + [TestMethod] + public void CanSetReadOnlyObjectField() + { + var o = new ReadOnlyTestObject(); + var t = new object(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("objectField")); + u.getObject(o, f).Should().BeSameAs(null); + u.putObject(o, f, t); + u.getObject(o, f).Should().BeSameAs(t); + o.objectField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStringField() + { + var o = new ReadOnlyTestObject(); + var t = "TEST"; + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("stringField")); + u.getObject(o, f).Should().BeSameAs(null); + u.putObject(o, f, t); + u.getObject(o, f).Should().BeSameAs(t); + o.stringField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyBooleanField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("booleanField")); + u.getBoolean(o, f).Should().Be(false); + u.putBoolean(o, f, true); + u.getBoolean(o, f).Should().Be(true); + o.booleanField.Should().Be(true); + } + + [TestMethod] + public void CanSetReadOnlyByteField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("byteField")); + u.getByte(o, f).Should().Be(0); + u.putByte(o, f, 1); + u.getByte(o, f).Should().Be(1); + o.byteField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyCharField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("charField")); + u.getChar(o, f).Should().Be('\0'); + u.putChar(o, f, 'A'); + u.getChar(o, f).Should().Be('A'); + o.charField.Should().Be('A'); + } + + [TestMethod] + public void CanSetReadOnlyShortField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("shortField")); + u.getShort(o, f).Should().Be(0); + u.putShort(o, f, 1); + u.getShort(o, f).Should().Be(1); + o.shortField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyIntField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("intField")); + u.getInt(o, f).Should().Be(0); + u.putInt(o, f, 1); + u.getInt(o, f).Should().Be(1); + o.intField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyLongField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("longField")); + u.getLong(o, f).Should().Be(0); + u.putLong(o, f, 1); + u.getLong(o, f).Should().Be(1); + o.longField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyFloatField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("floatField")); + u.getFloat(o, f).Should().Be(0); + u.putFloat(o, f, 1); + u.getFloat(o, f).Should().Be(1); + o.floatField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyDoubleField() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("doubleField")); + u.getDouble(o, f).Should().Be(0); + u.putDouble(o, f, 1); + u.getDouble(o, f).Should().Be(1); + o.doubleField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyObjectFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var t = new object(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("objectField")); + u.getObjectVolatile(o, f).Should().BeSameAs(null); + u.putObjectVolatile(o, f, t); + u.getObjectVolatile(o, f).Should().BeSameAs(t); + o.objectField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStringFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var t = "TEST"; + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("stringField")); + u.getObjectVolatile(o, f).Should().BeSameAs(null); + u.putObjectVolatile(o, f, t); + u.getObjectVolatile(o, f).Should().BeSameAs(t); + o.stringField.Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyBooleanFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("booleanField")); + u.getBooleanVolatile(o, f).Should().Be(false); + u.putBooleanVolatile(o, f, true); + u.getBooleanVolatile(o, f).Should().Be(true); + o.booleanField.Should().Be(true); + } + + [TestMethod] + public void CanSetReadOnlyByteFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("byteField")); + u.getByteVolatile(o, f).Should().Be(0); + u.putByteVolatile(o, f, 1); + u.getByteVolatile(o, f).Should().Be(1); + o.byteField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyCharFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("charField")); + u.getCharVolatile(o, f).Should().Be('\0'); + u.putCharVolatile(o, f, 'A'); + u.getCharVolatile(o, f).Should().Be('A'); + o.charField.Should().Be('A'); + } + + [TestMethod] + public void CanSetReadOnlyShortFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("shortField")); + u.getShortVolatile(o, f).Should().Be(0); + u.putShortVolatile(o, f, 1); + u.getShortVolatile(o, f).Should().Be(1); + o.shortField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyIntFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("intField")); + u.getIntVolatile(o, f).Should().Be(0); + u.putIntVolatile(o, f, 1); + u.getIntVolatile(o, f).Should().Be(1); + o.intField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyLongFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("longField")); + u.getLongVolatile(o, f).Should().Be(0); + u.putLongVolatile(o, f, 1); + u.getLongVolatile(o, f).Should().Be(1); + o.longField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyFloatFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("floatField")); + u.getFloatVolatile(o, f).Should().Be(0); + u.putFloatVolatile(o, f, 1); + u.getFloatVolatile(o, f).Should().Be(1); + o.floatField.Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyDoubleFieldVolatile() + { + var o = new ReadOnlyTestObject(); + var f = u.objectFieldOffset(((Class)typeof(ReadOnlyTestObject)).getField("doubleField")); + u.getDoubleVolatile(o, f).Should().Be(0); + u.putDoubleVolatile(o, f, 1); + u.getDoubleVolatile(o, f).Should().Be(1); + o.doubleField.Should().Be(1); + } + + class StaticTestObject + { + + public static object objectField = null; + public static string stringField = null; + public static bool booleanField = false; + public static byte byteField = 0; + public static char charField = '\0'; + public static short shortField = 0; + public static int intField = 0; + public static long longField = 0; + public static float floatField = 0; + public static double doubleField = 0; + + } + + [TestMethod] + public void CanSetStaticObjectField() + { + var t = new object(); + var f = u.objectFieldOffset(((Class)typeof(StaticTestObject)).getField("objectField")); + u.getObject(null, f).Should().BeSameAs(null); + u.putObject(null, f, t); + u.getObject(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStaticStringField() + { + var t = "TEST"; + var f = u.objectFieldOffset(((Class)typeof(StaticTestObject)).getField("stringField")); + u.getObject(null, f).Should().BeSameAs(null); + u.putObject(null, f, t); + u.getObject(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStaticBooleanField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("booleanField")); + u.getBoolean(null, f).Should().Be(false); + u.putBoolean(null, f, true); + u.getBoolean(null, f).Should().Be(true); + } + + [TestMethod] + public void CanSetStaticByteField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("byteField")); + u.getByte(null, f).Should().Be(0); + u.putByte(null, f, 1); + u.getByte(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticCharField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("charField")); + u.getChar(null, f).Should().Be('\0'); + u.putChar(null, f, 'A'); + u.getChar(null, f).Should().Be('A'); + } + + [TestMethod] + public void CanSetStaticShortField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("shortField")); + u.getShort(null, f).Should().Be(0); + u.putShort(null, f, 1); + u.getShort(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticIntField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("intField")); + u.getInt(null, f).Should().Be(0); + u.putInt(null, f, 1); + u.getInt(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticLongField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("longField")); + u.getLong(null, f).Should().Be(0); + u.putLong(null, f, 1); + u.getLong(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticFloatField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("floatField")); + u.getFloat(null, f).Should().Be(0); + u.putFloat(null, f, 1); + u.getFloat(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticDoubleField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticTestObject)).getField("doubleField")); + u.getDouble(null, f).Should().Be(0); + u.putDouble(null, f, 1); + u.getDouble(null, f).Should().Be(1); + } + + class StaticVolatileTestObject + { + + public static object objectField = null; + public static string stringField = null; + public static bool booleanField = false; + public static byte byteField = 0; + public static char charField = '\0'; + public static short shortField = 0; + public static int intField = 0; + public static long longField = 0; + public static float floatField = 0; + public static double doubleField = 0; + + } + + [TestMethod] + public void CanSetStaticObjectFieldVolatile() + { + var t = new object(); + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("objectField")); + u.getObjectVolatile(null, f).Should().BeSameAs(null); + u.putObjectVolatile(null, f, t); + u.getObjectVolatile(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStaticStringFieldVolatile() + { + var t = "TEST"; + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("stringField")); + u.getObjectVolatile(null, f).Should().BeSameAs(null); + u.putObjectVolatile(null, f, t); + u.getObjectVolatile(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetStaticBooleanFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("booleanField")); + u.getBooleanVolatile(null, f).Should().Be(false); + u.putBooleanVolatile(null, f, true); + u.getBooleanVolatile(null, f).Should().Be(true); + } + + [TestMethod] + public void CanSetStaticByteFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("byteField")); + u.getByteVolatile(null, f).Should().Be(0); + u.putByteVolatile(null, f, 1); + u.getByteVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticCharFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("charField")); + u.getCharVolatile(null, f).Should().Be('\0'); + u.putCharVolatile(null, f, 'A'); + u.getCharVolatile(null, f).Should().Be('A'); + } + + [TestMethod] + public void CanSetStaticShortFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("shortField")); + u.getShortVolatile(null, f).Should().Be(0); + u.putShortVolatile(null, f, 1); + u.getShortVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticIntFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("intField")); + u.getIntVolatile(null, f).Should().Be(0); + u.putIntVolatile(null, f, 1); + u.getIntVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticLongFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("longField")); + u.getLongVolatile(null, f).Should().Be(0); + u.putLongVolatile(null, f, 1); + u.getLongVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticFloatFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("floatField")); + u.getFloatVolatile(null, f).Should().Be(0); + u.putFloatVolatile(null, f, 1); + u.getFloatVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetStaticDoubleFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(StaticVolatileTestObject)).getField("doubleField")); + u.getDoubleVolatile(null, f).Should().Be(0); + u.putDoubleVolatile(null, f, 1); + u.getDoubleVolatile(null, f).Should().Be(1); + } + + class ReadOnlyStaticTestObject + { + + public readonly static object objectField = null; + public readonly static string stringField = null; + public readonly static bool booleanField = false; + public readonly static byte byteField = 0; + public readonly static char charField = '\0'; + public readonly static short shortField = 0; + public readonly static int intField = 0; + public readonly static long longField = 0; + public readonly static float floatField = 0; + public readonly static double doubleField = 0; + + } + + [TestMethod] + public void CanSetReadOnlyStaticObjectField() + { + var t = new object(); + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("objectField")); + u.getObject(null, f).Should().BeSameAs(null); + u.putObject(null, f, t); + u.getObject(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStaticStringField() + { + var t = "TEST"; + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("stringField")); + u.getObject(null, f).Should().BeSameAs(null); + u.putObject(null, f, t); + u.getObject(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStaticBooleanField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("booleanField")); + u.getBoolean(null, f).Should().Be(false); + u.putBoolean(null, f, true); + u.getBoolean(null, f).Should().Be(true); + } + + [TestMethod] + public void CanSetReadOnlyStaticByteField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("byteField")); + u.getByte(null, f).Should().Be(0); + u.putByte(null, f, 1); + u.getByte(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticCharField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("charField")); + u.getChar(null, f).Should().Be('\0'); + u.putChar(null, f, 'A'); + u.getChar(null, f).Should().Be('A'); + } + + [TestMethod] + public void CanSetReadOnlyStaticShortField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("shortField")); + u.getShort(null, f).Should().Be(0); + u.putShort(null, f, 1); + u.getShort(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticIntField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("intField")); + u.getInt(null, f).Should().Be(0); + u.putInt(null, f, 1); + u.getInt(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticLongField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("longField")); + u.getLong(null, f).Should().Be(0); + u.putLong(null, f, 1); + u.getLong(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticFloatField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("floatField")); + u.getFloat(null, f).Should().Be(0); + u.putFloat(null, f, 1); + u.getFloat(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticDoubleField() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticTestObject)).getField("doubleField")); + u.getDouble(null, f).Should().Be(0); + u.putDouble(null, f, 1); + u.getDouble(null, f).Should().Be(1); + } + + class ReadOnlyStaticVolatileTestObject + { + + public readonly static object objectField = null; + public readonly static string stringField = null; + public readonly static bool booleanField = false; + public readonly static byte byteField = 0; + public readonly static char charField = '\0'; + public readonly static short shortField = 0; + public readonly static int intField = 0; + public readonly static long longField = 0; + public readonly static float floatField = 0; + public readonly static double doubleField = 0; + + } + + [TestMethod] + public void CanSetReadOnlyStaticObjectFieldVolatile() + { + var t = new object(); + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("objectField")); + u.getObjectVolatile(null, f).Should().BeSameAs(null); + u.putObjectVolatile(null, f, t); + u.getObjectVolatile(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStaticStringFieldVolatile() + { + var t = "TEST"; + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("stringField")); + u.getObjectVolatile(null, f).Should().BeSameAs(null); + u.putObjectVolatile(null, f, t); + u.getObjectVolatile(null, f).Should().BeSameAs(t); + } + + [TestMethod] + public void CanSetReadOnlyStaticBooleanFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("booleanField")); + u.getBooleanVolatile(null, f).Should().Be(false); + u.putBooleanVolatile(null, f, true); + u.getBooleanVolatile(null, f).Should().Be(true); + } + + [TestMethod] + public void CanSetReadOnlyStaticByteFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("byteField")); + u.getByteVolatile(null, f).Should().Be(0); + u.putByteVolatile(null, f, 1); + u.getByteVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticCharFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("charField")); + u.getCharVolatile(null, f).Should().Be('\0'); + u.putCharVolatile(null, f, 'A'); + u.getCharVolatile(null, f).Should().Be('A'); + } + + [TestMethod] + public void CanSetReadOnlyStaticShortFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("shortField")); + u.getShortVolatile(null, f).Should().Be(0); + u.putShortVolatile(null, f, 1); + u.getShortVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticIntFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("intField")); + u.getIntVolatile(null, f).Should().Be(0); + u.putIntVolatile(null, f, 1); + u.getIntVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticLongFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("longField")); + u.getLongVolatile(null, f).Should().Be(0); + u.putLongVolatile(null, f, 1); + u.getLongVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticFloatFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("floatField")); + u.getFloatVolatile(null, f).Should().Be(0); + u.putFloatVolatile(null, f, 1); + u.getFloatVolatile(null, f).Should().Be(1); + } + + [TestMethod] + public void CanSetReadOnlyStaticDoubleFieldVolatile() + { + var f = u.staticFieldOffset(((Class)typeof(ReadOnlyStaticVolatileTestObject)).getField("doubleField")); + u.getDoubleVolatile(null, f).Should().Be(0); + u.putDoubleVolatile(null, f, 1); + u.getDoubleVolatile(null, f).Should().Be(1); + } + + + [TestMethod] + public void CanSetObjectInArray() + { + var o1 = new object(); + var o2 = new object(); + var a = new object[16]; + u.putObject(a, 0L, o1); + u.getObject(a, 0L).Should().BeSameAs(o1); + a[0].Should().BeSameAs(o1); + u.putObject(a, 0L, null); + u.getObject(a, 0L).Should().BeSameAs(null); + a[0].Should().BeNull(); + u.putObject(a, 1L, o2); + u.getObject(a, 1L).Should().BeSameAs(o2); + a[1].Should().BeSameAs(o2); + u.putObject(a, 1L, null); + u.getObject(a, 1L).Should().BeSameAs(null); + a[1].Should().BeNull(); + } + + [TestMethod] + public void CanSetObjectInArrayVolatile() + { + var o1 = new object(); + var o2 = new object(); + var a = new object[16]; + u.putObjectVolatile(a, 0L, o1); + u.getObjectVolatile(a, 0L).Should().BeSameAs(o1); + a[0].Should().BeSameAs(o1); + u.putObjectVolatile(a, 0L, null); + u.getObjectVolatile(a, 0L).Should().BeSameAs(null); + a[0].Should().BeNull(); + u.putObjectVolatile(a, 1L, o2); + u.getObjectVolatile(a, 1L).Should().BeSameAs(o2); + a[1].Should().BeSameAs(o2); + u.putObjectVolatile(a, 1L, null); + u.getObjectVolatile(a, 1L).Should().BeSameAs(null); + a[1].Should().BeNull(); + } + + class AnonymousTestObject + { + + + + } + + [TestMethod] + public void CanSetObjectInTypedArray() + { + var o1 = new AnonymousTestObject(); + var o2 = new AnonymousTestObject(); + var a = new AnonymousTestObject[16]; + u.putObject(a, 0L, o1); + u.getObject(a, 0L).Should().BeSameAs(o1); + a[0].Should().BeSameAs(o1); + u.putObject(a, 0L, null); + u.getObject(a, 0L).Should().BeSameAs(null); + a[0].Should().BeNull(); + u.putObject(a, 1L, o2); + u.getObject(a, 1L).Should().BeSameAs(o2); + a[1].Should().BeSameAs(o2); + u.putObject(a, 1L, null); + u.getObject(a, 1L).Should().BeSameAs(null); + a[1].Should().BeNull(); + } + + [TestMethod] + public void CanSetObjectInTypedArrayVolatile() + { + var o1 = new AnonymousTestObject(); + var o2 = new AnonymousTestObject(); + var a = new AnonymousTestObject[16]; + u.putObjectVolatile(a, 0L, o1); + u.getObjectVolatile(a, 0L).Should().BeSameAs(o1); + a[0].Should().BeSameAs(o1); + u.putObjectVolatile(a, 0L, null); + u.getObjectVolatile(a, 0L).Should().BeSameAs(null); + a[0].Should().BeNull(); + u.putObjectVolatile(a, 1L, o2); + u.getObjectVolatile(a, 1L).Should().BeSameAs(o2); + a[1].Should().BeSameAs(o2); + u.putObjectVolatile(a, 1L, null); + u.getObjectVolatile(a, 1L).Should().BeSameAs(null); + a[1].Should().BeNull(); + } + + [TestMethod] + public void CanSetBoolInArray() + { + var a = new bool[16]; + u.putBoolean(a, (long)sizeof(bool), true); + u.getBoolean(a, (long)sizeof(bool)).Should().Be(true); + a[0].Should().Be(false); + a[1].Should().Be(true); + a[2].Should().Be(false); + } + + [TestMethod] + public void CanSetByteInArray() + { + var a = new byte[16]; + u.putByte(a, (long)sizeof(byte), 1); + u.getByte(a, (long)sizeof(byte)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetCharInArray() + { + var a = new char[16]; + u.putChar(a, (long)sizeof(char), 'A'); + u.getChar(a, (long)sizeof(char)).Should().Be('A'); + a[0].Should().Be('\0'); + a[1].Should().Be('A'); + a[2].Should().Be('\0'); + } + + [TestMethod] + public void CanSetShortInArray() + { + var a = new short[16]; + u.putShort(a, (long)sizeof(short), 1); + u.getShort(a, (long)sizeof(short)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetShortInByteArray() + { + var a = new byte[16]; + u.putShort(a, (long)0, 1); + u.getShort(a, (long)0).Should().Be(1); + MemoryMarshal.Cast(a)[0].Should().Be(1); + } + + [TestMethod] + public void CanSetIntInArray() + { + var a = new int[16]; + u.putInt(a, (long)sizeof(int), 1); + u.getInt(a, (long)sizeof(int)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetIntInByteArray() + { + var a = new byte[16]; + u.putInt(a, (long)0, 1); + u.getInt(a, (long)0).Should().Be(1); + MemoryMarshal.Cast(a)[0].Should().Be(1); + } + + [TestMethod] + public void CanSetLongInArray() + { + var a = new long[16]; + u.putLong(a, (long)sizeof(long), 1); + u.getLong(a, (long)sizeof(long)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetLongInByteArray() + { + var a = new byte[16]; + u.putLong(a, (long)0, 1); + u.getLong(a, (long)0).Should().Be(1); + MemoryMarshal.Cast(a)[0].Should().Be(1); + } + + [TestMethod] + public void CanSetFloatInArray() + { + var a = new float[16]; + u.putFloat(a, (long)sizeof(float), 1); + u.getFloat(a, (long)sizeof(float)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetFloatInByteArray() + { + var a = new byte[16]; + u.putFloat(a, (long)0, 1); + u.getFloat(a, (long)0).Should().Be(1); + MemoryMarshal.Cast(a)[0].Should().Be(1); + } + + [TestMethod] + public void CanSetDoubleInArray() + { + var a = new double[16]; + u.putDouble(a, (long)sizeof(double), 1); + u.getDouble(a, (long)sizeof(double)).Should().Be(1); + a[0].Should().Be(0); + a[1].Should().Be(1); + a[2].Should().Be(0); + } + + [TestMethod] + public void CanSetDoubleInByteArray() + { + var a = new byte[16]; + u.putDouble(a, (long)0, 1); + u.getDouble(a, (long)0).Should().Be(1); + MemoryMarshal.Cast(a)[0].Should().Be(1); + } + + [TestMethod] + public void CanAllocateAndFreeMemory() + { + var b = u.allocateMemory(32); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateAndSetAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + + for (int i = 0; i < 32; i++) + Marshal.ReadByte((IntPtr)(b + i)).Should().Be(0); + + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetByteAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putByte(b + sizeof(byte), 1); + u.getByte(b).Should().Be(0); + u.getByte(b + sizeof(byte)).Should().Be(1); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetCharAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putChar(b + sizeof(char), 'A'); + u.getChar(b).Should().Be('\0'); + u.getChar(b + sizeof(char)).Should().Be('A'); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetShortAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putShort(b + sizeof(short), 1); + u.getShort(b).Should().Be(0); + u.getShort(b + sizeof(short)).Should().Be(1); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetIntAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putInt(b + sizeof(int), 1); + u.getInt(b).Should().Be(0); + u.getInt(b + sizeof(int)).Should().Be(1); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetLongAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putLong(b + sizeof(long), 1); + u.getLong(b).Should().Be(0); + u.getLong(b + sizeof(long)).Should().Be(1); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetFloatAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putFloat(b + sizeof(float), 1); + u.getFloat(b).Should().Be(0); + u.getFloat(b + sizeof(float)).Should().Be(1); + u.freeMemory(b); + } + + [TestMethod] + public void CanAllocateSetDoubleAndFreeMemory() + { + var b = u.allocateMemory(32); + u.setMemory(b, 32, 0); + u.putDouble(b + sizeof(double), 1); + u.getDouble(b).Should().Be(0); + u.getDouble(b + sizeof(double)).Should().Be(1); + u.freeMemory(b); + } + + class CompareAndSwapTestObject + { + + public object objectField = null; + public string stringField = null; + public int intField = 0; + public long longField = 0; + + } + + [TestMethod] + public void CanCompareAndSwapObjectField() + { + var o = new CompareAndSwapTestObject(); + var o1 = new object(); + var o2 = new object(); + var f = u.objectFieldOffset(((Class)typeof(CompareAndSwapTestObject)).getField("objectField")); + u.compareAndSwapObject(o, f, null, o1).Should().BeTrue(); + o.objectField.Should().BeSameAs(o1); + u.compareAndSwapObject(o, f, o1, o2).Should().BeTrue(); + o.objectField.Should().BeSameAs(o2); + u.compareAndSwapObject(o, f, o1, o2).Should().BeFalse(); + o.objectField.Should().BeSameAs(o2); + } + + [TestMethod] + public void CanCompareAndSwapStringField() + { + var o = new CompareAndSwapTestObject(); + var o1 = "TEST1"; + var o2 = "TEST2"; + var f = u.objectFieldOffset(((Class)typeof(CompareAndSwapTestObject)).getField("stringField")); + u.compareAndSwapObject(o, f, null, o1).Should().BeTrue(); + o.stringField.Should().BeSameAs(o1); + u.compareAndSwapObject(o, f, o1, o2).Should().BeTrue(); + o.stringField.Should().BeSameAs(o2); + u.compareAndSwapObject(o, f, o1, o2).Should().BeFalse(); + o.stringField.Should().BeSameAs(o2); + } + + [TestMethod] + public void CanCompareAndSwapIntField() + { + var o = new CompareAndSwapTestObject(); + var f = u.objectFieldOffset(((Class)typeof(CompareAndSwapTestObject)).getField("intField")); + u.compareAndSwapInt(o, f, 0, 1).Should().BeTrue(); + o.intField.Should().Be(1); + u.compareAndSwapInt(o, f, 1, 2).Should().BeTrue(); + o.intField.Should().Be(2); + u.compareAndSwapInt(o, f, 1, 2).Should().BeFalse(); + o.intField.Should().Be(2); + } + + [TestMethod] + public void CanCompareAndSwapLongField() + { + var o = new CompareAndSwapTestObject(); + var f = u.objectFieldOffset(((Class)typeof(CompareAndSwapTestObject)).getField("longField")); + u.compareAndSwapLong(o, f, 0, 1).Should().BeTrue(); + o.longField.Should().Be(1); + u.compareAndSwapLong(o, f, 1, 2).Should().BeTrue(); + o.longField.Should().Be(2); + u.compareAndSwapLong(o, f, 1, 2).Should().BeFalse(); + o.longField.Should().Be(2); + } + + class StaticCompareAndSwapTestObject + { + + public static object objectField = null; + public static string stringField = null; + public static int intField = 0; + public static long longField = 0; + + } + + [TestMethod] + public void CanCompareAndSwapStaticObjectField() + { + var o1 = new object(); + var o2 = new object(); + var f = u.staticFieldOffset(((Class)typeof(StaticCompareAndSwapTestObject)).getField("objectField")); + u.compareAndSwapObject(null, f, null, o1).Should().BeTrue(); + StaticCompareAndSwapTestObject.objectField.Should().BeSameAs(o1); + u.compareAndSwapObject(null, f, o1, o2).Should().BeTrue(); + StaticCompareAndSwapTestObject.objectField.Should().BeSameAs(o2); + u.compareAndSwapObject(null, f, o1, o2).Should().BeFalse(); + StaticCompareAndSwapTestObject.objectField.Should().BeSameAs(o2); + } + + [TestMethod] + public void CanCompareAndSwapStaticStringField() + { + var o1 = "TEST1"; + var o2 = "TEST2"; + var f = u.staticFieldOffset(((Class)typeof(StaticCompareAndSwapTestObject)).getField("stringField")); + u.compareAndSwapObject(null, f, null, o1).Should().BeTrue(); + StaticCompareAndSwapTestObject.stringField.Should().BeSameAs(o1); + u.compareAndSwapObject(null, f, o1, o2).Should().BeTrue(); + StaticCompareAndSwapTestObject.stringField.Should().BeSameAs(o2); + u.compareAndSwapObject(null, f, o1, o2).Should().BeFalse(); + StaticCompareAndSwapTestObject.stringField.Should().BeSameAs(o2); + } + + [TestMethod] + public void CanCompareAndSwapStaticIntField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticCompareAndSwapTestObject)).getField("intField")); + u.compareAndSwapInt(null, f, 0, 1).Should().BeTrue(); + StaticCompareAndSwapTestObject.intField.Should().Be(1); + u.compareAndSwapInt(null, f, 1, 2).Should().BeTrue(); + StaticCompareAndSwapTestObject.intField.Should().Be(2); + u.compareAndSwapInt(null, f, 1, 2).Should().BeFalse(); + StaticCompareAndSwapTestObject.intField.Should().Be(2); + } + + [TestMethod] + public void CanCompareAndSwapStaticLongField() + { + var f = u.staticFieldOffset(((Class)typeof(StaticCompareAndSwapTestObject)).getField("longField")); + u.compareAndSwapLong(null, f, 0, 1).Should().BeTrue(); + StaticCompareAndSwapTestObject.longField.Should().Be(1); + u.compareAndSwapLong(null, f, 1, 2).Should().BeTrue(); + StaticCompareAndSwapTestObject.longField.Should().Be(2); + u.compareAndSwapLong(null, f, 1, 2).Should().BeFalse(); + StaticCompareAndSwapTestObject.longField.Should().Be(2); + } + + [TestMethod] + public void CanCompareAndSwapObjectInArray() + { + var a = new object[4]; + for (int i = 0; i < 4; i++) + { + var o1 = new object(); + var o2 = new object(); + + u.compareAndSwapObject(a, i, null, o1).Should().BeTrue(); + a[i].Should().BeSameAs(o1); + u.compareAndSwapObject(a, i, o1, o2).Should().BeTrue(); + a[i].Should().BeSameAs(o2); + u.compareAndSwapObject(a, i, o1, o2).Should().BeFalse(); + a[i].Should().BeSameAs(o2); + } + } + + [TestMethod] + public void CanCompareAndSwapStringInArray() + { + var a = new string[4]; + for (int i = 0; i < 4; i++) + { + var o1 = "TEST1"; + var o2 = "TEST2"; + + u.compareAndSwapObject(a, i, null, o1).Should().BeTrue(); + a[i].Should().BeSameAs(o1); + u.compareAndSwapObject(a, i, o1, o2).Should().BeTrue(); + a[i].Should().BeSameAs(o2); + u.compareAndSwapObject(a, i, o1, o2).Should().BeFalse(); + a[i].Should().BeSameAs(o2); + } + } + + [TestMethod] + public void CanCompareAndSwapIntInArray() + { + var a = new int[4]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapInt(a, i * sizeof(int), 0, 1).Should().BeTrue(); + a[i].Should().Be(1); + u.compareAndSwapInt(a, i * sizeof(int), 1, 2).Should().BeTrue(); + a[i].Should().Be(2); + u.compareAndSwapInt(a, i * sizeof(int), 1, 2).Should().BeFalse(); + a[i].Should().Be(2); + } + } + + [TestMethod] + public void CanCompareAndSwapIntInLongArrayAligned() + { + var a = new long[4]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapInt(a, i * sizeof(long), 0, 1).Should().BeTrue(); + u.compareAndSwapInt(a, i * sizeof(long), 1, 2).Should().BeTrue(); + u.compareAndSwapInt(a, i * sizeof(long), 1, 2).Should().BeFalse(); + } + } + + [TestMethod] + public void CanCompareAndSwapIntInLongArrayUnaligned() + { + var a = new long[4]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapInt(a, i * sizeof(long) + 1, 0, 1).Should().BeTrue(); + u.compareAndSwapInt(a, i * sizeof(long) + 1, 1, 2).Should().BeTrue(); + u.compareAndSwapInt(a, i * sizeof(long) + 1, 1, 2).Should().BeFalse(); + } + } + + [TestMethod] + public void CanCompareAndSwapLongInArray() + { + var a = new long[4]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapLong(a, i * sizeof(long), 0, 1).Should().BeTrue(); + a[i].Should().Be(1); + u.compareAndSwapLong(a, i * sizeof(long), 1, 2).Should().BeTrue(); + a[i].Should().Be(2); + u.compareAndSwapLong(a, i * sizeof(long), 1, 2).Should().BeFalse(); + a[i].Should().Be(2); + } + } + + [TestMethod] + public void CanCompareAndSwapLongInIntArray() + { + var a = new int[8]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapLong(a, i * sizeof(long), 0, 1).Should().BeTrue(); + u.compareAndSwapLong(a, i * sizeof(long), 1, 2).Should().BeTrue(); + u.compareAndSwapLong(a, i * sizeof(long), 1, 2).Should().BeFalse(); + } + } + + [TestMethod] + public void CanCompareAndSwapLongInIntArrayUnaligned() + { + var a = new int[16]; + for (int i = 0; i < 4; i++) + { + u.compareAndSwapLong(a, i * sizeof(long) + 1, 0, 1).Should().BeTrue(); + u.compareAndSwapLong(a, i * sizeof(long) + 1, 1, 2).Should().BeTrue(); + u.compareAndSwapLong(a, i * sizeof(long) + 1, 1, 2).Should().BeFalse(); + } + } + + } + +} diff --git a/src/IKVM.Tests/Java/sun/security/ec/ECDHKeyAgreementTests.cs b/src/IKVM.Tests/Java/sun/security/ec/ECDHKeyAgreementTests.cs new file mode 100644 index 0000000000..de7010e298 --- /dev/null +++ b/src/IKVM.Tests/Java/sun/security/ec/ECDHKeyAgreementTests.cs @@ -0,0 +1,53 @@ +using FluentAssertions; + +using java.security; + +using javax.crypto; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.sun.security.ec +{ + + [TestClass] + public class ECDHKeyAgreementTests + { + + [TestMethod] + [Ignore] + public void CanGenerateAgreementBetweenParties() + { + // party A + var kpgA = KeyPairGenerator.getInstance("EC"); + kpgA.initialize(256); + var keyA = kpgA.generateKeyPair(); + var prvA = keyA.getPrivate(); + var pubA = keyA.getPublic(); + + // party B + var kpgB = KeyPairGenerator.getInstance("EC"); + kpgB.initialize(256); + var keyB = kpgB.generateKeyPair(); + var prvB = keyB.getPrivate(); + var pubB = keyB.getPublic(); + + // agreement of party A with party B + var kaA = KeyAgreement.getInstance("ECDH"); + kaA.init(prvA); + kaA.doPhase(pubB, true); + var secA = kaA.generateSecret(); + + // agreement of party B with party A + var kaB = KeyAgreement.getInstance("ECDH"); + kaB.init(prvB); + kaB.doPhase(pubA, true); + var secB = kaB.generateSecret(); + + // check that the same agreement resulted + secA.Should().HaveSameCount(secB); + secA.Should().BeEquivalentTo(secB); + } + + } + +} diff --git a/src/IKVM.Tests/Java/sun/security/ec/ECKeyPairGeneratorTests.cs b/src/IKVM.Tests/Java/sun/security/ec/ECKeyPairGeneratorTests.cs new file mode 100644 index 0000000000..205391b542 --- /dev/null +++ b/src/IKVM.Tests/Java/sun/security/ec/ECKeyPairGeneratorTests.cs @@ -0,0 +1,25 @@ +using java.security; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace IKVM.Tests.Java.sun.security.ec +{ + + [TestClass] + public class ECKeyPairGeneratorTests + { + + [TestMethod] + [Ignore] + public void CanGenerateKeyPair() + { + var kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(256); + var key = kpg.generateKeyPair(); + var prv = key.getPrivate(); + var pub = key.getPublic(); + } + + } + +} diff --git a/src/IKVM.Tests/Native.cs b/src/IKVM.Tests/Native.cs index f6920eb747..2586d5dab1 100644 --- a/src/IKVM.Tests/Native.cs +++ b/src/IKVM.Tests/Native.cs @@ -73,6 +73,11 @@ static IEnumerable GetRuntimeIdentifiers() { yield return $"linux-{arch}"; } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + yield return $"osx-{arch}"; + } } /// @@ -88,6 +93,9 @@ static string GetLibraryFileName(string name) if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return $"lib{name}.so"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return $"lib{name}.dylib"; + throw new NotSupportedException(); } diff --git a/src/IKVM.Tests/Runtime/Text/MUTF8EncodingTests.cs b/src/IKVM.Tests/Runtime/Text/MUTF8EncodingTests.cs deleted file mode 100644 index acdd59549d..0000000000 --- a/src/IKVM.Tests/Runtime/Text/MUTF8EncodingTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -using FluentAssertions; - -using IKVM.Runtime.Text; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace IKVM.Tests.Runtime.Text -{ - - [TestClass] - public class MUTF8EncodingTests - { - - [TestMethod] - public unsafe void CanFindNullByte() - { - fixed (byte* ptr = new byte[] { 0x01, 0x00 }) - MUTF8Encoding.IndexOfNull(ptr).Should().Be(1); - fixed (byte* ptr = new byte[] { 0x00, 0x00 }) - MUTF8Encoding.IndexOfNull(ptr).Should().Be(0); - fixed (byte* ptr = new byte[] { 0x01, 0x01, 0x00 }) - MUTF8Encoding.IndexOfNull(ptr).Should().Be(2); - } - - [TestMethod] - public void CanEncodeNull() - { - MUTF8Encoding.MUTF8.GetBytes("\0").Should().HaveCount(2); - MUTF8Encoding.MUTF8.GetBytes("a\0").Should().HaveCount(3); - MUTF8Encoding.MUTF8.GetBytes("a\0a").Should().HaveCount(4); - MUTF8Encoding.MUTF8.GetBytes("\0\0").Should().HaveCount(4); - MUTF8Encoding.MUTF8.GetBytes("a\0\0").Should().HaveCount(5); - MUTF8Encoding.MUTF8.GetBytes("a\0\0a").Should().HaveCount(6); - } - - [TestMethod] - public void CanHandleEmptyString() - { - MUTF8Encoding.MUTF8.GetBytes("").Should().BeEmpty(); - MUTF8Encoding.MUTF8.GetString(Array.Empty()).Should().Be(""); - } - - } - -} diff --git a/src/IKVM.Tests/Runtime/Vfs/VfsAssemblyDirectoryTests.cs b/src/IKVM.Tests/Runtime/Vfs/VfsAssemblyDirectoryTests.cs index 9442b41eb5..37249f1c91 100644 --- a/src/IKVM.Tests/Runtime/Vfs/VfsAssemblyDirectoryTests.cs +++ b/src/IKVM.Tests/Runtime/Vfs/VfsAssemblyDirectoryTests.cs @@ -15,7 +15,7 @@ public class VfsAssemblyDirectoryTests { [TestMethod] - public void Can_get_assembly_dir() + public void CanGetAssemblyDirectory() { var ctx = VfsRuntimeContext.Instance; var dir = new VfsAssemblyDirectory(ctx); @@ -28,7 +28,7 @@ public void Can_get_assembly_dir() } [TestMethod] - public void Can_get_assembly_resource() + public void CanGetAssemblyResource() { var ctx = VfsRuntimeContext.Instance; var dir = new VfsAssemblyDirectory(ctx); @@ -44,7 +44,7 @@ public void Can_get_assembly_resource() } [TestMethod] - public void Can_find_class_file_in_package() + public void CanFindClassFileInPackage() { var ctx = VfsRuntimeContext.Instance; var dir = new VfsAssemblyClassDirectory(ctx, typeof(global::java.lang.Object).Assembly, "java.lang"); @@ -54,6 +54,50 @@ public void Can_find_class_file_in_package() new BinaryReader(((VfsFile)ent).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); } + [TestMethod] + public void CanFindClassFileForNestedClass() + { + var ctx = VfsRuntimeContext.Instance; + var dir = new VfsAssemblyClassDirectory(ctx, typeof(global::java.lang.Character).Assembly, "java.lang"); + var ent = dir.GetEntry("Character$Subset.class"); + ent.Should().NotBeNull(); + ent.Should().BeOfType(); + new BinaryReader(((VfsFile)ent).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanFindClassFileForAnonymousClass() + { + var ctx = VfsRuntimeContext.Instance; + var dir = new VfsAssemblyClassDirectory(ctx, typeof(global::java.lang.ClassLoader).Assembly, "java.lang"); + var ent = dir.GetEntry("ClassLoader$1.class"); + ent.Should().NotBeNull(); + ent.Should().BeOfType(); + new BinaryReader(((VfsFile)ent).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanFindClassFileForInnerClassOfInterface() + { + var ctx = VfsRuntimeContext.Instance; + var dir = new VfsAssemblyClassDirectory(ctx, typeof(global::java.lang.CharSequence).Assembly, "java.lang"); + var ent = dir.GetEntry("CharSequence$1CharIterator.class"); + ent.Should().NotBeNull(); + ent.Should().BeOfType(); + new BinaryReader(((VfsFile)ent).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanFindClassFileForNestedClassOfInterface() + { + var ctx = VfsRuntimeContext.Instance; + var dir = new VfsAssemblyClassDirectory(ctx, typeof(global::javax.tools.JavaFileObject).Assembly, "javax.tools"); + var ent = dir.GetEntry("JavaFileObject$Kind.class"); + ent.Should().NotBeNull(); + ent.Should().BeOfType(); + new BinaryReader(((VfsFile)ent).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + } } diff --git a/src/IKVM.Tests/Runtime/Vfs/VfsTableTests.cs b/src/IKVM.Tests/Runtime/Vfs/VfsTableTests.cs index 6416e44a90..86b9ee8dd3 100644 --- a/src/IKVM.Tests/Runtime/Vfs/VfsTableTests.cs +++ b/src/IKVM.Tests/Runtime/Vfs/VfsTableTests.cs @@ -15,63 +15,88 @@ public class VfsTableTests { [TestMethod] - public void Should_return_directory_for_home_path() + public void CanGetAssemblyDirectory() { - VfsTable.Default.GetPath(VfsTable.RootPath).Should().BeAssignableTo(); + VfsTable.Default.GetEntry(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly)).Should().BeAssignableTo(); + VfsTable.Default.GetEntry(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly)).Should().BeAssignableTo(); } [TestMethod] - public void Should_return_directory_for_assembly_path() + public void CanListAssemblyClassesDirectory() { - VfsTable.Default.GetPath(Path.Combine(VfsTable.RootPath, "assembly")).Should().BeAssignableTo(); - } - - [TestMethod] - public void Should_return_assembly_directories() - { - VfsTable.Default.GetPath(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly)).Should().BeAssignableTo(); - VfsTable.Default.GetPath(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly)).Should().BeAssignableTo(); - } - - [TestMethod] - public void Should_list_assembly_classes_directory() - { - var dir = (VfsDirectory)VfsTable.Default.GetPath(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly)); + var dir = (VfsDirectory)VfsTable.Default.GetEntry(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly)); var lst = dir.List(); lst.Should().NotContain(""); } [TestMethod] - public void Should_return_class_file_for_standard_classes() + public void CanGetClassFileForStandardClass() { - var f1 = VfsTable.Default.GetPath(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly), "java", "lang", "Object.class")); + var f1 = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Object).Assembly), "java", "lang", "Object.class")); f1.Should().BeAssignableTo(); ((VfsFile)f1).Size.Should().BeGreaterThan(1); ((VfsFile)f1).Open(FileMode.Open, FileAccess.Read).Length.Should().BeGreaterThan(1); - var f2 = VfsTable.Default.GetPath(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.String).Assembly), "java", "lang", "String.class")); + var f2 = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.String).Assembly), "java", "lang", "String.class")); f2.Should().BeAssignableTo(); ((VfsFile)f2).Size.Should().BeGreaterThan(1); ((VfsFile)f2).Open(FileMode.Open, FileAccess.Read).Length.Should().BeGreaterThan(1); } [TestMethod] - public void Should_return_resource_file_for_standard_resources() + public void CanGetResourcesJar() { - var f1 = VfsTable.Default.GetPath(Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly), "resources.jar")); + var f1 = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly), "resources.jar")); f1.Should().BeAssignableTo(); ((VfsFile)f1).Size.Should().BeGreaterThan(1); ((VfsFile)f1).Open(FileMode.Open, FileAccess.Read).Length.Should().BeGreaterThan(1); } [TestMethod] - public void Should_return_resource_jar_file_for_standard_resources() + public void CanReadManifestFromResourcesJar() { - var fil = VfsTable.Default.GetPath(Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly), "resources.jar")); + var fil = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyResourcesPath(typeof(global::java.lang.Object).Assembly), "resources.jar")); var stm = ((VfsFile)fil).Open(FileMode.Open, FileAccess.Read); var zip = new ZipArchive(stm); var man = zip.GetEntry("META-INF/MANIFEST.MF"); man.Should().NotBeNull(); + new StreamReader(man.Open()).ReadToEnd(); + } + + [TestMethod] + public void CanGetClassFileForNestedClass() + { + var file = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.Character).Assembly), "java", "lang", "Character$Subset.class")); + file.Should().BeAssignableTo(); + ((VfsFile)file).Size.Should().BeGreaterThan(1); + new BinaryReader(((VfsFile)file).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanGetClassFileForAnonymousClass() + { + var file = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.ClassLoader).Assembly), "java", "lang", "ClassLoader$1.class")); + file.Should().BeAssignableTo(); + ((VfsFile)file).Size.Should().BeGreaterThan(1); + new BinaryReader(((VfsFile)file).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanGetClassFileForInnerClassOfInterface() + { + var file = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::java.lang.CharSequence).Assembly), "java", "lang", "CharSequence$1CharIterator.class")); + file.Should().BeAssignableTo(); + ((VfsFile)file).Size.Should().BeGreaterThan(1); + new BinaryReader(((VfsFile)file).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); + } + + [TestMethod] + public void CanGetClassFileForNestedClassOfInterface() + { + var file = VfsTable.Default.GetEntry(Path.Combine(VfsTable.Default.GetAssemblyClassesPath(typeof(global::javax.tools.JavaFileObject).Assembly), "javax", "tools", "JavaFileObject$Kind.class")); + file.Should().BeAssignableTo(); + ((VfsFile)file).Size.Should().BeGreaterThan(1); + new BinaryReader(((VfsFile)file).Open(FileMode.Open, FileAccess.Read)).ReadUInt32().Should().Be(0xBEBAFECA); } } diff --git a/src/IKVM.Tests/Util/Jar/JarFileTests.cs b/src/IKVM.Tests/Util/Jar/JarFileTests.cs index 8e1804dd86..e987022446 100644 --- a/src/IKVM.Tests/Util/Jar/JarFileTests.cs +++ b/src/IKVM.Tests/Util/Jar/JarFileTests.cs @@ -1,4 +1,6 @@ -using FluentAssertions; +using System.IO; + +using FluentAssertions; using IKVM.Util.Jar; @@ -12,12 +14,19 @@ public class JarFileTests { [TestMethod] - public void Can_read_manifest_version() + public void CanReadManifestVersion() { - var z = new JarFile(@"helloworld-2.0.jar"); + var z = new JarFile(Path.Combine("ext","helloworld-2.0.jar")); z.Manifest.MainAttributes.Should().Contain("Manifest-Version", "1.0"); } + [TestMethod] + public void CanReadModuleName() + { + var z = new JarFile(Path.Combine("ext", "helloworld-mod.jar")); + z.GetModuleInfo().Name.Should().Be("helloworld"); + } + } } diff --git a/src/IKVM.Tools.Exporter.Tests/IKVM.Tools.Exporter.Tests.csproj b/src/IKVM.Tools.Exporter.Tests/IKVM.Tools.Exporter.Tests.csproj index acd659fa25..6f57792eba 100644 --- a/src/IKVM.Tools.Exporter.Tests/IKVM.Tools.Exporter.Tests.csproj +++ b/src/IKVM.Tools.Exporter.Tests/IKVM.Tools.Exporter.Tests.csproj @@ -1,8 +1,6 @@  net461;netcoreapp3.1 - true - true true diff --git a/src/IKVM.Tools.Exporter.Tests/IkvmExporterTests.cs b/src/IKVM.Tools.Exporter.Tests/IkvmExporterTests.cs index 4831e88d66..050fa4af07 100644 --- a/src/IKVM.Tools.Exporter.Tests/IkvmExporterTests.cs +++ b/src/IKVM.Tools.Exporter.Tests/IkvmExporterTests.cs @@ -42,7 +42,7 @@ public async Task Can_stub_library() options.Output = Path.Combine(Path.GetTempPath(), Path.GetFileName(Path.ChangeExtension(options.Assembly, ".jar"))); #endif - var ret = await new IkvmExporter(options).ExecuteAsync(CancellationToken.None); + var ret = await IkvmExporterTool.ExecuteAsync(options, CancellationToken.None); ret.Should().Be(0); File.Exists(options.Output).Should().BeTrue(); } diff --git a/src/IKVM.Tools.Exporter/BootstrapBootstrapClassLoader.cs b/src/IKVM.Tools.Exporter/BootstrapBootstrapClassLoader.cs index a6e24dc567..c10bf2dbfe 100644 --- a/src/IKVM.Tools.Exporter/BootstrapBootstrapClassLoader.cs +++ b/src/IKVM.Tools.Exporter/BootstrapBootstrapClassLoader.cs @@ -25,11 +25,13 @@ Jeroen Frijters using IKVM.Attributes; using IKVM.Internal; +using IKVM.Runtime; sealed class BootstrapBootstrapClassLoader : ClassLoaderWrapper { - internal BootstrapBootstrapClassLoader() - : base(CodeGenOptions.None, null) + + internal BootstrapBootstrapClassLoader() : + base(CodeGenOptions.None, null) { TypeWrapper javaLangObject = new StubTypeWrapper(Modifiers.Public, "java.lang.Object", null, true); SetRemappedType(JVM.Import(typeof(object)), javaLangObject); @@ -45,4 +47,5 @@ internal BootstrapBootstrapClassLoader() RegisterInitiatingLoader(new StubTypeWrapper(Modifiers.Public | Modifiers.Final, "java.lang.Class", javaLangObject, false)); RegisterInitiatingLoader(new StubTypeWrapper(Modifiers.Public | Modifiers.Abstract, "java.lang.invoke.MethodHandle", javaLangObject, false)); } + } diff --git a/src/IKVM.Tools.Exporter/IKVM.Tools.Exporter.csproj b/src/IKVM.Tools.Exporter/IKVM.Tools.Exporter.csproj index 8903b2fc2d..e002a96386 100644 --- a/src/IKVM.Tools.Exporter/IKVM.Tools.Exporter.csproj +++ b/src/IKVM.Tools.Exporter/IKVM.Tools.Exporter.csproj @@ -1,46 +1,86 @@  - + net461;netcoreapp3.1 - $(DefineConstants);STUB_GENERATOR + $(DefineConstants);EXPORTER + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/src/IKVM.Tools.Exporter/IkvmExporter.cs b/src/IKVM.Tools.Exporter/IkvmExporter.cs deleted file mode 100644 index 868494ab36..0000000000 --- a/src/IKVM.Tools.Exporter/IkvmExporter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace IKVM.Tools.Exporter -{ - - /// - /// - /// - public class IkvmExporter - { - - public IkvmExporterContext context; - - /// - /// Initializes a new instance. - /// - /// - /// - public IkvmExporter(IkvmExporterOptions options) - { - this.context = new IkvmExporterContext(options ?? throw new ArgumentNullException(nameof(options))); - } - - /// - /// Executes the exporter. - /// - /// - /// - public Task ExecuteAsync(CancellationToken cancellationToken) - { - return context.ExecuteAsync(cancellationToken); - } - - } - -} diff --git a/src/IKVM.Tools.Exporter/IkvmExporterContext.NetCore.cs b/src/IKVM.Tools.Exporter/IkvmExporterContext.NetCore.cs index 69b8bee47f..f263decfd8 100644 --- a/src/IKVM.Tools.Exporter/IkvmExporterContext.NetCore.cs +++ b/src/IKVM.Tools.Exporter/IkvmExporterContext.NetCore.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.DependencyModel.Resolution; -using Newtonsoft.Json; +using System.Text.Json; namespace IKVM.Tools.Exporter { @@ -36,7 +36,7 @@ public IkvmExporterDispatcher(string options) if (options is null) throw new ArgumentNullException(nameof(options)); - this.options = JsonConvert.DeserializeObject(options); + this.options = JsonSerializer.Deserialize(options); } /// @@ -73,7 +73,8 @@ protected override Assembly Load(AssemblyName assemblyName) } - readonly object dispatcher; + IsolatedAssemblyLoadContext context; + object dispatcher; /// /// Initializes a new instance. @@ -83,10 +84,10 @@ protected override Assembly Load(AssemblyName assemblyName) public IkvmExporterContext(IkvmExporterOptions options) { // load the exporter in a nested assembly context - var ctx = new IsolatedAssemblyLoadContext("IkvmExporter", true); - var asm = ctx.LoadFromAssemblyName(typeof(IkvmExporterDispatcher).Assembly.GetName()); + context = new IsolatedAssemblyLoadContext("IkvmExporter", true); + var asm = context.LoadFromAssemblyName(typeof(IkvmExporterDispatcher).Assembly.GetName()); var typ = asm.GetType(typeof(IkvmExporterDispatcher).FullName); - dispatcher = Activator.CreateInstance(typ, new[] { JsonConvert.SerializeObject(options) }); + dispatcher = Activator.CreateInstance(typ, new[] { JsonSerializer.Serialize(options) }); } /// @@ -105,6 +106,17 @@ public partial async Task ExecuteAsync(CancellationToken cancellationToken) /// public void Dispose() { + try + { + dispatcher = null; + if (context != null) + context.Unload(); + } + finally + { + context = null; + } + GC.SuppressFinalize(this); } diff --git a/src/IKVM.Tools.Exporter/IkvmExporterInternal.cs b/src/IKVM.Tools.Exporter/IkvmExporterInternal.cs index 153c5ec8a5..2f44f95aef 100644 --- a/src/IKVM.Tools.Exporter/IkvmExporterInternal.cs +++ b/src/IKVM.Tools.Exporter/IkvmExporterInternal.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; -using ICSharpCode.SharpZipLib.Zip; - using IKVM.Internal; using IKVM.Reflection; +using IKVM.Runtime; +using IKVM.Tools.Importer; using Type = IKVM.Reflection.Type; @@ -19,16 +20,10 @@ namespace IKVM.Tools.Exporter static class IkvmExporterInternal { - static int zipCount; - static ZipOutputStream zipFile; + static ZipArchive zipFile; static Dictionary done = new Dictionary(); static Dictionary todo = new Dictionary(); static FileInfo file; - static bool includeSerialVersionUID; - static bool includeNonPublicInterfaces; - static bool includeNonPublicMembers; - static bool includeParameterNames; - static List namespaces = new List(); /// /// Executes the exporter. @@ -39,53 +34,30 @@ public static int Execute(IkvmExporterOptions options) IKVM.Internal.Tracer.EnableTraceConsoleListener(); IKVM.Internal.Tracer.EnableTraceForDebug(); - string assemblyNameOrPath = null; - bool continueOnError = false; - bool autoLoadSharedClassLoaderAssemblies = false; - List references = new List(); - List libpaths = new List(); - bool nostdlib = false; - bool bootstrap = false; - string outputFile = null; - bool forwarders = false; - - if (options.JApi) - { - includeSerialVersionUID = true; - includeNonPublicInterfaces = true; - includeNonPublicMembers = true; - } - - autoLoadSharedClassLoaderAssemblies = options.Shared; - continueOnError = options.SkipError; - nostdlib = options.NoStdLib; - bootstrap = options.Boostrap; - outputFile = options.Output; - forwarders = options.Forwarders; - includeParameterNames = options.Parameters; - assemblyNameOrPath = options.Assembly; - + var references = new List(); if (options.References != null) foreach (var reference in options.References) references.Add(reference); + var libpaths = new List(); if (options.Libraries != null) foreach (var library in options.Libraries) libpaths.Add(library); + var namespaces = new List(); if (options.Namespaces != null) foreach (var ns in options.Namespaces) namespaces.Add(ns); - if (File.Exists(assemblyNameOrPath) && nostdlib) + if (File.Exists(options.Assembly) && options.NoStdLib) { // Add the target assembly to the references list, to allow it to be considered as "mscorlib". // This allows "ikvmstub -nostdlib \...\mscorlib.dll" to work. - references.Add(assemblyNameOrPath); + references.Add(options.Assembly); } StaticCompiler.Resolver.Warning += new AssemblyResolver.WarningEvent(Resolver_Warning); - StaticCompiler.Resolver.Init(StaticCompiler.Universe, nostdlib, references, libpaths); + StaticCompiler.Resolver.Init(StaticCompiler.Universe, options.NoStdLib, references, libpaths); var cache = new Dictionary(); foreach (var reference in references) @@ -101,29 +73,30 @@ public static int Execute(IkvmExporterOptions options) Assembly assembly = null; try { - file = new FileInfo(assemblyNameOrPath); + file = new FileInfo(options.Assembly); } catch (Exception x) { - Console.Error.WriteLine("Error: unable to load \"{0}\"\n {1}", assemblyNameOrPath, x.Message); + Console.Error.WriteLine("Error: unable to load \"{0}\"\n {1}", options.Assembly, x.Message); return 1; } if (file != null && file.Exists) { - assembly = StaticCompiler.LoadFile(assemblyNameOrPath); + assembly = StaticCompiler.LoadFile(options.Assembly); } else { - assembly = StaticCompiler.Resolver.LoadWithPartialName(assemblyNameOrPath); + assembly = StaticCompiler.Resolver.LoadWithPartialName(options.Assembly); } int rc = 0; if (assembly == null) { - Console.Error.WriteLine("Error: Assembly \"{0}\" not found", assemblyNameOrPath); + rc = 1; + Console.Error.WriteLine("Error: Assembly \"{0}\" not found", options.Assembly); } else { - if (bootstrap) + if (options.Boostrap) { StaticCompiler.runtimeAssembly = StaticCompiler.LoadFile(typeof(IkvmExporterTool).Assembly.Location); ClassLoaderWrapper.SetBootstrapClassLoader(new BootstrapBootstrapClassLoader()); @@ -136,9 +109,9 @@ public static int Execute(IkvmExporterOptions options) if (runtimeAssemblyPath != null) StaticCompiler.runtimeAssembly = StaticCompiler.LoadFile(runtimeAssemblyPath); - var coreAssemblyPath = references.FirstOrDefault(i => Path.GetFileNameWithoutExtension(i) == "IKVM.Java"); - if (coreAssemblyPath != null) - JVM.CoreAssembly = StaticCompiler.LoadFile(coreAssemblyPath); + var baseAssemblyPath = references.FirstOrDefault(i => Path.GetFileNameWithoutExtension(i) == "IKVM.Java"); + if (baseAssemblyPath != null) + JVM.BaseAssembly = StaticCompiler.LoadFile(baseAssemblyPath); if (StaticCompiler.runtimeAssembly == null || StaticCompiler.runtimeAssembly.__IsMissing) { @@ -146,7 +119,7 @@ public static int Execute(IkvmExporterOptions options) return 1; } - if (JVM.CoreAssembly == null || StaticCompiler.runtimeAssembly.__IsMissing) + if (JVM.BaseAssembly == null || StaticCompiler.runtimeAssembly.__IsMissing) { Console.Error.WriteLine("Error: IKVM.Java not found."); return 1; @@ -156,39 +129,35 @@ public static int Execute(IkvmExporterOptions options) if (AttributeHelper.IsJavaModule(assembly.ManifestModule)) Console.Error.WriteLine("Warning: Running ikvmstub on ikvmc compiled assemblies is not supported."); - if (outputFile == null) - outputFile = assembly.GetName().Name + ".jar"; + if (options.Output == null) + options.Output = assembly.GetName().Name + ".jar"; try { - using (zipFile = new ZipOutputStream(new FileStream(outputFile, FileMode.Create))) + using (zipFile = new ZipArchive(new FileStream(options.Output, FileMode.Create), ZipArchiveMode.Create)) { - zipFile.SetComment(GetVersionAndCopyrightInfo()); try { - List assemblies = new List(); + var assemblies = new List(); assemblies.Add(assembly); - if (autoLoadSharedClassLoaderAssemblies) - { + + if (options.Shared) LoadSharedClassLoaderAssemblies(assembly, assemblies); - } - foreach (Assembly asm in assemblies) + + foreach (var asm in assemblies) { - if (ProcessTypes(asm.GetTypes(), continueOnError) != 0) + if (ProcessTypes(options, asm.GetTypes()) != 0) { rc = 1; - if (!continueOnError) - { + if (options.ContinueOnError == false) break; - } } - if (forwarders && ProcessTypes(asm.ManifestModule.__GetExportedTypes(), continueOnError) != 0) + + if (options.Forwarders && ProcessTypes(options, asm.ManifestModule.__GetExportedTypes()) != 0) { rc = 1; - if (!continueOnError) - { + if (options.ContinueOnError == false) break; - } } } } @@ -196,20 +165,17 @@ public static int Execute(IkvmExporterOptions options) { Console.Error.WriteLine(x); - if (!continueOnError) + if (options.ContinueOnError == false) Console.Error.WriteLine("Warning: Assembly reflection encountered an error. Resultant JAR may be incomplete."); rc = 1; } } } - catch (ZipException x) + catch (InvalidDataException x) { rc = 1; - if (zipCount == 0) - Console.Error.WriteLine("Error: Assembly contains no public IKVM.NET compatible types"); - else - Console.Error.WriteLine("Error: {0}", x.Message); + Console.Error.WriteLine("Error: {0}", x.Message); } } @@ -224,15 +190,6 @@ static void Resolver_Warning(AssemblyResolver.WarningId warning, string message, } } - static string GetVersionAndCopyrightInfo() - { - var asm = typeof(IkvmExporterTool).Assembly; - var desc = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); - var copy = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); - var info = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); - return $"{desc.Title} ({info.InformationalVersion}){Environment.NewLine}{copy.Copyright}"; // TODO: Add domain once we get one {Environment.NewLine}http://www.ikvm.org/ - } - private static void LoadSharedClassLoaderAssemblies(Assembly assembly, List assemblies) { if (assembly.GetManifestResourceInfo("ikvm.exports") != null) @@ -265,59 +222,43 @@ private static void LoadSharedClassLoaderAssemblies(Assembly assembly, List namespaces, Type type) { if (namespaces.Count == 0) - { return true; - } - string name = type.FullName; + + var name = type.FullName; foreach (string ns in namespaces) - { if (name.StartsWith(ns, StringComparison.Ordinal)) - { return true; - } - } + return false; } - private static int ProcessTypes(Type[] types, bool continueOnError) + private static int ProcessTypes(IkvmExporterOptions options, Type[] types) { int rc = 0; - foreach (Type t in types) + foreach (var t in types) { - if (t.IsPublic - && ExportNamespace(t) - && !t.IsGenericTypeDefinition - && !AttributeHelper.IsHideFromJava(t) - && (!t.IsGenericType || !AttributeHelper.IsJavaModule(t.Module))) + if ((t.IsPublic || options.IncludeNonPublicTypes) && ExportNamespace(options.Namespaces, t) && !t.IsGenericTypeDefinition && !AttributeHelper.IsHideFromJava(t) && (!t.IsGenericType || !AttributeHelper.IsJavaModule(t.Module))) { TypeWrapper c; if (ClassLoaderWrapper.IsRemappedType(t) || t.IsPrimitive || t == Types.Void) - { c = DotNetTypeWrapper.GetWrapperFromDotNetType(t); - } else - { c = ClassLoaderWrapper.GetWrapperFromType(t); - } + if (c != null) - { AddToExportList(c); - } } } @@ -325,7 +266,7 @@ private static int ProcessTypes(Type[] types, bool continueOnError) do { keepGoing = false; - foreach (TypeWrapper c in new List(todo.Values).OrderBy(i => i.Name)) + foreach (var c in new List(todo.Values).OrderBy(i => i.Name)) { if (!done.ContainsKey(c.Name)) { @@ -335,11 +276,11 @@ private static int ProcessTypes(Type[] types, bool continueOnError) try { ProcessClass(c); - WriteClass(c); + WriteClass(options, c); } catch (Exception x) { - if (continueOnError) + if (options.ContinueOnError) { rc = 1; Console.WriteLine(x); diff --git a/src/IKVM.Tools.Exporter/IkvmExporterOptions.cs b/src/IKVM.Tools.Exporter/IkvmExporterOptions.cs index fd0dab2a63..8fcb0e2eef 100644 --- a/src/IKVM.Tools.Exporter/IkvmExporterOptions.cs +++ b/src/IKVM.Tools.Exporter/IkvmExporterOptions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Security.Policy; namespace IKVM.Tools.Exporter { @@ -26,19 +27,43 @@ public class IkvmExporterOptions /// public List Libraries { get; set; } = new List(); - public bool JApi { get; set; } + /// + /// Set of namespaces to export. + /// + public List Namespaces { get; set; } = new List(); public bool Shared { get; set; } public bool NoStdLib { get; set; } - public List Namespaces { get; set; } = new List(); - public bool Forwarders { get; set; } - public bool Parameters { get; set; } + /// + /// Whether to export parameter names. + /// + public bool IncludeParameterNames { get; set; } + + /// + /// Whehter to export non-public types. + /// + public bool IncludeNonPublicTypes { get; set; } + + /// + /// Whether to export non-public interface implementations. + /// + public bool IncludeNonPublicInterfaces { get; set; } + + /// + /// Whether to export non-public members. + /// + public bool IncludeNonPublicMembers { get; set; } + + /// + /// Whether to export serialVersionUID fields. + /// + public bool SerialVersionUID { get; set; } - public bool SkipError { get; set; } + public bool ContinueOnError { get; set; } public bool Boostrap { get; set; } diff --git a/src/IKVM.Tools.Exporter/IkvmExporterTool.cs b/src/IKVM.Tools.Exporter/IkvmExporterTool.cs index e966343346..897c230cda 100644 --- a/src/IKVM.Tools.Exporter/IkvmExporterTool.cs +++ b/src/IKVM.Tools.Exporter/IkvmExporterTool.cs @@ -2,6 +2,7 @@ using System.CommandLine.Invocation; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace IKVM.Tools.Exporter @@ -18,24 +19,38 @@ public class IkvmExporterTool /// /// /// - public static Task Main(string[] args) + public static async Task Main(string[] args, CancellationToken cancellationToken) { - return new IkvmExporterTool().ExecuteAsync(args); + return await new IkvmExporterTool().ExecuteAsync(args, cancellationToken); + } + + /// + /// Executes the exporter. + /// + /// + /// + /// + public static async Task ExecuteAsync(IkvmExporterOptions options, CancellationToken cancellationToken) + { + using var exporter = new IkvmExporterContext(options); + return await exporter.ExecuteAsync(cancellationToken); } readonly RootCommand command; + readonly Argument assemblyArgument; readonly Option outputOption; readonly Option referenceOption; - readonly Option japiOption; - readonly Option skipErrorOption; - readonly Option sharedOption; - readonly Option noStdLibOption; readonly Option libraryOption; readonly Option namespaceOption; + readonly Option includeNonPublicTypesOption; + readonly Option includeNonPublicInterfacesOption; + readonly Option includeNonPublicMembersOption; + readonly Option includeParameterNamesOption; + readonly Option noStdLibOption; + readonly Option sharedOption; readonly Option forwardersOption; - readonly Option parametersOption; readonly Option bootstrapOption; - readonly Argument assemblyAttribute; + readonly Option continueOnErrorOption; /// /// Initializes a new instance. @@ -46,44 +61,44 @@ public IkvmExporterTool() { (outputOption = new Option( aliases: new[] { "--out", "-out", "-o" }, - description: "Specify the output filename.") - { - IsRequired = true - }), + description: "Specify the output filename.") { IsRequired = true }), (referenceOption = new Option( aliases: new [] { "--reference", "-reference", "-r" }, description: "Reference an assembly.")), - (japiOption = new Option( - aliases: new [] { "--japi", "-japi" }, - description: "Generate jar suitable for comparison with japitools.")), - (skipErrorOption = new Option( - aliases: new [] { "--skiperror", "-skiperror" }, - description: "Continue when errors are encountered.")), - (sharedOption = new Option( - aliases: new [] { "--shared", "-shared" }, - description: "Process all assemblies in shared group.")), - (noStdLibOption = new Option( - aliases: new [] { "--nostdlib", "-nostdlib" }, - description: "Do not reference standard libraries.")), (libraryOption = new Option( aliases: new [] { "--lib", "-lib", "-l" }, description: "Additional directories to search for references.")), (namespaceOption = new Option( aliases: new [] { "--ns", "-ns" }, description: "Only include types from specified namespace.")), + (includeNonPublicTypesOption = new Option( + aliases: new [] { "--non-public-types", "-non-public-types" }, + description: "Include classes for non-public types.")), + (includeNonPublicInterfacesOption = new Option( + aliases: new [] { "--non-public-interfaces", "-non-public-interfaces" }, + description: "Include non-public interface implementations.")), + (includeNonPublicMembersOption = new Option( + aliases: new [] { "--non-public-members", "-non-public-members" }, + description: "Include non-public members.")), + (includeParameterNamesOption = new Option( + aliases: new [] { "--parameters", "-parameters" }, + description: "Include parameter names.")), (forwardersOption = new Option( aliases: new [] { "--forwarders", "-forwarders" }, - description: "Export forwarded types too.")), - (parametersOption = new Option( - aliases: new [] { "--parameters", "-parameters" }, - description: "Emit Java 8 classes with parameter names.")), + description: "Export forwarded types.")), + (noStdLibOption = new Option( + aliases: new [] { "--nostdlib", "-nostdlib" }, + description: "Do not reference standard libraries.")), + (sharedOption = new Option( + aliases: new [] { "--shared", "-shared" }, + description: "Process all assemblies in shared group.")), (bootstrapOption = new Option( aliases: new [] { "--bootstrap", "-bootstrap" }, - description: "Enabled bootstrap mode.") - { - IsHidden = true, - }), - (assemblyAttribute = new Argument( + description: "Enabled bootstrap mode.") { IsHidden = true }), + (continueOnErrorOption = new Option( + aliases: new [] { "--skiperror", "-skiperror" }, + description: "Continue when errors are encountered.")), + (assemblyArgument = new Argument( name: "assemblyNameOrPath", description: "Path or name of assembly to export.")), }; @@ -95,10 +110,11 @@ public IkvmExporterTool() /// Executes the exporter. /// /// + /// /// - Task ExecuteAsync(string[] args) + async Task ExecuteAsync(string[] args, CancellationToken cancellationToken) { - return command.InvokeAsync(args); + return await command.InvokeAsync(args); } /// @@ -106,24 +122,23 @@ Task ExecuteAsync(string[] args) /// /// /// - Task ExecuteAsync(InvocationContext context) + async Task ExecuteAsync(InvocationContext context) => await ExecuteAsync(new IkvmExporterOptions() { - return Task.FromResult(IkvmExporterInternal.Execute(new IkvmExporterOptions() - { - Output = context.ParseResult.GetValueForOption(outputOption).FullName, - References = context.ParseResult.GetValueForOption(referenceOption).Select(i => i.FullName).ToList(), - JApi = context.ParseResult.GetValueForOption(japiOption), - SkipError = context.ParseResult.GetValueForOption(skipErrorOption), - Shared = context.ParseResult.GetValueForOption(sharedOption), - NoStdLib = context.ParseResult.GetValueForOption(noStdLibOption), - Libraries = context.ParseResult.GetValueForOption(libraryOption).Select(i => i.FullName).ToList(), - Namespaces = context.ParseResult.GetValueForOption(namespaceOption).ToList(), - Forwarders = context.ParseResult.GetValueForOption(forwardersOption), - Parameters = context.ParseResult.GetValueForOption(parametersOption), - Boostrap = context.ParseResult.GetValueForOption(bootstrapOption), - Assembly = context.ParseResult.GetValueForArgument(assemblyAttribute) - })); - } + Output = context.ParseResult.GetValueForOption(outputOption).FullName, + References = context.ParseResult.GetValueForOption(referenceOption).Select(i => i.FullName).ToList(), + Libraries = context.ParseResult.GetValueForOption(libraryOption).Select(i => i.FullName).ToList(), + Namespaces = context.ParseResult.GetValueForOption(namespaceOption).ToList(), + IncludeNonPublicTypes = context.ParseResult.GetValueForOption(includeNonPublicTypesOption), + IncludeNonPublicInterfaces = context.ParseResult.GetValueForOption(includeNonPublicInterfacesOption), + IncludeNonPublicMembers = context.ParseResult.GetValueForOption(includeNonPublicMembersOption), + IncludeParameterNames = context.ParseResult.GetValueForOption(includeParameterNamesOption), + Forwarders = context.ParseResult.GetValueForOption(forwardersOption), + NoStdLib = context.ParseResult.GetValueForOption(noStdLibOption), + Shared = context.ParseResult.GetValueForOption(sharedOption), + Boostrap = context.ParseResult.GetValueForOption(bootstrapOption), + ContinueOnError = context.ParseResult.GetValueForOption(continueOnErrorOption), + Assembly = context.ParseResult.GetValueForArgument(assemblyArgument) + }, CancellationToken.None); } diff --git a/src/IKVM.Tools.Exporter/StaticCompiler.cs b/src/IKVM.Tools.Exporter/StaticCompiler.cs index 601897279b..f6873cbb94 100644 --- a/src/IKVM.Tools.Exporter/StaticCompiler.cs +++ b/src/IKVM.Tools.Exporter/StaticCompiler.cs @@ -21,8 +21,8 @@ Jeroen Frijters jeroen@frijters.net */ -using IKVM.Internal; using IKVM.Reflection; +using IKVM.Tools.Importer; using Type = IKVM.Reflection.Type; diff --git a/src/ikvmc.Tests/ikvmc.Tests.csproj b/src/IKVM.Tools.Importer.Tests/IKVM.Tools.Importer.Tests.csproj similarity index 83% rename from src/ikvmc.Tests/ikvmc.Tests.csproj rename to src/IKVM.Tools.Importer.Tests/IKVM.Tools.Importer.Tests.csproj index b9c67ba432..06323f21b9 100644 --- a/src/ikvmc.Tests/ikvmc.Tests.csproj +++ b/src/IKVM.Tools.Importer.Tests/IKVM.Tools.Importer.Tests.csproj @@ -1,5 +1,4 @@  - net461;netcoreapp3.1 true @@ -16,22 +15,21 @@ - + - + - - + - + - + \ No newline at end of file diff --git a/src/ikvmc.Tests/IkvmcTests.cs b/src/IKVM.Tools.Importer.Tests/IkvmImporterTests.cs similarity index 59% rename from src/ikvmc.Tests/IkvmcTests.cs rename to src/IKVM.Tools.Importer.Tests/IkvmImporterTests.cs index b34d478d77..106706cd3e 100644 --- a/src/ikvmc.Tests/IkvmcTests.cs +++ b/src/IKVM.Tools.Importer.Tests/IkvmImporterTests.cs @@ -2,6 +2,8 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using FluentAssertions; @@ -13,18 +15,18 @@ using Microsoft.Extensions.DependencyModel; #endif -namespace ikvmc.Tests +namespace IKVM.Tools.Importer.Tests { [TestClass] - public class IkvmcTests + public class IkvmImporterTests { [TestMethod] - public void Should_convert_simple_jar() + public async Task CanImportSimpleTest() { - var s = new StreamReader(typeof(IkvmcTests).Assembly.GetManifestResourceStream("ikvmc.Tests.IkvmcTests.java")).ReadToEnd(); - var f = new InMemoryCodeUnit("ikvmc.tests.IkvmcTests", s); + var s = new StreamReader(typeof(IkvmImporterTests).Assembly.GetManifestResourceStream("IKVM.Tools.Importer.Tests.IkvmImporterTests.java")).ReadToEnd(); + var f = new InMemoryCodeUnit("ikvm.tools.importer.tests.IkvmImporterTests", s); var c = new InMemoryCompiler(new[] { f }); c.Compile(); var j = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("n") + ".jar"); @@ -37,11 +39,10 @@ public void Should_convert_simple_jar() #endif var asm = Path.ChangeExtension(j, ".dll"); - var ret = ikvmc.IkvmcCompiler.Main(a.Concat(new[] { "-nostdlib", "-assembly:ikvmc.Tests.Java", $"-out:{asm}", j }).ToArray()); + var ret = await IkvmImporterTool.Main(a.Concat(new[] { "-nostdlib", "-assembly:IKVM.Tools.Importer.Tests.Java", $"-out:{asm}", j }).ToArray(), CancellationToken.None); ret.Should().Be(0); File.Exists(asm).Should().BeTrue(); } - } } \ No newline at end of file diff --git a/src/ikvmc.Tests/IkvmcTests.java b/src/IKVM.Tools.Importer.Tests/IkvmImporterTests.java similarity index 50% rename from src/ikvmc.Tests/IkvmcTests.java rename to src/IKVM.Tools.Importer.Tests/IkvmImporterTests.java index 5cabed608a..44cf77d204 100644 --- a/src/ikvmc.Tests/IkvmcTests.java +++ b/src/IKVM.Tools.Importer.Tests/IkvmImporterTests.java @@ -1,6 +1,6 @@ -package ikvmc.tests; +package ikvm.tools.importer.tests; -public class IkvmcTests +public class IkvmImporterTests { public static String echo(String value) diff --git a/src/ikvmc/IKVM/Internal/AotTypeWrapper.cs b/src/IKVM.Tools.Importer/AotTypeWrapper.cs similarity index 89% rename from src/ikvmc/IKVM/Internal/AotTypeWrapper.cs rename to src/IKVM.Tools.Importer/AotTypeWrapper.cs index 7dc02be23a..90f5758c64 100644 --- a/src/ikvmc/IKVM/Internal/AotTypeWrapper.cs +++ b/src/IKVM.Tools.Importer/AotTypeWrapper.cs @@ -26,16 +26,20 @@ Jeroen Frijters using System.Collections.Generic; using IKVM.Attributes; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using IKVM.Runtime; +using IKVM.Tools.Importer.MapXml; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal +namespace IKVM.Tools.Importer { + sealed class AotTypeWrapper : DynamicTypeWrapper { + private FieldInfo ghostRefField; private MethodBuilder ghostIsInstanceMethod; private MethodBuilder ghostIsInstanceArrayMethod; @@ -47,9 +51,15 @@ sealed class AotTypeWrapper : DynamicTypeWrapper private MethodWrapper[] replacedMethods; private WorkaroundBaseClass workaroundBaseClass; - internal AotTypeWrapper(ClassFile f, CompilerClassLoader loader) - : base(null, f, loader, null) + /// + /// Initializes a new instance. + /// + /// + /// + internal AotTypeWrapper(ClassFile f, CompilerClassLoader loader) : + base(null, f, loader, null) { + } protected override Type GetBaseTypeForDefineType() @@ -216,39 +226,26 @@ internal override Type TypeAsBaseType internal void GetParameterNamesFromXml(string methodName, string methodSig, string[] parameterNames) { - IKVM.Internal.MapXml.Param[] parameters = classLoader.GetXmlMapParameters(Name, methodName, methodSig); + var parameters = classLoader.GetXmlMapParameters(Name, methodName, methodSig); if (parameters != null) - { for (int i = 0; i < parameters.Length; i++) - { if (parameters[i].Name != null) - { parameterNames[i] = parameters[i].Name; - } - } - } } - internal void AddXmlMapParameterAttributes(MethodBuilder method, string className, string methodName, string methodSig, ref ParameterBuilder[] pbs) + internal void AddXmlMapParameterAttributes(MethodBuilder method, string className, string methodName, string methodSig, ref ParameterBuilder[] parameterBuilders) { - IKVM.Internal.MapXml.Param[] parameters = classLoader.GetXmlMapParameters(className, methodName, methodSig); + var parameters = classLoader.GetXmlMapParameters(className, methodName, methodSig); if (parameters != null) { - if (pbs == null) - { - // let's hope that the parameters array is the right length - pbs = GetParameterBuilders(method, parameters.Length, null); - } - for (int i = 0; i < pbs.Length; i++) - { + parameterBuilders ??= GetParameterBuilders(method, parameters.Length, null); + if (parameters.Length > parameterBuilders.Length) + throw new InvalidOperationException($"Number of parameters in map XML exceeds the number of parameters being built for '{className}:{methodName}' ({parameters.Length} > {parameterBuilders.Length})."); + + for (int i = 0; i < parameters.Length; i++) if (parameters[i].Attributes != null) - { - foreach (IKVM.Internal.MapXml.Attribute attr in parameters[i].Attributes) - { - AttributeHelper.SetCustomAttribute(classLoader, pbs[i], attr); - } - } - } + foreach (var attr in parameters[i].Attributes) + AttributeHelper.SetCustomAttribute(classLoader, parameterBuilders[i], attr); } } @@ -266,92 +263,86 @@ private void AddParameterMetadata(MethodBuilder method, MethodWrapper mw) { pbs = GetParameterBuilders(method, mw.GetParameters().Length, null); } + if ((mw.Modifiers & Modifiers.VarArgs) != 0 && pbs.Length > 0) - { AttributeHelper.SetParamArrayAttribute(pbs[pbs.Length - 1]); - } + AddXmlMapParameterAttributes(method, Name, mw.Name, mw.Signature, ref pbs); } + /// + /// Applies fields from the map XML. + /// + /// protected override void AddMapXmlFields(ref FieldWrapper[] fields) { - Dictionary mapxml = classLoader.GetMapXmlClasses(); - if (mapxml != null) + var mapxml = classLoader.GetMapXmlClasses(); + if (mapxml != null && mapxml.TryGetValue(Name, out var clazz) && clazz.Fields != null) { - IKVM.Internal.MapXml.Class clazz; - if (mapxml.TryGetValue(this.Name, out clazz)) + foreach (var field in clazz.Fields) { - if (clazz.Fields != null) + // are we adding a new field? + bool found = false; + foreach (var fw in fields) { - foreach (IKVM.Internal.MapXml.Field field in clazz.Fields) + if (fw.Name == field.Name && fw.Signature == field.Sig) { - // are we adding a new field? - bool found = false; - foreach (FieldWrapper fw in fields) - { - if (fw.Name == field.Name && fw.Signature == field.Sig) - { - found = true; - break; - } - } - if (!found) - { - fields = ArrayUtil.Concat(fields, FieldWrapper.Create(this, null, null, field.Name, field.Sig, new ExModifiers((Modifiers)field.Modifiers, false))); - } + found = true; + break; } } + + if (found == false) + fields = ArrayUtil.Concat(fields, FieldWrapper.Create(this, null, null, field.Name, field.Sig, new ExModifiers((Modifiers)field.Modifiers, false))); } } } protected override bool EmitMapXmlMethodPrologueAndOrBody(CodeEmitter ilgen, ClassFile f, ClassFile.Method m) { - IKVM.Internal.MapXml.InstructionList prologue = classLoader.GetMethodPrologue(new MethodKey(f.Name, m.Name, m.Signature)); + var prologue = classLoader.GetMethodPrologue(new MethodKey(f.Name, m.Name, m.Signature)); if (prologue != null) - { prologue.Emit(classLoader, ilgen); - } - Dictionary mapxml = classLoader.GetMapXmlMethodBodies(); - if (mapxml != null) + + var mapxml = classLoader.GetMapXmlMethodBodies(); + if (mapxml != null && mapxml.TryGetValue(new MethodKey(f.Name, m.Name, m.Signature), out var opcodes)) { - IKVM.Internal.MapXml.InstructionList opcodes; - if (mapxml.TryGetValue(new MethodKey(f.Name, m.Name, m.Signature), out opcodes)) - { - opcodes.Emit(classLoader, ilgen); - return true; - } + opcodes.Emit(classLoader, ilgen); + return true; } + return false; } - private void PublishAttributes(TypeBuilder typeBuilder, IKVM.Internal.MapXml.Class clazz) + void PublishAttributes(TypeBuilder typeBuilder, Class clazz) { - foreach (IKVM.Internal.MapXml.Attribute attr in clazz.Attributes) - { + foreach (var attr in clazz.Attributes) AttributeHelper.SetCustomAttribute(classLoader, typeBuilder, attr); - } } - private static bool CheckPropertyArgs(Type[] args1, Type[] args2) + /// + /// Returns true if the given type arrays are equal. + /// + /// + /// + /// + static bool CheckPropertyArgs(Type[] args1, Type[] args2) { if (args1.Length == args2.Length) { for (int i = 0; i < args1.Length; i++) - { if (args1[i] != args2[i]) - { return false; - } - } + return true; } + return false; } - private static MethodAttributes GetPropertyMethodAttributes(MethodWrapper mw, bool final) + static MethodAttributes GetPropertyMethodAttributes(MethodWrapper mw, bool final) { - MethodAttributes attribs = MethodAttributes.HideBySig; + var attribs = MethodAttributes.HideBySig; if (mw.IsStatic) { attribs |= MethodAttributes.Static; @@ -364,33 +355,25 @@ private static MethodAttributes GetPropertyMethodAttributes(MethodWrapper mw, bo // or not (and vice versa). attribs |= MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride; if (final) - { attribs |= MethodAttributes.Final; - } } + // TODO what happens if accessibility doesn't match our peer? if (mw.IsPublic) - { attribs |= MethodAttributes.Public; - } else if (mw.IsProtected) - { attribs |= MethodAttributes.FamORAssem; - } else if (mw.IsPrivate) - { attribs |= MethodAttributes.Private; - } else - { attribs |= MethodAttributes.Assembly; - } + return attribs; } - private void PublishProperties(TypeBuilder typeBuilder, IKVM.Internal.MapXml.Class clazz) + private void PublishProperties(TypeBuilder typeBuilder, Class clazz) { - foreach (IKVM.Internal.MapXml.Property prop in clazz.Properties) + foreach (var prop in clazz.Properties) { TypeWrapper typeWrapper = GetClassLoader().RetTypeWrapperFromSig(prop.Sig, LoadMode.Link); TypeWrapper[] propargs = GetClassLoader().ArgTypeWrapperListFromSig(prop.Sig, LoadMode.Link); @@ -403,24 +386,24 @@ private void PublishProperties(TypeBuilder typeBuilder, IKVM.Internal.MapXml.Cla AttributeHelper.HideFromJava(propbuilder); if (prop.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in prop.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in prop.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, propbuilder, attr); } } MethodWrapper getter = null; MethodWrapper setter = null; - if (prop.getter != null) + if (prop.Getter != null) { - getter = GetMethodWrapper(prop.getter.Name, prop.getter.Sig, true); + getter = GetMethodWrapper(prop.Getter.Name, prop.Getter.Sig, true); if (getter == null) { Console.Error.WriteLine("Warning: getter not found for {0}::{1}", clazz.Name, prop.Name); } } - if (prop.setter != null) + if (prop.Setter != null) { - setter = GetMethodWrapper(prop.setter.Name, prop.setter.Sig, true); + setter = GetMethodWrapper(prop.Setter.Name, prop.Setter.Sig, true); if (setter == null) { Console.Error.WriteLine("Warning: setter not found for {0}::{1}", clazz.Name, prop.Name); @@ -507,7 +490,7 @@ private void PublishProperties(TypeBuilder typeBuilder, IKVM.Internal.MapXml.Cla } } - private static void MapModifiers(MapXml.MapModifiers mapmods, bool isConstructor, out bool setmodifiers, ref MethodAttributes attribs, bool isNewSlot) + private static void MapModifiers(MapModifiers mapmods, bool isConstructor, out bool setmodifiers, ref MethodAttributes attribs, bool isNewSlot) { setmodifiers = false; Modifiers modifiers = (Modifiers)mapmods; @@ -582,10 +565,10 @@ private void MapSignature(string sig, out Type returnType, out Type[] parameterT protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile classFile, FieldWrapper[] fields, MethodWrapper[] methods) { - Dictionary mapxml = classLoader.GetMapXmlClasses(); + Dictionary mapxml = classLoader.GetMapXmlClasses(); if (mapxml != null) { - IKVM.Internal.MapXml.Class clazz; + IKVM.Tools.Importer.MapXml.Class clazz; if (mapxml.TryGetValue(classFile.Name, out clazz)) { if (clazz.Attributes != null) @@ -598,7 +581,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl } if (clazz.Fields != null) { - foreach (IKVM.Internal.MapXml.Field field in clazz.Fields) + foreach (IKVM.Tools.Importer.MapXml.Field field in clazz.Fields) { if (field.Attributes != null) { @@ -609,7 +592,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl FieldBuilder fb = fw.GetField() as FieldBuilder; if (fb != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in field.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in field.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, fb, attr); } @@ -622,12 +605,12 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl if (clazz.Constructors != null) { // HACK this isn't the right place to do this, but for now it suffices - foreach (IKVM.Internal.MapXml.Constructor constructor in clazz.Constructors) + foreach (IKVM.Tools.Importer.MapXml.Constructor constructor in clazz.Constructors) { // are we adding a new constructor? if (GetMethodWrapper(StringConstants.INIT, constructor.Sig, false) == null) { - if (constructor.body == null) + if (constructor.Body == null) { Console.Error.WriteLine("Error: Constructor {0}.{1} in xml remap file doesn't have a body.", clazz.Name, constructor.Sig); continue; @@ -643,20 +626,20 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl { AttributeHelper.SetModifiers(cb, (Modifiers)constructor.Modifiers, false); } - CompilerClassLoader.AddDeclaredExceptions(cb, constructor.throws); + CompilerClassLoader.AddDeclaredExceptions(cb, constructor.Throws); CodeEmitter ilgen = CodeEmitter.Create(cb); constructor.Emit(classLoader, ilgen); ilgen.DoEmit(); if (constructor.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in constructor.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in constructor.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, cb, attr); } } } } - foreach (IKVM.Internal.MapXml.Constructor constructor in clazz.Constructors) + foreach (IKVM.Tools.Importer.MapXml.Constructor constructor in clazz.Constructors) { if (constructor.Attributes != null) { @@ -667,7 +650,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl MethodBuilder mb = mw.GetMethod() as MethodBuilder; if (mb != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in constructor.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in constructor.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, mb, attr); } @@ -680,7 +663,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl if (clazz.Methods != null) { // HACK this isn't the right place to do this, but for now it suffices - foreach (IKVM.Internal.MapXml.Method method in clazz.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method method in clazz.Methods) { // are we adding a new method? if (GetMethodWrapper(method.Name, method.Sig, false) == null) @@ -688,7 +671,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl bool setmodifiers = false; MethodAttributes attribs = method.MethodAttributes; MapModifiers(method.Modifiers, false, out setmodifiers, ref attribs, BaseTypeWrapper == null || BaseTypeWrapper.GetMethodWrapper(method.Name, method.Sig, true) == null); - if (method.body == null && (attribs & MethodAttributes.Abstract) == 0) + if (method.Body == null && (attribs & MethodAttributes.Abstract) == 0) { Console.Error.WriteLine("Error: Method {0}.{1}{2} in xml remap file doesn't have a body.", clazz.Name, method.Name, method.Sig); continue; @@ -701,14 +684,14 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl { AttributeHelper.SetModifiers(mb, (Modifiers)method.Modifiers, false); } - if (method.@override != null) + if (method.Override != null) { - MethodWrapper mw = GetClassLoader().LoadClassByDottedName(method.@override.Class).GetMethodWrapper(method.@override.Name, method.Sig, true); + MethodWrapper mw = GetClassLoader().LoadClassByDottedName(method.Override.Class).GetMethodWrapper(method.Override.Name, method.Sig, true); mw.Link(); typeBuilder.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); } - CompilerClassLoader.AddDeclaredExceptions(mb, method.throws); - if (method.body != null) + CompilerClassLoader.AddDeclaredExceptions(mb, method.Throws); + if (method.Body != null) { CodeEmitter ilgen = CodeEmitter.Create(mb); method.Emit(classLoader, ilgen); @@ -716,14 +699,14 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl } if (method.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in method.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in method.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, mb, attr); } } } } - foreach (IKVM.Internal.MapXml.Method method in clazz.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method method in clazz.Methods) { if (method.Attributes != null) { @@ -734,7 +717,7 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl MethodBuilder mb = mw.GetMethod() as MethodBuilder; if (mb != null) { - foreach (IKVM.Internal.MapXml.Attribute attr in method.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute attr in method.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, mb, attr); } @@ -746,15 +729,15 @@ protected override void EmitMapXmlMetadata(TypeBuilder typeBuilder, ClassFile cl } if (clazz.Interfaces != null) { - foreach (IKVM.Internal.MapXml.Interface iface in clazz.Interfaces) + foreach (Implements iface in clazz.Interfaces) { - TypeWrapper tw = GetClassLoader().LoadClassByDottedName(iface.Name); + TypeWrapper tw = GetClassLoader().LoadClassByDottedName(iface.Class); // NOTE since this interface won't be part of the list in the ImplementAttribute, // it won't be visible from Java that the type implements this interface. typeBuilder.AddInterfaceImplementation(tw.TypeAsBaseType); if (iface.Methods != null) { - foreach (IKVM.Internal.MapXml.Method m in iface.Methods) + foreach (Method m in iface.Methods) { MethodWrapper mw = tw.GetMethodWrapper(m.Name, m.Sig, false); if (mw == null) @@ -1188,15 +1171,15 @@ internal override Type EnumType private sealed class ReplacedMethodWrapper : MethodWrapper { - private IKVM.Internal.MapXml.InstructionList code; + private IKVM.Tools.Importer.MapXml.InstructionList code; - internal ReplacedMethodWrapper(TypeWrapper tw, string name, string sig, IKVM.Internal.MapXml.InstructionList code) + internal ReplacedMethodWrapper(TypeWrapper tw, string name, string sig, IKVM.Tools.Importer.MapXml.InstructionList code) : base(tw, name, sig, null, null, null, Modifiers.Public, MemberFlags.None) { this.code = code; } - internal ReplacedMethodWrapper(ClassFile.ConstantPoolItemMI cpi, IKVM.Internal.MapXml.InstructionList code) + internal ReplacedMethodWrapper(ClassFile.ConstantPoolItemMI cpi, IKVM.Tools.Importer.MapXml.InstructionList code) : base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), Modifiers.Public, MemberFlags.None) { this.code = code; @@ -1208,11 +1191,11 @@ protected override void DoLinkMethod() private void DoEmit(CodeEmitter ilgen) { - IKVM.Internal.MapXml.CodeGenContext context = new IKVM.Internal.MapXml.CodeGenContext(this.DeclaringType.GetClassLoader()); + IKVM.Tools.Importer.MapXml.CodeGenContext context = new IKVM.Tools.Importer.MapXml.CodeGenContext(this.DeclaringType.GetClassLoader()); // we don't want the line numbers from map.xml, so we have our own emit loop - for (int i = 0; i < code.invoke.Length; i++) + for (int i = 0; i < code.Instructions.Length; i++) { - code.invoke[i].Generate(context, ilgen); + code.Instructions[i].Generate(context, ilgen); } } @@ -1234,7 +1217,7 @@ internal override void EmitNewobj(CodeEmitter ilgen) internal override MethodWrapper[] GetReplacedMethodsFor(MethodWrapper mw) { - IKVM.Internal.MapXml.ReplaceMethodCall[] replacedMethods = ((CompilerClassLoader)GetClassLoader()).GetReplacedMethodsFor(mw); + IKVM.Tools.Importer.MapXml.ReplaceMethodCall[] replacedMethods = ((CompilerClassLoader)GetClassLoader()).GetReplacedMethodsFor(mw); MethodWrapper[] baseReplacedMethodWrappers = base.GetReplacedMethodsFor(mw); if (replacedMethods != null || baseReplacedMethodWrappers != null || this.replacedMethods != null) { @@ -1243,7 +1226,7 @@ internal override MethodWrapper[] GetReplacedMethodsFor(MethodWrapper mw) { for (int i = 0; i < replacedMethods.Length; i++) { - list.Add(new ReplacedMethodWrapper(GetClassLoader().LoadClassByDottedName(replacedMethods[i].Class), replacedMethods[i].Name, replacedMethods[i].Sig, replacedMethods[i].code)); + list.Add(new ReplacedMethodWrapper(GetClassLoader().LoadClassByDottedName(replacedMethods[i].Class), replacedMethods[i].Name, replacedMethods[i].Sig, replacedMethods[i].Code)); } } if (baseReplacedMethodWrappers != null) @@ -1278,19 +1261,15 @@ internal MethodWrapper ReplaceMethodWrapper(MethodWrapper mw) } } } + return mw; } - internal override MethodBase GetBaseSerializationConstructor() + internal override IKVM.Reflection.MethodBase GetBaseSerializationConstructor() { - if (workaroundBaseClass != null) - { - return workaroundBaseClass.GetSerializationConstructor(); - } - else - { - return base.GetBaseSerializationConstructor(); - } + return workaroundBaseClass != null ? workaroundBaseClass.GetSerializationConstructor() : base.GetBaseSerializationConstructor(); } + } + } diff --git a/src/ikvmc/IKVM/Internal/AssemblyResolver.cs b/src/IKVM.Tools.Importer/AssemblyResolver.cs similarity index 99% rename from src/ikvmc/IKVM/Internal/AssemblyResolver.cs rename to src/IKVM.Tools.Importer/AssemblyResolver.cs index 36217e6128..4315773afc 100644 --- a/src/ikvmc/IKVM/Internal/AssemblyResolver.cs +++ b/src/IKVM.Tools.Importer/AssemblyResolver.cs @@ -25,12 +25,15 @@ Jeroen Frijters using System.Collections.Generic; using System.IO; +using IKVM.Internal; using IKVM.Reflection; -namespace IKVM.Internal +namespace IKVM.Tools.Importer { + sealed class AssemblyResolver { + private readonly List libpath = new List(); private Universe universe; private Version coreLibVersion; @@ -93,7 +96,7 @@ internal void Init(Universe universe, bool nostdlib, IList references, I coreLibVersion = universe.Load(Universe.CoreLibName).GetName().Version; } -#if STATIC_COMPILER +#if IMPORTER universe.AssemblyResolve += AssemblyResolve; #else universe.AssemblyResolve += LegacyAssemblyResolve; @@ -345,7 +348,7 @@ internal Assembly LegacyLoad(AssemblyName name, Assembly requestingAssembly) } else { -#if STUB_GENERATOR +#if EXPORTER return universe.CreateMissingAssembly(name.FullName); #else Console.Error.WriteLine("Error: unable to find assembly '{0}'", name.FullName); diff --git a/src/ikvmc/IKVM/Internal/CompilerClassLoader.cs b/src/IKVM.Tools.Importer/CompilerClassLoader.cs similarity index 84% rename from src/ikvmc/IKVM/Internal/CompilerClassLoader.cs rename to src/IKVM.Tools.Importer/CompilerClassLoader.cs index 7cded678cb..6122369687 100644 --- a/src/ikvmc/IKVM/Internal/CompilerClassLoader.cs +++ b/src/IKVM.Tools.Importer/CompilerClassLoader.cs @@ -26,25 +26,23 @@ Jeroen Frijters using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Compression; using System.Security; -using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; -using System.Xml; - -using ICSharpCode.SharpZipLib.Zip; +using System.Xml.Linq; using IKVM.Attributes; +using IKVM.ByteCode.Reading; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using IKVM.Runtime; -using ikvmc; - using Type = IKVM.Reflection.Type; -namespace IKVM.Internal +namespace IKVM.Tools.Importer { sealed class CompilerClassLoader : ClassLoaderWrapper @@ -52,34 +50,34 @@ sealed class CompilerClassLoader : ClassLoaderWrapper const string DEFAULT_RUNTIME_ARGS_PREFIX = "-J"; - private Dictionary classes; - private Dictionary remapped = new Dictionary(); - private string assemblyName; - private string assemblyFile; - private string assemblyDir; - private bool targetIsModule; - private AssemblyBuilder assemblyBuilder; - private IKVM.Internal.MapXml.Attribute[] assemblyAttributes; - private CompilerOptions options; - private AssemblyClassLoader[] referencedAssemblies; - private Dictionary nameMappings = new Dictionary(); - private Packages packages; - private Dictionary> ghosts; - private TypeWrapper[] mappedExceptions; - private bool[] mappedExceptionsAllSubClasses; - private Dictionary mapxml_Classes; - private Dictionary mapxml_MethodBodies; - private Dictionary mapxml_ReplacedMethods; - private Dictionary mapxml_MethodPrologues; - private IKVM.Internal.MapXml.Root map; - private List classesToCompile; - private List peerReferences = new List(); - private Dictionary peerLoading = new Dictionary(); - private List internalsVisibleTo = new List(); - private List dynamicallyImportedTypes = new List(); - private List jarList = new List(); - private List allwrappers; - private bool compilingCoreAssembly; + Dictionary classes; + Dictionary remapped = new Dictionary(); + string assemblyName; + string assemblyFile; + string assemblyDir; + bool targetIsModule; + AssemblyBuilder assemblyBuilder; + MapXml.Attribute[] assemblyAttributes; + CompilerOptions options; + AssemblyClassLoader[] referencedAssemblies; + Dictionary nameMappings = new Dictionary(); + Packages packages; + Dictionary> ghosts; + TypeWrapper[] mappedExceptions; + bool[] mappedExceptionsAllSubClasses; + Dictionary mapxml_Classes; + Dictionary mapxml_MethodBodies; + Dictionary mapxml_ReplacedMethods; + Dictionary mapxml_MethodPrologues; + MapXml.Root map; + List classesToCompile; + List peerReferences = new List(); + Dictionary peerLoading = new Dictionary(); + List internalsVisibleTo = new List(); + List dynamicallyImportedTypes = new List(); + List jarList = new List(); + List allwrappers; + bool compilingCoreAssembly; internal CompilerClassLoader(AssemblyClassLoader[] referencedAssemblies, CompilerOptions options, FileInfo assemblyPath, bool targetIsModule, string assemblyName, Dictionary classes, bool compilingCoreAssembly) : base(options.codegenoptions, null) @@ -285,8 +283,7 @@ private TypeWrapper GetTypeWrapperCompilerHook(string name) ClassFile f; try { - byte[] buf = itemRef.GetData(); - f = new ClassFile(buf, 0, buf.Length, name, ClassFileParseOptions, null); + f = new ClassFile(ClassReader.Read(itemRef.GetData()), name, ClassFileParseOptions, null); } catch (ClassFormatError x) { @@ -294,12 +291,14 @@ private TypeWrapper GetTypeWrapperCompilerHook(string name) StaticCompiler.IssueMessage(options, Message.ClassFormatError, name, x.Message); return null; } + if (f.Name != name) { StaticCompiler.SuppressWarning(options, Message.ClassNotFound, name); StaticCompiler.IssueMessage(options, Message.WrongClassName, name, f.Name); return null; } + if (f.IsPublic && options.privatePackages != null) { foreach (string p in options.privatePackages) @@ -741,13 +740,8 @@ private void WriteResources() { var hasEntries = false; var mem = new MemoryStream(); - using (ZipOutputStream zip = new ZipOutputStream(mem)) + using (ZipArchive zip = new ZipArchive(mem, ZipArchiveMode.Create)) { - if (!string.IsNullOrEmpty(options.jars[i].Comment)) - zip.SetComment(options.jars[i].Comment); - - zip.SetLevel(9); - var stubs = new List(); foreach (Jar.Item item in options.jars[i]) { @@ -759,31 +753,30 @@ private void WriteResources() continue; } - var zipEntry = item.ZipEntry; - if (options.compressedResources || zipEntry.CompressionMethod != CompressionMethod.Stored) - zipEntry.CompressionMethod = CompressionMethod.Deflated; + var zipEntry = zip.CreateEntry(item.Name, options.compressedResources ? CompressionLevel.Optimal : CompressionLevel.NoCompression); - zip.PutNextEntry(zipEntry); byte[] data = item.GetData(); - zip.Write(data, 0, data.Length); - zip.CloseEntry(); + + using Stream stream = zipEntry.Open(); + stream.Write(data, 0, data.Length); + hasEntries = true; } if (stubs.Count != 0) { // generate the --ikvm-classes-- file in the jar - ZipEntry zipEntry = new ZipEntry(JVM.JarClassList); - zipEntry.CompressionMethod = CompressionMethod.Deflated; - zip.PutNextEntry(zipEntry); - BinaryWriter bw = new BinaryWriter(zip); + ZipArchiveEntry zipEntry = zip.CreateEntry(JVM.JarClassList); + + using Stream stream = zipEntry.Open(); + using BinaryWriter bw = new BinaryWriter(stream); + bw.Write(stubs.Count); foreach (string classFile in stubs) { bw.Write(classFile); } - bw.Flush(); - zip.CloseEntry(); + hasEntries = true; } } @@ -802,32 +795,32 @@ private void WriteResources() } } - private static MethodAttributes MapMethodAccessModifiers(IKVM.Internal.MapXml.MapModifiers mod) + private static MethodAttributes MapMethodAccessModifiers(IKVM.Tools.Importer.MapXml.MapModifiers mod) { - const IKVM.Internal.MapXml.MapModifiers access = IKVM.Internal.MapXml.MapModifiers.Public | IKVM.Internal.MapXml.MapModifiers.Protected | IKVM.Internal.MapXml.MapModifiers.Private; + const IKVM.Tools.Importer.MapXml.MapModifiers access = IKVM.Tools.Importer.MapXml.MapModifiers.Public | IKVM.Tools.Importer.MapXml.MapModifiers.Protected | IKVM.Tools.Importer.MapXml.MapModifiers.Private; switch (mod & access) { - case IKVM.Internal.MapXml.MapModifiers.Public: + case IKVM.Tools.Importer.MapXml.MapModifiers.Public: return MethodAttributes.Public; - case IKVM.Internal.MapXml.MapModifiers.Protected: + case IKVM.Tools.Importer.MapXml.MapModifiers.Protected: return MethodAttributes.FamORAssem; - case IKVM.Internal.MapXml.MapModifiers.Private: + case IKVM.Tools.Importer.MapXml.MapModifiers.Private: return MethodAttributes.Private; default: return MethodAttributes.Assembly; } } - private static FieldAttributes MapFieldAccessModifiers(IKVM.Internal.MapXml.MapModifiers mod) + private static FieldAttributes MapFieldAccessModifiers(IKVM.Tools.Importer.MapXml.MapModifiers mod) { - const IKVM.Internal.MapXml.MapModifiers access = IKVM.Internal.MapXml.MapModifiers.Public | IKVM.Internal.MapXml.MapModifiers.Protected | IKVM.Internal.MapXml.MapModifiers.Private; + const IKVM.Tools.Importer.MapXml.MapModifiers access = IKVM.Tools.Importer.MapXml.MapModifiers.Public | IKVM.Tools.Importer.MapXml.MapModifiers.Protected | IKVM.Tools.Importer.MapXml.MapModifiers.Private; switch (mod & access) { - case IKVM.Internal.MapXml.MapModifiers.Public: + case IKVM.Tools.Importer.MapXml.MapModifiers.Public: return FieldAttributes.Public; - case IKVM.Internal.MapXml.MapModifiers.Protected: + case IKVM.Tools.Importer.MapXml.MapModifiers.Protected: return FieldAttributes.FamORAssem; - case IKVM.Internal.MapXml.MapModifiers.Private: + case IKVM.Tools.Importer.MapXml.MapModifiers.Private: return FieldAttributes.Private; default: return FieldAttributes.Assembly; @@ -840,7 +833,7 @@ private sealed class RemapperTypeWrapper : TypeWrapper private TypeBuilder typeBuilder; private TypeBuilder helperTypeBuilder; private Type shadowType; - private IKVM.Internal.MapXml.Class classDef; + private IKVM.Tools.Importer.MapXml.Class classDef; private TypeWrapper baseTypeWrapper; private TypeWrapper[] interfaceWrappers; @@ -857,9 +850,9 @@ internal override bool IsRemapped } } - private static TypeWrapper GetBaseWrapper(IKVM.Internal.MapXml.Class c) + private static TypeWrapper GetBaseWrapper(IKVM.Tools.Importer.MapXml.Class c) { - if ((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Interface) != 0) + if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Interface) != 0) { return null; } @@ -870,7 +863,7 @@ private static TypeWrapper GetBaseWrapper(IKVM.Internal.MapXml.Class c) return CoreClasses.java.lang.Object.Wrapper; } - internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapXml.Class c, IKVM.Internal.MapXml.Root map) + internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Tools.Importer.MapXml.Class c, IKVM.Tools.Importer.MapXml.Root map) : base(TypeFlags.None, (Modifiers)c.Modifiers, c.Name) { this.classLoader = classLoader; @@ -886,7 +879,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX baseInterface = baseType; } TypeAttributes attrs = TypeAttributes.Public; - if ((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Interface) == 0) + if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Interface) == 0) { attrs |= TypeAttributes.Class; if (baseType.IsSealed) @@ -900,7 +893,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX attrs |= TypeAttributes.Interface | TypeAttributes.Abstract; baseType = null; } - if ((c.Modifiers & IKVM.Internal.MapXml.MapModifiers.Abstract) != 0) + if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Abstract) != 0) { attrs |= TypeAttributes.Abstract; } @@ -908,7 +901,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX typeBuilder = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(name, attrs, baseIsSealed ? Types.Object : baseType); if (c.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in c.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in c.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, typeBuilder, custattr); } @@ -927,7 +920,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX AttributeHelper.SetModifiers(typeBuilder, (Modifiers)c.Modifiers, false); } - if (c.scope == IKVM.Internal.MapXml.Scope.Public) + if (c.Scope == MapXml.Scope.Public) { // FXBUG we would like to emit an attribute with a Type argument here, but that doesn't work because // of a bug in SetCustomAttribute that causes type arguments to be serialized incorrectly (if the type @@ -942,7 +935,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX if (c.Constructors != null) { - foreach (IKVM.Internal.MapXml.Constructor m in c.Constructors) + foreach (IKVM.Tools.Importer.MapXml.Constructor m in c.Constructors) { methods.Add(new RemappedConstructorWrapper(this, m)); } @@ -950,7 +943,7 @@ internal RemapperTypeWrapper(CompilerClassLoader classLoader, IKVM.Internal.MapX if (c.Methods != null) { - foreach (IKVM.Internal.MapXml.Method m in c.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method m in c.Methods) { methods.Add(new RemappedMethodWrapper(this, m, map, false)); } @@ -979,14 +972,14 @@ internal sealed override TypeWrapper BaseTypeWrapper get { return baseTypeWrapper; } } - internal void LoadInterfaces(IKVM.Internal.MapXml.Class c) + internal void LoadInterfaces(IKVM.Tools.Importer.MapXml.Class c) { if (c.Interfaces != null) { interfaceWrappers = new TypeWrapper[c.Interfaces.Length]; for (int i = 0; i < c.Interfaces.Length; i++) { - TypeWrapper iface = classLoader.LoadClassByDottedName(c.Interfaces[i].Name); + TypeWrapper iface = classLoader.LoadClassByDottedName(c.Interfaces[i].Class); interfaceWrappers[i] = iface; foreach (MethodWrapper mw in iface.GetMethods()) { @@ -1033,10 +1026,10 @@ internal RemappedMethodBaseWrapper(RemapperTypeWrapper typeWrapper, string name, sealed class RemappedConstructorWrapper : RemappedMethodBaseWrapper { - private IKVM.Internal.MapXml.Constructor m; + private IKVM.Tools.Importer.MapXml.Constructor m; private MethodBuilder mbHelper; - internal RemappedConstructorWrapper(RemapperTypeWrapper typeWrapper, IKVM.Internal.MapXml.Constructor m) + internal RemappedConstructorWrapper(RemapperTypeWrapper typeWrapper, IKVM.Tools.Importer.MapXml.Constructor m) : base(typeWrapper, "", m.Sig, (Modifiers)m.Modifiers) { this.m = m; @@ -1072,28 +1065,28 @@ internal override MethodBase DoLink() mbHelper = typeWrapper.typeBuilder.DefineMethod("newhelper", attr | MethodAttributes.Static, CallingConventions.Standard, typeWrapper.shadowType, paramTypes); if (m.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes) { AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbHelper, custattr); } } - SetParameters(DeclaringType.GetClassLoader(), mbHelper, m.Params); + SetParameters(DeclaringType.GetClassLoader(), mbHelper, m.Parameters); AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false); AttributeHelper.SetNameSig(mbHelper, "", m.Sig); - AddDeclaredExceptions(mbHelper, m.throws); + AddDeclaredExceptions(mbHelper, m.Throws); } else { cbCore = ReflectUtil.DefineConstructor(typeWrapper.typeBuilder, attr, paramTypes); if (m.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes) { AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), cbCore, custattr); } } - SetParameters(DeclaringType.GetClassLoader(), cbCore, m.Params); - AddDeclaredExceptions(cbCore, m.throws); + SetParameters(DeclaringType.GetClassLoader(), cbCore, m.Parameters); + AddDeclaredExceptions(cbCore, m.Throws); } return cbCore; } @@ -1110,10 +1103,10 @@ internal override void Finish() { CodeEmitter ilgen = CodeEmitter.Create(cbCore); // TODO we need to support ghost (and other funky?) parameter types - if (m.body != null) + if (m.Body != null) { // TODO do we need return type conversion here? - m.body.Emit(DeclaringType.GetClassLoader(), ilgen); + m.Body.Emit(DeclaringType.GetClassLoader(), ilgen); } else { @@ -1122,7 +1115,7 @@ internal override void Finish() { ilgen.EmitLdarg(i + 1); } - if (m.redirect != null) + if (m.Redirect != null) { throw new NotImplementedException(); } @@ -1148,15 +1141,15 @@ internal override void Finish() if (mbHelper != null) { CodeEmitter ilgen = CodeEmitter.Create(mbHelper); - if (m.redirect != null) + if (m.Redirect != null) { - m.redirect.Emit(DeclaringType.GetClassLoader(), ilgen); + m.Redirect.Emit(DeclaringType.GetClassLoader(), ilgen); } - else if (m.alternateBody != null) + else if (m.AlternateBody != null) { - m.alternateBody.Emit(DeclaringType.GetClassLoader(), ilgen); + m.AlternateBody.Emit(DeclaringType.GetClassLoader(), ilgen); } - else if (m.body != null) + else if (m.Body != null) { // doesn't make sense for helper constructors (which are actually factory methods) throw new InvalidOperationException(); @@ -1187,13 +1180,13 @@ internal override void Finish() sealed class RemappedMethodWrapper : RemappedMethodBaseWrapper { - private IKVM.Internal.MapXml.Method m; - private IKVM.Internal.MapXml.Root map; + private IKVM.Tools.Importer.MapXml.Method m; + private IKVM.Tools.Importer.MapXml.Root map; private MethodBuilder mbHelper; private List overriders = new List(); private bool inherited; - internal RemappedMethodWrapper(RemapperTypeWrapper typeWrapper, IKVM.Internal.MapXml.Method m, IKVM.Internal.MapXml.Root map, bool inherited) + internal RemappedMethodWrapper(RemapperTypeWrapper typeWrapper, IKVM.Tools.Importer.MapXml.Method m, IKVM.Tools.Importer.MapXml.Root map, bool inherited) : base(typeWrapper, m.Name, m.Sig, (Modifiers)m.Modifiers) { this.m = m; @@ -1201,7 +1194,7 @@ internal RemappedMethodWrapper(RemapperTypeWrapper typeWrapper, IKVM.Internal.Ma this.inherited = inherited; } - internal IKVM.Internal.MapXml.Method XmlMethod + internal IKVM.Tools.Importer.MapXml.Method XmlMethod { get { @@ -1247,29 +1240,29 @@ internal override MethodBase DoLink() if (typeWrapper.IsInterface) { - if (m.@override == null) + if (m.Override == null) { throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig); } - MethodInfo interfaceMethod = typeWrapper.shadowType.GetMethod(m.@override.Name, typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig)); + MethodInfo interfaceMethod = typeWrapper.shadowType.GetMethod(m.Override.Name, typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig)); if (interfaceMethod == null) { throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig); } // if any of the remapped types has a body for this interface method, we need a helper method // to special invocation through this interface for that type - List specialCases = null; - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + List specialCases = null; + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Methods != null) { - foreach (IKVM.Internal.MapXml.Method mm in c.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method mm in c.Methods) { - if (mm.Name == m.Name && mm.Sig == m.Sig && mm.body != null) + if (mm.Name == m.Name && mm.Sig == m.Sig && mm.Body != null) { if (specialCases == null) { - specialCases = new List(); + specialCases = new List(); } specialCases.Add(c); break; @@ -1278,19 +1271,19 @@ internal override MethodBase DoLink() } } string[] throws; - if (m.throws == null) + if (m.Throws == null) { throws = new string[0]; } else { - throws = new string[m.throws.Length]; + throws = new string[m.Throws.Length]; for (int i = 0; i < throws.Length; i++) { - throws[i] = m.throws[i].Class; + throws[i] = m.Throws[i].Class; } } - AttributeHelper.SetRemappedInterfaceMethod(typeWrapper.typeBuilder, m.Name, m.@override.Name, throws); + AttributeHelper.SetRemappedInterfaceMethod(typeWrapper.typeBuilder, m.Name, m.Override.Name, throws); MethodBuilder helper = null; if (specialCases != null) { @@ -1304,14 +1297,14 @@ internal override MethodBase DoLink() helper = typeWrapper.helperTypeBuilder.DefineMethod(m.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, typeWrapper.GetClassLoader().RetTypeWrapperFromSig(m.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType, argTypes); if (m.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes) { AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), helper, custattr); } } - SetParameters(DeclaringType.GetClassLoader(), helper, m.Params); + SetParameters(DeclaringType.GetClassLoader(), helper, m.Parameters); ilgen = CodeEmitter.Create(helper); - foreach (IKVM.Internal.MapXml.Class c in specialCases) + foreach (IKVM.Tools.Importer.MapXml.Class c in specialCases) { TypeWrapper tw = typeWrapper.GetClassLoader().LoadClassByDottedName(c.Name); ilgen.Emit(OpCodes.Ldarg_0); @@ -1347,19 +1340,19 @@ internal override MethodBase DoLink() Type[] paramTypes = typeWrapper.GetClassLoader().ArgTypeListFromSig(m.Sig); Type retType = typeWrapper.GetClassLoader().RetTypeWrapperFromSig(m.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType; - if (typeWrapper.shadowType.IsSealed && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0) + if (typeWrapper.shadowType.IsSealed && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0) { // skip instance methods in sealed types, but we do need to add them to the overriders - if (typeWrapper.BaseTypeWrapper != null && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0) + if (typeWrapper.BaseTypeWrapper != null && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0) { RemappedMethodWrapper baseMethod = typeWrapper.BaseTypeWrapper.GetMethodWrapper(m.Name, m.Sig, true) as RemappedMethodWrapper; if (baseMethod != null && !baseMethod.IsFinal && !baseMethod.IsPrivate && - (baseMethod.m.@override != null || - baseMethod.m.redirect != null || - baseMethod.m.body != null || - baseMethod.m.alternateBody != null)) + (baseMethod.m.Override != null || + baseMethod.m.Redirect != null || + baseMethod.m.Body != null || + baseMethod.m.AlternateBody != null)) { baseMethod.overriders.Add(typeWrapper); } @@ -1369,11 +1362,11 @@ internal override MethodBase DoLink() { MethodInfo overrideMethod = null; MethodAttributes attr = m.MethodAttributes | MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig; - if ((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) != 0) + if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) != 0) { attr |= MethodAttributes.Static; } - else if ((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) == 0) + else if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) == 0) { attr |= MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride; if (!typeWrapper.shadowType.IsSealed) @@ -1391,9 +1384,9 @@ internal override MethodBase DoLink() if (baseMethod != null) { baseMethod.overriders.Add(typeWrapper); - if (baseMethod.m.@override != null) + if (baseMethod.m.Override != null) { - overrideMethod = typeWrapper.BaseTypeWrapper.TypeAsTBD.GetMethod(baseMethod.m.@override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null); + overrideMethod = typeWrapper.BaseTypeWrapper.TypeAsTBD.GetMethod(baseMethod.m.Override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null); if (overrideMethod == null) { throw new InvalidOperationException(); @@ -1405,12 +1398,12 @@ internal override MethodBase DoLink() mbCore = GetDefineMethodHelper().DefineMethod(DeclaringType.GetClassLoader().GetTypeWrapperFactory(), typeWrapper.typeBuilder, m.Name, attr); if (m.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes) { AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbCore, custattr); } } - SetParameters(DeclaringType.GetClassLoader(), mbCore, m.Params); + SetParameters(DeclaringType.GetClassLoader(), mbCore, m.Parameters); if (overrideMethod != null && !inherited) { typeWrapper.typeBuilder.DefineMethodOverride(mbCore, overrideMethod); @@ -1419,16 +1412,16 @@ internal override MethodBase DoLink() { AttributeHelper.HideFromReflection(mbCore); } - AddDeclaredExceptions(mbCore, m.throws); + AddDeclaredExceptions(mbCore, m.Throws); } - if ((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0 && !IsHideFromJava(m)) + if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0 && !IsHideFromJava(m)) { // instance methods must have an instancehelper method MethodAttributes attr = MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig | MethodAttributes.Static; // NOTE instancehelpers for protected methods are made internal // and special cased in DotNetTypeWrapper.LazyPublishMembers - if ((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Protected) != 0) + if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Protected) != 0) { attr &= ~MethodAttributes.MemberAccessMask; attr |= MethodAttributes.Assembly; @@ -1436,22 +1429,22 @@ internal override MethodBase DoLink() mbHelper = typeWrapper.typeBuilder.DefineMethod("instancehelper_" + m.Name, attr, CallingConventions.Standard, retType, ArrayUtil.Concat(typeWrapper.shadowType, paramTypes)); if (m.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes) { AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mbHelper, custattr); } } - IKVM.Internal.MapXml.Param[] parameters; - if (m.Params == null) + IKVM.Tools.Importer.MapXml.Parameter[] parameters; + if (m.Parameters == null) { - parameters = new IKVM.Internal.MapXml.Param[1]; + parameters = new IKVM.Tools.Importer.MapXml.Parameter[1]; } else { - parameters = new IKVM.Internal.MapXml.Param[m.Params.Length + 1]; - m.Params.CopyTo(parameters, 1); + parameters = new IKVM.Tools.Importer.MapXml.Parameter[m.Parameters.Length + 1]; + m.Parameters.CopyTo(parameters, 1); } - parameters[0] = new IKVM.Internal.MapXml.Param(); + parameters[0] = new IKVM.Tools.Importer.MapXml.Parameter(); parameters[0].Name = "this"; SetParameters(DeclaringType.GetClassLoader(), mbHelper, parameters); if (!typeWrapper.IsFinal) @@ -1460,14 +1453,14 @@ internal override MethodBase DoLink() } AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false); AttributeHelper.SetNameSig(mbHelper, m.Name, m.Sig); - AddDeclaredExceptions(mbHelper, m.throws); + AddDeclaredExceptions(mbHelper, m.Throws); mbHelper.SetCustomAttribute(new CustomAttributeBuilder(JVM.Import(typeof(ObsoleteAttribute)).GetConstructor(new Type[] { Types.String }), new object[] { "This function will be removed from future versions. Please use extension methods from ikvm.extensions namespace instead." })); } return mbCore; } } - private static bool IsHideFromJava(IKVM.Internal.MapXml.Method m) + private static bool IsHideFromJava(IKVM.Tools.Importer.MapXml.Method m) { if (m.Attributes != null) { @@ -1494,9 +1487,9 @@ internal override void Finish() { CodeEmitter ilgen = CodeEmitter.Create(mbCore); MethodInfo baseMethod = null; - if (m.@override != null) + if (m.Override != null) { - baseMethod = DeclaringType.TypeAsTBD.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null); + baseMethod = DeclaringType.TypeAsTBD.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null); if (baseMethod == null) { throw new InvalidOperationException(); @@ -1504,13 +1497,13 @@ internal override void Finish() ((TypeBuilder)DeclaringType.TypeAsBaseType).DefineMethodOverride(mbCore, baseMethod); } // TODO we need to support ghost (and other funky?) parameter types - if (m.body != null) + if (m.Body != null) { // we manually walk the instruction list, because we need to special case the ret instructions - IKVM.Internal.MapXml.CodeGenContext context = new IKVM.Internal.MapXml.CodeGenContext(DeclaringType.GetClassLoader()); - foreach (IKVM.Internal.MapXml.Instruction instr in m.body.invoke) + IKVM.Tools.Importer.MapXml.CodeGenContext context = new IKVM.Tools.Importer.MapXml.CodeGenContext(DeclaringType.GetClassLoader()); + foreach (IKVM.Tools.Importer.MapXml.Instruction instr in m.Body.Instructions) { - if (instr is IKVM.Internal.MapXml.Ret) + if (instr is IKVM.Tools.Importer.MapXml.Ret) { this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null); } @@ -1519,12 +1512,11 @@ internal override void Finish() } else { - if (m.redirect != null && m.redirect.LineNumber != -1) - { - ilgen.SetLineNumber((ushort)m.redirect.LineNumber); - } + if (m.Redirect != null && m.Redirect.LineNumber != -1) + ilgen.SetLineNumber((ushort)m.Redirect.LineNumber); + int thisOffset = 0; - if ((m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) == 0) + if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0) { thisOffset = 1; ilgen.Emit(OpCodes.Ldarg_0); @@ -1533,7 +1525,7 @@ internal override void Finish() { ilgen.EmitLdarg(i + thisOffset); } - if (m.redirect != null) + if (m.Redirect != null) { EmitRedirect(DeclaringType.TypeAsTBD, ilgen); } @@ -1563,7 +1555,7 @@ internal override void Finish() { CodeEmitter ilgen = CodeEmitter.Create(mbHelper); // check "this" for null - if (m.@override != null && m.redirect == null && m.body == null && m.alternateBody == null) + if (m.Override != null && m.Redirect == null && m.Body == null && m.AlternateBody == null) { // we're going to be calling the overridden version, so we don't need the null check } @@ -1573,8 +1565,8 @@ internal override void Finish() ilgen.EmitNullCheck(); } if (mbCore != null && - (m.@override == null || m.redirect != null) && - (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) == 0) + (m.Override == null || m.Redirect != null) && + (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) == 0) { // TODO we should have a way to supress this for overridden methods ilgen.Emit(OpCodes.Ldarg_0); @@ -1595,7 +1587,7 @@ internal override void Finish() foreach (RemapperTypeWrapper overrider in overriders) { RemappedMethodWrapper mw = (RemappedMethodWrapper)overrider.GetMethodWrapper(Name, Signature, false); - if (mw.m.redirect == null && mw.m.body == null && mw.m.alternateBody == null) + if (mw.m.Redirect == null && mw.m.Body == null && mw.m.AlternateBody == null) { // the overridden method doesn't actually do anything special (that means it will end // up calling the .NET method it overrides), so we don't need to special case this @@ -1619,14 +1611,14 @@ internal override void Finish() ilgen.Emit(OpCodes.Pop); } } - if (m.body != null || m.alternateBody != null) + if (m.Body != null || m.AlternateBody != null) { - IKVM.Internal.MapXml.InstructionList body = m.alternateBody == null ? m.body : m.alternateBody; + IKVM.Tools.Importer.MapXml.InstructionList body = m.AlternateBody == null ? m.Body : m.AlternateBody; // we manually walk the instruction list, because we need to special case the ret instructions - IKVM.Internal.MapXml.CodeGenContext context = new IKVM.Internal.MapXml.CodeGenContext(DeclaringType.GetClassLoader()); - foreach (IKVM.Internal.MapXml.Instruction instr in body.invoke) + IKVM.Tools.Importer.MapXml.CodeGenContext context = new IKVM.Tools.Importer.MapXml.CodeGenContext(DeclaringType.GetClassLoader()); + foreach (IKVM.Tools.Importer.MapXml.Instruction instr in body.Instructions) { - if (instr is IKVM.Internal.MapXml.Ret) + if (instr is IKVM.Tools.Importer.MapXml.Ret) { this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null); } @@ -1635,96 +1627,92 @@ internal override void Finish() } else { - if (m.redirect != null && m.redirect.LineNumber != -1) + if (m.Redirect != null && m.Redirect.LineNumber != -1) { - ilgen.SetLineNumber((ushort)m.redirect.LineNumber); + ilgen.SetLineNumber((ushort)m.Redirect.LineNumber); } - Type shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType; + + var shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType; for (int i = 0; i < paramTypes.Length + 1; i++) - { ilgen.EmitLdarg(i); - } - if (m.redirect != null) + + if (m.Redirect != null) { EmitRedirect(shadowType, ilgen); } - else if (m.@override != null) + else if (m.Override != null) { - MethodInfo baseMethod = shadowType.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); + var baseMethod = shadowType.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); if (baseMethod == null) - { throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig); - } + ilgen.Emit(OpCodes.Callvirt, baseMethod); } else { - RemappedMethodWrapper baseMethod = DeclaringType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true) as RemappedMethodWrapper; - if (baseMethod == null || baseMethod.m.@override == null) - { + var baseMethod = DeclaringType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true) as RemappedMethodWrapper; + if (baseMethod == null || baseMethod.m.Override == null) throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig); - } - MethodInfo overrideMethod = shadowType.GetMethod(baseMethod.m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); + + var overrideMethod = shadowType.GetMethod(baseMethod.m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); if (overrideMethod == null) - { throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig); - } + ilgen.Emit(OpCodes.Callvirt, overrideMethod); } - this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null); + + ReturnType.EmitConvStackTypeToSignatureType(ilgen, null); ilgen.Emit(OpCodes.Ret); } + ilgen.DoEmit(); - if (this.DeclaringType.GetClassLoader().EmitStackTraceInfo) - { + + if (DeclaringType.GetClassLoader().EmitStackTraceInfo) ilgen.EmitLineNumberTable(mbHelper); - } } // do we need a helper for non-virtual reflection invocation? - if (m.nonvirtualAlternateBody != null || (m.@override != null && overriders.Count > 0)) + if (m.NonVirtualAlternateBody != null || (m.Override != null && overriders.Count > 0)) { - RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType; - MethodBuilder mb = typeWrapper.typeBuilder.DefineMethod("nonvirtualhelper/" + this.Name, MethodAttributes.Private | MethodAttributes.Static, - ReturnTypeForDefineMethod, ArrayUtil.Concat(typeWrapper.TypeAsSignatureType, GetParametersForDefineMethod())); + var tw = (RemapperTypeWrapper)DeclaringType; + var mb = tw.typeBuilder.DefineMethod("nonvirtualhelper/" + Name, MethodAttributes.Private | MethodAttributes.Static, ReturnTypeForDefineMethod, ArrayUtil.Concat(tw.TypeAsSignatureType, GetParametersForDefineMethod())); + + // apply custom attributes from map XML if (m.Attributes != null) - { - foreach (IKVM.Internal.MapXml.Attribute custattr in m.Attributes) - { + foreach (var custattr in m.Attributes) AttributeHelper.SetCustomAttribute(DeclaringType.GetClassLoader(), mb, custattr); - } - } - SetParameters(DeclaringType.GetClassLoader(), mb, m.Params); + + SetParameters(DeclaringType.GetClassLoader(), mb, m.Parameters); AttributeHelper.HideFromJava(mb); - CodeEmitter ilgen = CodeEmitter.Create(mb); - if (m.nonvirtualAlternateBody != null) + + var ilgen = CodeEmitter.Create(mb); + if (m.NonVirtualAlternateBody != null) { - m.nonvirtualAlternateBody.Emit(DeclaringType.GetClassLoader(), ilgen); + m.NonVirtualAlternateBody.Emit(DeclaringType.GetClassLoader(), ilgen); } else { - Type shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType; - MethodInfo baseMethod = shadowType.GetMethod(m.@override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); + var shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType; + var baseMethod = shadowType.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null); if (baseMethod == null) - { throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig); - } + ilgen.Emit(OpCodes.Ldarg_0); for (int i = 0; i < paramTypes.Length; i++) - { ilgen.EmitLdarg(i + 1); - } + ilgen.Emit(OpCodes.Call, baseMethod); ilgen.Emit(OpCodes.Ret); } + ilgen.DoEmit(); } } private void EmitRedirect(Type baseType, CodeEmitter ilgen) { - string redirName = m.redirect.Name; - string redirSig = m.redirect.Sig; + string redirName = m.Redirect.Name; + string redirSig = m.Redirect.Sig; if (redirName == null) { redirName = m.Name; @@ -1735,12 +1723,12 @@ private void EmitRedirect(Type baseType, CodeEmitter ilgen) } ClassLoaderWrapper classLoader = DeclaringType.GetClassLoader(); // HACK if the class name contains a comma, we assume it is a .NET type - if (m.redirect.Class == null || m.redirect.Class.IndexOf(',') >= 0) + if (m.Redirect.Class == null || m.Redirect.Class.IndexOf(',') >= 0) { // TODO better error handling - Type type = m.redirect.Class == null ? baseType : StaticCompiler.Universe.GetType(m.redirect.Class, true); + Type type = m.Redirect.Class == null ? baseType : StaticCompiler.Universe.GetType(m.Redirect.Class, true); Type[] redirParamTypes = classLoader.ArgTypeListFromSig(redirSig); - MethodInfo mi = type.GetMethod(m.redirect.Name, redirParamTypes); + MethodInfo mi = type.GetMethod(m.Redirect.Name, redirParamTypes); if (mi == null) { throw new InvalidOperationException(); @@ -1749,7 +1737,7 @@ private void EmitRedirect(Type baseType, CodeEmitter ilgen) } else { - TypeWrapper tw = classLoader.LoadClassByDottedName(m.redirect.Class); + TypeWrapper tw = classLoader.LoadClassByDottedName(m.Redirect.Class); MethodWrapper mw = tw.GetMethodWrapper(redirName, redirSig, false); if (mw == null) { @@ -1761,7 +1749,7 @@ private void EmitRedirect(Type baseType, CodeEmitter ilgen) } } - private static void SetParameters(ClassLoaderWrapper loader, MethodBuilder mb, IKVM.Internal.MapXml.Param[] parameters) + private static void SetParameters(ClassLoaderWrapper loader, MethodBuilder mb, IKVM.Tools.Importer.MapXml.Parameter[] parameters) { if (parameters != null) { @@ -1791,9 +1779,9 @@ internal void Process2ndPassStep1() AttributeHelper.SetImplementsAttribute(typeBuilder, interfaceWrappers); } - internal void Process2ndPassStep2(IKVM.Internal.MapXml.Root map) + internal void Process2ndPassStep2(IKVM.Tools.Importer.MapXml.Root map) { - IKVM.Internal.MapXml.Class c = classDef; + IKVM.Tools.Importer.MapXml.Class c = classDef; TypeBuilder tb = typeBuilder; List fields = new List(); @@ -1801,7 +1789,7 @@ internal void Process2ndPassStep2(IKVM.Internal.MapXml.Root map) // TODO fields should be moved to the RemapperTypeWrapper constructor as well if (c.Fields != null) { - foreach (IKVM.Internal.MapXml.Field f in c.Fields) + foreach (IKVM.Tools.Importer.MapXml.Field f in c.Fields) { { FieldAttributes attr = MapFieldAccessModifiers(f.Modifiers); @@ -1809,18 +1797,18 @@ internal void Process2ndPassStep2(IKVM.Internal.MapXml.Root map) { attr |= FieldAttributes.Literal; } - else if ((f.Modifiers & IKVM.Internal.MapXml.MapModifiers.Final) != 0) + else if ((f.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) != 0) { attr |= FieldAttributes.InitOnly; } - if ((f.Modifiers & IKVM.Internal.MapXml.MapModifiers.Static) != 0) + if ((f.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) != 0) { attr |= FieldAttributes.Static; } FieldBuilder fb = tb.DefineField(f.Name, GetClassLoader().FieldTypeWrapperFromSig(f.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType, attr); if (f.Attributes != null) { - foreach (IKVM.Internal.MapXml.Attribute custattr in f.Attributes) + foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in f.Attributes) { AttributeHelper.SetCustomAttribute(classLoader, fb, custattr); } @@ -1870,7 +1858,7 @@ internal void Process4thPass(ICollection remappedTypes) MethodBuilder cb = ReflectUtil.DefineTypeInitializer(typeBuilder, classLoader); CodeEmitter ilgen = CodeEmitter.Create(cb); // TODO emit code to make sure super class is initialized - classDef.Clinit.body.Emit(classLoader, ilgen); + classDef.Clinit.Body.Emit(classLoader, ilgen); ilgen.DoEmit(); } @@ -1878,9 +1866,9 @@ internal void Process4thPass(ICollection remappedTypes) // we need to explicitly finish the interface we implement (if they are ghosts, we need the nested __Interface type) if (classDef.Interfaces != null) { - foreach (IKVM.Internal.MapXml.Interface iface in classDef.Interfaces) + foreach (IKVM.Tools.Importer.MapXml.Implements iface in classDef.Interfaces) { - GetClassLoader().LoadClassByDottedName(iface.Name).Finish(); + GetClassLoader().LoadClassByDottedName(iface.Class).Finish(); } } @@ -1915,7 +1903,6 @@ internal void Process4thPass(ICollection remappedTypes) MethodBuilder mb = typeBuilder.DefineMethod(mi.Name, mi.Attributes & (MethodAttributes.MemberAccessMask | MethodAttributes.SpecialName | MethodAttributes.Static), mi.ReturnType, paramTypes); AttributeHelper.HideFromJava(mb); AttributeHelper.SetEditorBrowsableNever(mb); - CopyLinkDemands(mb, mi); CodeEmitter ilgen = CodeEmitter.Create(mb); for (int i = 0; i < paramTypes.Length; i++) { @@ -1963,17 +1950,6 @@ internal void Process4thPass(ICollection remappedTypes) } } - private static void CopyLinkDemands(MethodBuilder mb, MethodInfo mi) - { - foreach (CustomAttributeData cad in CustomAttributeData.__GetDeclarativeSecurity(mi)) - { - if (cad.ConstructorArguments.Count == 0 || (int)cad.ConstructorArguments[0].Value == (int)SecurityAction.LinkDemand) - { - mb.__AddDeclarativeSecurity(cad.__ToBuilder()); - } - } - } - private static string MakeMethodKey(MethodInfo method) { StringBuilder sb = new StringBuilder(); @@ -2138,7 +2114,7 @@ internal override bool IsFastClassLiteralSafe } } - internal static void AddDeclaredExceptions(MethodBuilder mb, IKVM.Internal.MapXml.Throws[] throws) + internal static void AddDeclaredExceptions(MethodBuilder mb, IKVM.Tools.Importer.MapXml.Throws[] throws) { if (throws != null) { @@ -2155,13 +2131,13 @@ internal void EmitRemappedTypes() { Tracer.Info(Tracer.Compiler, "Emit remapped types"); - assemblyAttributes = map.assembly.Attributes; + assemblyAttributes = map.Assembly.Attributes; - if (map.assembly.Classes != null) + if (map.Assembly.Classes != null) { // 1st pass, put all types in remapped to make them loadable bool hasRemappedTypes = false; - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Shadows != null) { @@ -2177,7 +2153,7 @@ internal void EmitRemappedTypes() if (hasRemappedTypes) { SetupGhosts(map); - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Shadows != null) { @@ -2190,10 +2166,10 @@ internal void EmitRemappedTypes() internal void EmitRemappedTypes2ndPass() { - if (map != null && map.assembly != null && map.assembly.Classes != null) + if (map != null && map.Assembly != null && map.Assembly.Classes != null) { // 2nd pass, resolve interfaces, publish methods/fields - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Shadows != null) { @@ -2201,7 +2177,7 @@ internal void EmitRemappedTypes2ndPass() typeWrapper.Process2ndPassStep1(); } } - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Shadows != null) { @@ -2215,113 +2191,102 @@ internal void EmitRemappedTypes2ndPass() internal bool IsMapUnsafeException(TypeWrapper tw) { if (mappedExceptions != null) - { for (int i = 0; i < mappedExceptions.Length; i++) - { - if (mappedExceptions[i].IsSubTypeOf(tw) || - (mappedExceptionsAllSubClasses[i] && tw.IsSubTypeOf(mappedExceptions[i]))) - { + if (mappedExceptions[i].IsSubTypeOf(tw) || (mappedExceptionsAllSubClasses[i] && tw.IsSubTypeOf(mappedExceptions[i]))) return true; - } - } - } + return false; } - internal void LoadMappedExceptions(IKVM.Internal.MapXml.Root map) + internal void LoadMappedExceptions(MapXml.Root map) { - if (map.exceptionMappings != null) + if (map.ExceptionMappings.Length > 0) { - mappedExceptionsAllSubClasses = new bool[map.exceptionMappings.Length]; - mappedExceptions = new TypeWrapper[map.exceptionMappings.Length]; + mappedExceptionsAllSubClasses = new bool[map.ExceptionMappings.Length]; + mappedExceptions = new TypeWrapper[map.ExceptionMappings.Length]; for (int i = 0; i < mappedExceptions.Length; i++) { - string dst = map.exceptionMappings[i].dst; + var dst = map.ExceptionMappings[i].Destination; if (dst[0] == '*') { mappedExceptionsAllSubClasses[i] = true; dst = dst.Substring(1); } + mappedExceptions[i] = LoadClassByDottedName(dst); } + // HACK we need to find the element and bind it - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) - { - if (c.Methods != null) - { - foreach (IKVM.Internal.MapXml.Method m in c.Methods) - { - if (m.body != null && m.body.invoke != null) - { - foreach (IKVM.Internal.MapXml.Instruction instr in m.body.invoke) - { - IKVM.Internal.MapXml.EmitExceptionMapping eem = instr as IKVM.Internal.MapXml.EmitExceptionMapping; - if (eem != null) - { - eem.mapping = map.exceptionMappings; - } - } - } - } - } - } + foreach (var c in map.Assembly.Classes) + foreach (var m in c.Methods) + if (m.Body != null) + foreach (var instr in m.Body.Instructions) + if (instr is MapXml.EmitExceptionMapping eem) + eem.mapping = map.ExceptionMappings; } } internal sealed class ExceptionMapEmitter { - private IKVM.Internal.MapXml.ExceptionMapping[] map; - internal ExceptionMapEmitter(IKVM.Internal.MapXml.ExceptionMapping[] map) + readonly MapXml.ExceptionMapping[] map; + + /// + /// Initializes a new instance. + /// + /// + internal ExceptionMapEmitter(MapXml.ExceptionMapping[] map) { this.map = map; } - internal void Emit(IKVM.Internal.MapXml.CodeGenContext context, CodeEmitter ilgen) + internal void Emit(MapXml.CodeGenContext context, CodeEmitter ilgen) { - MethodWrapper mwSuppressFillInStackTrace = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "()V", false); + var mwSuppressFillInStackTrace = CoreClasses.java.lang.Throwable.Wrapper.GetMethodWrapper("__", "()V", false); mwSuppressFillInStackTrace.Link(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); + for (int i = 0; i < map.Length; i++) { ilgen.Emit(OpCodes.Dup); - ilgen.Emit(OpCodes.Ldtoken, StaticCompiler.Universe.GetType(map[i].src, true)); + ilgen.Emit(OpCodes.Ldtoken, StaticCompiler.Universe.GetType(map[i].Source, true)); ilgen.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod); ilgen.Emit(OpCodes.Ceq); - CodeEmitterLabel label = ilgen.DefineLabel(); + var label = ilgen.DefineLabel(); ilgen.EmitBrfalse(label); ilgen.Emit(OpCodes.Pop); - if (map[i].code != null) + if (map[i].Code != null) { ilgen.Emit(OpCodes.Ldarg_0); - if (map[i].code.invoke != null) + + if (map[i].Code.Instructions.Length > 0) { - foreach (MapXml.Instruction instr in map[i].code.invoke) + foreach (var instr in map[i].Code.Instructions) { - MapXml.NewObj newobj = instr as MapXml.NewObj; - if (newobj != null - && newobj.Class != null - && context.ClassLoader.LoadClassByDottedName(newobj.Class).IsSubTypeOf(CoreClasses.java.lang.Throwable.Wrapper)) - { + var newobj = instr as MapXml.NewObj; + if (newobj != null && newobj.Class != null && context.ClassLoader.LoadClassByDottedName(newobj.Class).IsSubTypeOf(CoreClasses.java.lang.Throwable.Wrapper)) mwSuppressFillInStackTrace.EmitCall(ilgen); - } + instr.Generate(context, ilgen); } } + ilgen.Emit(OpCodes.Ret); } else { - TypeWrapper tw = context.ClassLoader.LoadClassByDottedName(map[i].dst); - MethodWrapper mw = tw.GetMethodWrapper("", "()V", false); + var tw = context.ClassLoader.LoadClassByDottedName(map[i].Destination); + var mw = tw.GetMethodWrapper("", "()V", false); mw.Link(); mwSuppressFillInStackTrace.EmitCall(ilgen); mw.EmitNewobj(ilgen); ilgen.Emit(OpCodes.Ret); } + ilgen.MarkLabel(label); } + ilgen.Emit(OpCodes.Pop); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Ret); @@ -2330,13 +2295,14 @@ internal void Emit(IKVM.Internal.MapXml.CodeGenContext context, CodeEmitter ilge internal void LoadMapXml() { - if (map.assembly.Classes != null) + if (map.Assembly.Classes.Length > 0) { - mapxml_Classes = new Dictionary(); - mapxml_MethodBodies = new Dictionary(); - mapxml_ReplacedMethods = new Dictionary(); - mapxml_MethodPrologues = new Dictionary(); - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + mapxml_Classes = new Dictionary(); + mapxml_MethodBodies = new Dictionary(); + mapxml_ReplacedMethods = new Dictionary(); + mapxml_MethodPrologues = new Dictionary(); + + foreach (var c in map.Assembly.Classes) { // if it is not a remapped type, it must be a container for native, patched or augmented methods if (c.Shadows == null) @@ -2346,102 +2312,80 @@ internal void LoadMapXml() AddMapXmlMethods(className, c.Constructors); AddMapXmlMethods(className, c.Methods); if (c.Clinit != null) - { AddMapXmlMethod(className, c.Clinit); - } } } } } - private void AddMapXmlMethods(string className, IKVM.Internal.MapXml.MethodBase[] methods) + private void AddMapXmlMethods(string className, MapXml.MethodBase[] methods) { if (methods != null) - { - foreach (IKVM.Internal.MapXml.MethodBase method in methods) - { + foreach (var method in methods) AddMapXmlMethod(className, method); - } - } } - private void AddMapXmlMethod(string className, IKVM.Internal.MapXml.MethodBase method) + private void AddMapXmlMethod(string className, MapXml.MethodBase method) { - if (method.body != null) - { - mapxml_MethodBodies.Add(method.ToMethodKey(className), method.body); - } - if (method.ReplaceMethodCalls != null) - { + if (method.Body != null) + mapxml_MethodBodies.Add(method.ToMethodKey(className), method.Body); + + if (method.ReplaceMethodCalls.Length > 0) mapxml_ReplacedMethods.Add(method.ToMethodKey(className), method.ReplaceMethodCalls); - } - if (method.prologue != null) - { - mapxml_MethodPrologues.Add(method.ToMethodKey(className), method.prologue); - } + + if (method.Prologue != null) + mapxml_MethodPrologues.Add(method.ToMethodKey(className), method.Prologue); } - internal IKVM.Internal.MapXml.InstructionList GetMethodPrologue(MethodKey method) + internal MapXml.InstructionList GetMethodPrologue(MethodKey method) { if (mapxml_MethodPrologues == null) - { return null; - } - IKVM.Internal.MapXml.InstructionList prologue; - mapxml_MethodPrologues.TryGetValue(method, out prologue); + + mapxml_MethodPrologues.TryGetValue(method, out var prologue); return prologue; } - internal IKVM.Internal.MapXml.ReplaceMethodCall[] GetReplacedMethodsFor(MethodWrapper mw) + internal MapXml.ReplaceMethodCall[] GetReplacedMethodsFor(MethodWrapper mw) { if (mapxml_ReplacedMethods == null) - { return null; - } - IKVM.Internal.MapXml.ReplaceMethodCall[] rmc; - mapxml_ReplacedMethods.TryGetValue(new MethodKey(mw.DeclaringType.Name, mw.Name, mw.Signature), out rmc); + + mapxml_ReplacedMethods.TryGetValue(new MethodKey(mw.DeclaringType.Name, mw.Name, mw.Signature), out var rmc); return rmc; } - internal Dictionary GetMapXmlClasses() + internal Dictionary GetMapXmlClasses() { return mapxml_Classes; } - internal Dictionary GetMapXmlMethodBodies() + internal Dictionary GetMapXmlMethodBodies() { return mapxml_MethodBodies; } - internal IKVM.Internal.MapXml.Param[] GetXmlMapParameters(string classname, string method, string sig) + internal MapXml.Parameter[] GetXmlMapParameters(string classname, string method, string sig) { if (mapxml_Classes != null) { - IKVM.Internal.MapXml.Class clazz; - if (mapxml_Classes.TryGetValue(classname, out clazz)) + if (mapxml_Classes.TryGetValue(classname, out var clazz)) { - if (method == "" && clazz.Constructors != null) + if (method == "" && clazz.Constructors.Length > 0) { for (int i = 0; i < clazz.Constructors.Length; i++) - { if (clazz.Constructors[i].Sig == sig) - { - return clazz.Constructors[i].Params; - } - } + return clazz.Constructors[i].Parameters; } - else if (clazz.Methods != null) + else if (clazz.Methods.Length > 0) { for (int i = 0; i < clazz.Methods.Length; i++) - { if (clazz.Methods[i].Name == method && clazz.Methods[i].Sig == sig) - { - return clazz.Methods[i].Params; - } - } + return clazz.Methods[i].Parameters; } } } + return null; } @@ -2450,53 +2394,48 @@ internal bool IsGhost(TypeWrapper tw) return ghosts != null && tw.IsInterface && ghosts.ContainsKey(tw.Name); } - private void SetupGhosts(IKVM.Internal.MapXml.Root map) + void SetupGhosts(MapXml.Root map) { ghosts = new Dictionary>(); // find the ghost interfaces - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (var c in map.Assembly.Classes) { - if (c.Shadows != null && c.Interfaces != null) + if (c.Shadows != null && c.Interfaces.Length > 0) { // NOTE we don't support interfaces that inherit from other interfaces // (actually, if they are explicitly listed it would probably work) - TypeWrapper typeWrapper = FindLoadedClass(c.Name); - foreach (IKVM.Internal.MapXml.Interface iface in c.Interfaces) + var typeWrapper = FindLoadedClass(c.Name); + + foreach (var iface in c.Interfaces) { - TypeWrapper ifaceWrapper = FindLoadedClass(iface.Name); + var ifaceWrapper = FindLoadedClass(iface.Class); if (ifaceWrapper == null || !ifaceWrapper.TypeAsTBD.IsAssignableFrom(typeWrapper.TypeAsTBD)) - { - AddGhost(iface.Name, typeWrapper); - } + AddGhost(iface.Class, typeWrapper); } } } + // we manually add the array ghost interfaces - TypeWrapper array = ClassLoaderWrapper.GetWrapperFromType(Types.Array); + var array = ClassLoaderWrapper.GetWrapperFromType(Types.Array); AddGhost("java.io.Serializable", array); AddGhost("java.lang.Cloneable", array); } private void AddGhost(string interfaceName, TypeWrapper implementer) { - List list; - if (!ghosts.TryGetValue(interfaceName, out list)) + if (!ghosts.TryGetValue(interfaceName, out var list)) { list = new List(); ghosts[interfaceName] = list; } + list.Add(implementer); } internal TypeWrapper[] GetGhostImplementers(TypeWrapper wrapper) { - List list; - if (!ghosts.TryGetValue(wrapper.Name, out list)) - { - return TypeWrapper.EmptyArray; - } - return list.ToArray(); + return ghosts.TryGetValue(wrapper.Name, out var list) ? list.ToArray() : TypeWrapper.EmptyArray; } internal void FinishRemappedTypes() @@ -2504,23 +2443,16 @@ internal void FinishRemappedTypes() // 3rd pass, link the methods. Note that a side effect of the linking is the // twiddling with the overriders array in the base methods, so we need to do this // as a separate pass before we compile the methods - foreach (RemapperTypeWrapper typeWrapper in remapped.Values) - { + foreach (var typeWrapper in remapped.Values) typeWrapper.Process3rdPass(); - } + // 4th pass, implement methods/fields and bake the type - foreach (RemapperTypeWrapper typeWrapper in remapped.Values) - { + foreach (var typeWrapper in remapped.Values) typeWrapper.Process4thPass(remapped.Values); - } if (assemblyAttributes != null) - { - foreach (IKVM.Internal.MapXml.Attribute attr in assemblyAttributes) - { + foreach (MapXml.Attribute attr in assemblyAttributes) AttributeHelper.SetCustomAttribute(this, assemblyBuilder, attr); - } - } } private static bool IsSigned(Assembly asm) @@ -2536,16 +2468,11 @@ internal static bool IsCoreAssembly(Assembly asm) private bool CheckCompilingCoreAssembly() { - if (map != null && map.assembly != null && map.assembly.Classes != null) - { - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) - { + if (map != null && map.Assembly != null && map.Assembly.Classes != null) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) if (c.Shadows != null && c.Name == "java.lang.Object") - { return compilingCoreAssembly = true; - } - } - } + return false; } @@ -2737,8 +2664,8 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad { try { - byte[] buf = assemblyType.GetData(); - ClassFile f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None, null); + var f = new ClassFile(ClassReader.Read(assemblyType.GetData()), null, ClassFileParseOptions.None, null); + // NOTE the "assembly" type in the unnamed package is a magic type // that acts as the placeholder for assembly attributes if (f.Name == "assembly" && f.Annotations != null) @@ -2750,7 +2677,10 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad StaticCompiler.IssueMessage(Message.LegacyAssemblyAttributesFound); } } - catch (ClassFormatError) { } + catch (ClassFormatError) + { + + } } } @@ -2761,11 +2691,10 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad { try { - byte[] buf = h[className].GetData(); - ClassFile f = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None, null); + var f = new ClassFile(ClassReader.Read(h[className].GetData()), null, ClassFileParseOptions.None, null); if (f.Name == className) { - foreach (ClassFile.Method m in f.Methods) + foreach (var m in f.Methods) { if (m.IsPublic && m.IsStatic && m.Name == "main" && m.Signature == "([Ljava.lang.String;)V") { @@ -2776,7 +2705,9 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad } } } - catch (ClassFormatError) { } + catch (ClassFormatError) + { + } } break_outer:; } @@ -2786,11 +2717,6 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad options.target = PEFileKinds.Dll; } - if (options.target == PEFileKinds.Dll && options.mainClass != null) - { - throw new FatalCompilerErrorException(Message.MainClassRequiresExe); - } - if (options.target != PEFileKinds.Dll && options.mainClass == null) { throw new FatalCompilerErrorException(Message.ExeRequiresMainClass); @@ -2807,16 +2733,16 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad { if (options.targetIsModule) { - options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".netmodule"); + options.path = IkvmImporterInternal.GetFileInfo(options.assembly + ".netmodule"); } else { - options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".dll"); + options.path = IkvmImporterInternal.GetFileInfo(options.assembly + ".dll"); } } else { - options.path = IkvmcCompiler.GetFileInfo(options.assembly + ".exe"); + options.path = IkvmImporterInternal.GetFileInfo(options.assembly + ".exe"); } StaticCompiler.IssueMessage(Message.OutputFileIs, options.path.ToString()); } @@ -2847,9 +2773,7 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad if (options.remapfile != null) { Tracer.Info(Tracer.Compiler, "Loading remapped types (1) from {0}", options.remapfile); - System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(IKVM.Internal.MapXml.Root)); - ser.UnknownElement += new System.Xml.Serialization.XmlElementEventHandler(ser_UnknownElement); - ser.UnknownAttribute += new System.Xml.Serialization.XmlAttributeEventHandler(ser_UnknownAttribute); + FileStream fs; try { @@ -2857,27 +2781,26 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad // simultaneously on this file while we are reading it. fs = new FileStream(options.remapfile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } - catch (Exception x) + catch (Exception e) { - throw new FatalCompilerErrorException(Message.ErrorReadingFile, options.remapfile, x.Message); + throw new FatalCompilerErrorException(Message.ErrorReadingFile, options.remapfile, e.Message); } + try { - XmlTextReader rdr = new XmlTextReader(fs); - IKVM.Internal.MapXml.Root.xmlReader = rdr; - IKVM.Internal.MapXml.Root map; + MapXml.Root map; + try { - map = (IKVM.Internal.MapXml.Root)ser.Deserialize(rdr); + map = new MapXml.MapXmlSerializer().Read(XDocument.Load(fs, LoadOptions.SetLineInfo)); } - catch (InvalidOperationException x) + catch (MapXml.MapXmlException x) { throw new FatalCompilerErrorException(Message.ErrorParsingMapFile, options.remapfile, x.Message); } - if (!loader.ValidateAndSetMap(map)) - { + + if (loader.ValidateAndSetMap(map) == false) return 1; - } } finally { @@ -2893,7 +2816,7 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad // If we do not yet have a reference to the core assembly and we are not compiling the core assembly, // try to find the core assembly by looking at the assemblies that the runtime references - if (JVM.CoreAssembly == null && !compilingCoreAssembly) + if (JVM.BaseAssembly == null && !compilingCoreAssembly) { foreach (AssemblyName name in StaticCompiler.runtimeAssembly.GetReferencedAssemblies()) { @@ -2910,12 +2833,12 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad if (asm != null && IsCoreAssembly(asm)) { AssemblyClassLoader.PreloadExportedAssemblies(asm); - JVM.CoreAssembly = asm; + JVM.BaseAssembly = asm; break; } } - if (JVM.CoreAssembly == null) + if (JVM.BaseAssembly == null) { throw new FatalCompilerErrorException(Message.BootstrapClassesMissing); } @@ -2926,8 +2849,8 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad if (!compilingCoreAssembly) { - allReferencesAreStrongNamed &= IsSigned(JVM.CoreAssembly); - loader.AddReference(AssemblyClassLoader.FromAssembly(JVM.CoreAssembly)); + allReferencesAreStrongNamed &= IsSigned(JVM.BaseAssembly); + loader.AddReference(AssemblyClassLoader.FromAssembly(JVM.BaseAssembly)); } if ((options.keyPair != null || options.publicKey != null) && !allReferencesAreStrongNamed) @@ -2942,7 +2865,7 @@ private static int CreateCompiler(CompilerOptions options, ref CompilerClassLoad if (!compilingCoreAssembly) { - FakeTypes.Load(JVM.CoreAssembly); + FakeTypes.Load(JVM.BaseAssembly); } return 0; } @@ -3107,62 +3030,62 @@ private int CompilePass3() } } } + if (options.classLoader != null) { - TypeWrapper wrapper = null; + TypeWrapper classLoaderType = null; try { - wrapper = LoadClassByDottedNameFast(options.classLoader); + classLoaderType = LoadClassByDottedNameFast(options.classLoader); } catch (RetargetableJavaException) { + } - if (wrapper == null) - { + + if (classLoaderType == null) throw new FatalCompilerErrorException(Message.ClassLoaderNotFound); - } - if (!wrapper.IsPublic && !ReflectUtil.IsFromAssembly(wrapper.TypeAsBaseType, assemblyBuilder)) - { + + if (classLoaderType.IsPublic == false && ReflectUtil.IsFromAssembly(classLoaderType.TypeAsBaseType, assemblyBuilder) == false) throw new FatalCompilerErrorException(Message.ClassLoaderNotAccessible); - } - if (wrapper.IsAbstract) - { + + if (classLoaderType.IsAbstract) throw new FatalCompilerErrorException(Message.ClassLoaderIsAbstract); - } - if (!wrapper.IsAssignableTo(ClassLoaderWrapper.LoadClassCritical("java.lang.ClassLoader"))) - { + + if (classLoaderType.IsAssignableTo(ClassLoaderWrapper.LoadClassCritical("java.lang.ClassLoader")) == false) throw new FatalCompilerErrorException(Message.ClassLoaderNotClassLoader); - } - MethodWrapper mw = wrapper.GetMethodWrapper("", "(Lcli.System.Reflection.Assembly;)V", false); - if (mw == null) - { + + var classLoaderInitMethod = classLoaderType.GetMethodWrapper("", "(Lcli.System.Reflection.Assembly;)V", false); + if (classLoaderInitMethod == null) throw new FatalCompilerErrorException(Message.ClassLoaderConstructorMissing); - } - ConstructorInfo ci = JVM.LoadType(typeof(CustomAssemblyClassLoaderAttribute)).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { Types.Type }, null); - assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ci, new object[] { wrapper.TypeAsTBD })); - // TODO it would be better to do this for all assemblies in a shared class loader group (because options.classloader is relevant only for the main assembly), - // but since it is probably common to specify the custom assembly class loader at the group level, it hopefully won't make much difference in practice. - MethodWrapper mwModuleInit = wrapper.GetMethodWrapper("InitializeModule", "(Lcli.System.Reflection.Module;)V", false); - if (mwModuleInit != null && !mwModuleInit.IsStatic) - { - MethodBuilder moduleInitializer = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes); - ILGenerator ilgen = moduleInitializer.GetILGenerator(); - ilgen.Emit(OpCodes.Ldtoken, moduleInitializer); - ilgen.Emit(OpCodes.Call, JVM.Import(typeof(System.Reflection.MethodBase)).GetMethod("GetMethodFromHandle", new Type[] { JVM.Import(typeof(RuntimeMethodHandle)) })); - ilgen.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Reflection.MemberInfo)).GetMethod("get_Module")); - ilgen.Emit(OpCodes.Call, StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule")); - ilgen.Emit(OpCodes.Ret); - } + + // apply custom attribute specifying custom class loader + var ci = JVM.LoadType(typeof(CustomAssemblyClassLoaderAttribute)).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { Types.Type }, null); + assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ci, new object[] { classLoaderType.TypeAsTBD })); } + if (options.iconfile != null) { - assemblyBuilder.__DefineIconResource(IkvmcCompiler.ReadAllBytes(options.iconfile)); + assemblyBuilder.__DefineIconResource(IkvmImporterInternal.ReadAllBytes(options.iconfile)); } + if (options.manifestFile != null) { - assemblyBuilder.__DefineManifestResource(IkvmcCompiler.ReadAllBytes(options.manifestFile)); + assemblyBuilder.__DefineManifestResource(IkvmImporterInternal.ReadAllBytes(options.manifestFile)); } + + // define a module initialization method + // this method invokes on module load and calls into the runtime + var moduleInit = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes); + var moduleInitIL = moduleInit.GetILGenerator(); + moduleInitIL.Emit(OpCodes.Ldtoken, moduleInit); + moduleInitIL.Emit(OpCodes.Call, JVM.Import(typeof(System.Reflection.MethodBase)).GetMethod("GetMethodFromHandle", new Type[] { JVM.Import(typeof(RuntimeMethodHandle)) })); + moduleInitIL.Emit(OpCodes.Callvirt, JVM.Import(typeof(System.Reflection.MemberInfo)).GetMethod("get_Module")); + moduleInitIL.Emit(OpCodes.Call, StaticCompiler.GetRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule")); + moduleInitIL.Emit(OpCodes.Ret); + assemblyBuilder.DefineVersionInfoResource(); + return 0; } @@ -3176,43 +3099,43 @@ private static void ser_UnknownAttribute(object sender, System.Xml.Serialization StaticCompiler.IssueMessage(Message.UnknownAttributeInMapFile, e.Attr.Name, e.LineNumber.ToString(), e.LinePosition.ToString()); } - private bool ValidateAndSetMap(IKVM.Internal.MapXml.Root map) + private bool ValidateAndSetMap(IKVM.Tools.Importer.MapXml.Root map) { bool valid = true; - if (map.assembly != null) + if (map.Assembly != null) { - if (map.assembly.Classes != null) + if (map.Assembly.Classes != null) { - foreach (IKVM.Internal.MapXml.Class c in map.assembly.Classes) + foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes) { if (c.Fields != null) { - foreach (IKVM.Internal.MapXml.Field f in c.Fields) + foreach (IKVM.Tools.Importer.MapXml.Field f in c.Fields) { ValidateNameSig("field", c.Name, f.Name, f.Sig, ref valid, true); } } if (c.Methods != null) { - foreach (IKVM.Internal.MapXml.Method m in c.Methods) + foreach (IKVM.Tools.Importer.MapXml.Method m in c.Methods) { ValidateNameSig("method", c.Name, m.Name, m.Sig, ref valid, false); } } if (c.Constructors != null) { - foreach (IKVM.Internal.MapXml.Constructor ctor in c.Constructors) + foreach (IKVM.Tools.Importer.MapXml.Constructor ctor in c.Constructors) { ValidateNameSig("constructor", c.Name, "", ctor.Sig, ref valid, false); } } if (c.Properties != null) { - foreach (IKVM.Internal.MapXml.Property prop in c.Properties) + foreach (IKVM.Tools.Importer.MapXml.Property prop in c.Properties) { ValidateNameSig("property", c.Name, prop.Name, prop.Sig, ref valid, false); - ValidatePropertyGetterSetter("getter", c.Name, prop.Name, prop.getter, ref valid); - ValidatePropertyGetterSetter("setter", c.Name, prop.Name, prop.setter, ref valid); + ValidatePropertyGetterSetter("getter", c.Name, prop.Name, prop.Getter, ref valid); + ValidatePropertyGetterSetter("setter", c.Name, prop.Name, prop.Setter, ref valid); } } } @@ -3236,7 +3159,7 @@ private static void ValidateNameSig(string member, string clazz, string name, st } } - private static void ValidatePropertyGetterSetter(string getterOrSetter, string clazz, string property, IKVM.Internal.MapXml.Method method, ref bool valid) + private static void ValidatePropertyGetterSetter(string getterOrSetter, string clazz, string property, IKVM.Tools.Importer.MapXml.Method method, ref bool valid) { if (method != null) { @@ -3321,44 +3244,34 @@ protected override void CheckProhibitedPackage(string className) sealed class Jar { internal readonly string Name; - internal readonly string Comment; private readonly List Items = new List(); - internal Jar(string name, string comment) + internal Jar(string name) { this.Name = name; - this.Comment = comment; } internal Jar Copy() { - Jar newJar = new Jar(Name, Comment); + Jar newJar = new Jar(Name); newJar.Items.AddRange(Items); return newJar; } - internal void Add(ZipEntry ze, byte[] data) - { - Items.Add(new JarItem(ze, data, null)); - } - - internal void Add(string name, byte[] data, FileInfo fileInfo) + internal void Add(string name, byte[] data, FileInfo fileInfo = null) { - ZipEntry zipEntry = new ZipEntry(name); - zipEntry.DateTime = new DateTime(1980, 01, 01, 0, 00, 0, DateTimeKind.Utc); - zipEntry.CompressionMethod = CompressionMethod.Stored; - Items.Add(new JarItem(zipEntry, data, fileInfo)); + Items.Add(new JarItem(name, data, fileInfo)); } - private struct JarItem + private readonly struct JarItem { - internal readonly ZipEntry zipEntry; + internal readonly string name; internal readonly byte[] data; internal readonly FileInfo path; // path of the original file, if it was individual file (used to construct source file path) - internal JarItem(ZipEntry zipEntry, byte[] data, FileInfo path) + internal JarItem(string name, byte[] data, FileInfo path) { - this.zipEntry = zipEntry; + this.name = name; this.data = data; this.path = path; } @@ -3377,7 +3290,7 @@ internal Item(Jar jar, int index) internal string Name { - get { return Jar.Items[Index].zipEntry.Name; } + get { return Jar.Items[Index].name; } } internal byte[] GetData() @@ -3390,19 +3303,6 @@ internal FileInfo Path get { return Jar.Items[Index].path; } } - internal ZipEntry ZipEntry - { - get - { - var org = Jar.Items[Index].zipEntry; - var zipEntry = new ZipEntry(org.Name); - zipEntry.Comment = org.Comment; - zipEntry.CompressionMethod = org.CompressionMethod; - zipEntry.DateTime = org.DateTime; - return zipEntry; - } - } - internal void Remove() { Jar.Items[Index] = new JarItem(); @@ -3410,7 +3310,7 @@ internal void Remove() internal void MarkAsStub() { - Jar.Items[Index] = new JarItem(Jar.Items[Index].zipEntry, null, null); + Jar.Items[Index] = new JarItem(Jar.Items[Index].name, null, null); } internal bool IsStub @@ -3437,12 +3337,10 @@ public Item Current public bool MoveNext() { - while (index + 1 < jar.Items.Count) + if (index + 1 < jar.Items.Count) { - if (jar.Items[++index].zipEntry != null) - { - return true; - } + index++; + return true; } return false; } @@ -3536,18 +3434,18 @@ private static List Copy(List jars) return newJars; } - internal Jar GetJar(ZipFile zf) + internal Jar GetJar(string file) { int existingJar; - if (jarMap.TryGetValue(zf.Name, out existingJar)) + if (jarMap.TryGetValue(file, out existingJar)) { return jars[existingJar]; } - jarMap.Add(zf.Name, jars.Count); - return CreateJar(Path.GetFileName(zf.Name), zf.ZipFileComment); + jarMap.Add(file, jars.Count); + return CreateJar(Path.GetFileName(file)); } - private Jar CreateJar(string jarName, string comment) + private Jar CreateJar(string jarName) { int count = 0; string name = jarName; @@ -3560,7 +3458,7 @@ private Jar CreateJar(string jarName, string comment) goto retry; } } - Jar newJar = new Jar(name, comment); + Jar newJar = new Jar(name); jars.Add(newJar); return newJar; } @@ -3570,7 +3468,7 @@ internal Jar GetClassesJar() if (classesJar == -1) { classesJar = jars.Count; - CreateJar("classes.jar", null); + CreateJar("classes.jar"); } return jars[classesJar]; } @@ -3585,7 +3483,7 @@ internal Jar GetResourcesJar() if (resourcesJar == -1) { resourcesJar = jars.Count; - CreateJar("resources.jar", null); + CreateJar("resources.jar"); } return jars[resourcesJar]; diff --git a/src/ikvmc/IKVM/Internal/FakeTypes.cs b/src/IKVM.Tools.Importer/FakeTypes.cs similarity index 98% rename from src/ikvmc/IKVM/Internal/FakeTypes.cs rename to src/IKVM.Tools.Importer/FakeTypes.cs index d2757e53c2..a3dec3f1a8 100644 --- a/src/ikvmc/IKVM/Internal/FakeTypes.cs +++ b/src/IKVM.Tools.Importer/FakeTypes.cs @@ -21,15 +21,18 @@ Jeroen Frijters jeroen@frijters.net */ +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal +namespace IKVM.Tools.Importer { - static class FakeTypes + + static class FakeTypes { + private static Type genericEnumEnumType; private static Type genericDelegateInterfaceType; private static Type genericAttributeAnnotationType; diff --git a/src/IKVM.Tools.Importer/FatalCompilerErrorException.cs b/src/IKVM.Tools.Importer/FatalCompilerErrorException.cs new file mode 100644 index 0000000000..fe3658bbad --- /dev/null +++ b/src/IKVM.Tools.Importer/FatalCompilerErrorException.cs @@ -0,0 +1,166 @@ +/* + Copyright (C) 2002-2014 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; + +namespace IKVM.Tools.Importer +{ + + sealed class FatalCompilerErrorException : Exception + { + + internal FatalCompilerErrorException(Message id, params object[] args) : + base($"fatal error IKVMC{(int)id}: {(args.Length == 0 ? GetMessage(id) : string.Format(GetMessage(id), args))}") + { + + } + + private static string GetMessage(Message id) + { + switch (id) + { + case IKVM.Tools.Importer.Message.ResponseFileDepthExceeded: + return "Response file nesting depth exceeded"; + case IKVM.Tools.Importer.Message.ErrorReadingFile: + return "Unable to read file: {0}\n\t({1})"; + case IKVM.Tools.Importer.Message.NoTargetsFound: + return "No targets found"; + case IKVM.Tools.Importer.Message.FileFormatLimitationExceeded: + return "File format limitation exceeded: {0}"; + case IKVM.Tools.Importer.Message.CannotSpecifyBothKeyFileAndContainer: + return "You cannot specify both a key file and container"; + case IKVM.Tools.Importer.Message.DelaySignRequiresKey: + return "You cannot delay sign without a key file or container"; + case IKVM.Tools.Importer.Message.InvalidStrongNameKeyPair: + return "Invalid key {0} specified.\n\t(\"{1}\")"; + case IKVM.Tools.Importer.Message.ReferenceNotFound: + return "Reference not found: {0}"; + case IKVM.Tools.Importer.Message.OptionsMustPreceedChildLevels: + return "You can only specify options before any child levels"; + case IKVM.Tools.Importer.Message.UnrecognizedTargetType: + return "Invalid value '{0}' for -target option"; + case IKVM.Tools.Importer.Message.UnrecognizedPlatform: + return "Invalid value '{0}' for -platform option"; + case IKVM.Tools.Importer.Message.UnrecognizedApartment: + return "Invalid value '{0}' for -apartment option"; + case IKVM.Tools.Importer.Message.MissingFileSpecification: + return "Missing file specification for '{0}' option"; + case IKVM.Tools.Importer.Message.PathTooLong: + return "Path too long: {0}"; + case IKVM.Tools.Importer.Message.PathNotFound: + return "Path not found: {0}"; + case IKVM.Tools.Importer.Message.InvalidPath: + return "Invalid path: {0}"; + case IKVM.Tools.Importer.Message.InvalidOptionSyntax: + return "Invalid option: {0}"; + case IKVM.Tools.Importer.Message.ExternalResourceNotFound: + return "External resource file does not exist: {0}"; + case IKVM.Tools.Importer.Message.ExternalResourceNameInvalid: + return "External resource file may not include path specification: {0}"; + case IKVM.Tools.Importer.Message.InvalidVersionFormat: + return "Invalid version specified: {0}"; + case IKVM.Tools.Importer.Message.InvalidFileAlignment: + return "Invalid value '{0}' for -filealign option"; + case IKVM.Tools.Importer.Message.ErrorWritingFile: + return "Unable to write file: {0}\n\t({1})"; + case IKVM.Tools.Importer.Message.UnrecognizedOption: + return "Unrecognized option: {0}"; + case IKVM.Tools.Importer.Message.NoOutputFileSpecified: + return "No output file specified"; + case IKVM.Tools.Importer.Message.SharedClassLoaderCannotBeUsedOnModuleTarget: + return "Incompatible options: -target:module and -sharedclassloader cannot be combined"; + case IKVM.Tools.Importer.Message.RuntimeNotFound: + return "Unable to load runtime assembly"; + case IKVM.Tools.Importer.Message.MainClassRequiresExe: + return "Main class cannot be specified for library or module"; + case IKVM.Tools.Importer.Message.ExeRequiresMainClass: + return "No main method found"; + case IKVM.Tools.Importer.Message.PropertiesRequireExe: + return "Properties cannot be specified for library or module"; + case IKVM.Tools.Importer.Message.ModuleCannotHaveClassLoader: + return "Cannot specify assembly class loader for modules"; + case IKVM.Tools.Importer.Message.ErrorParsingMapFile: + return "Unable to parse remap file: {0}\n\t({1})"; + case IKVM.Tools.Importer.Message.BootstrapClassesMissing: + return "Bootstrap classes missing and core assembly not found"; + case IKVM.Tools.Importer.Message.StrongNameRequiresStrongNamedRefs: + return "All referenced assemblies must be strong named, to be able to sign the output assembly"; + case IKVM.Tools.Importer.Message.MainClassNotFound: + return "Main class not found"; + case IKVM.Tools.Importer.Message.MainMethodNotFound: + return "Main method not found"; + case IKVM.Tools.Importer.Message.UnsupportedMainMethod: + return "Redirected main method not supported"; + case IKVM.Tools.Importer.Message.ExternalMainNotAccessible: + return "External main method must be public and in a public class"; + case IKVM.Tools.Importer.Message.ClassLoaderNotFound: + return "Custom assembly class loader class not found"; + case IKVM.Tools.Importer.Message.ClassLoaderNotAccessible: + return "Custom assembly class loader class is not accessible"; + case IKVM.Tools.Importer.Message.ClassLoaderIsAbstract: + return "Custom assembly class loader class is abstract"; + case IKVM.Tools.Importer.Message.ClassLoaderNotClassLoader: + return "Custom assembly class loader class does not extend java.lang.ClassLoader"; + case IKVM.Tools.Importer.Message.ClassLoaderConstructorMissing: + return "Custom assembly class loader constructor is missing"; + case IKVM.Tools.Importer.Message.MapFileTypeNotFound: + return "Type '{0}' referenced in remap file was not found"; + case IKVM.Tools.Importer.Message.MapFileClassNotFound: + return "Class '{0}' referenced in remap file was not found"; + case IKVM.Tools.Importer.Message.MaximumErrorCountReached: + return "Maximum error count reached"; + case IKVM.Tools.Importer.Message.LinkageError: + return "Link error: {0}"; + case IKVM.Tools.Importer.Message.RuntimeMismatch: + return "Referenced assembly {0} was compiled with an incompatible IKVM.Runtime version\n" + + "\tCurrent runtime: {1}\n" + + "\tReferenced assembly runtime: {2}"; + case IKVM.Tools.Importer.Message.CoreClassesMissing: + return "Failed to find core classes in core library"; + case IKVM.Tools.Importer.Message.CriticalClassNotFound: + return "Unable to load critical class '{0}'"; + case IKVM.Tools.Importer.Message.AssemblyContainsDuplicateClassNames: + return "Type '{0}' and '{1}' both map to the same name '{2}'\n\t({3})"; + case IKVM.Tools.Importer.Message.CallerIDRequiresHasCallerIDAnnotation: + return "CallerID.getCallerID() requires a HasCallerID annotation"; + case IKVM.Tools.Importer.Message.UnableToResolveInterface: + return "Unable to resolve interface '{0}' on type '{1}'"; + case IKVM.Tools.Importer.Message.MissingBaseType: + return "The base class or interface '{0}' in assembly '{1}' referenced by type '{2}' in '{3}' could not be resolved"; + case IKVM.Tools.Importer.Message.MissingBaseTypeReference: + return "The type '{0}' is defined in an assembly that is not referenced. You must add a reference to assembly '{1}'"; + case IKVM.Tools.Importer.Message.FileNotFound: + return "File not found: {0}"; + case IKVM.Tools.Importer.Message.RuntimeMethodMissing: + return "Runtime method '{0}' not found"; + case IKVM.Tools.Importer.Message.MapFileFieldNotFound: + return "Field '{0}' referenced in remap file was not found in class '{1}'"; + case IKVM.Tools.Importer.Message.GhostInterfaceMethodMissing: + return "Remapped class '{0}' does not implement ghost interface method\n\t({1}.{2}{3})"; + default: + return "Missing Error Message. Please file a bug."; + } + } + } + +} diff --git a/src/IKVM.Tools.Importer/IKVM.Tools.Importer.csproj b/src/IKVM.Tools.Importer/IKVM.Tools.Importer.csproj new file mode 100644 index 0000000000..100405fa65 --- /dev/null +++ b/src/IKVM.Tools.Importer/IKVM.Tools.Importer.csproj @@ -0,0 +1,41 @@ + + + + + net461;netcoreapp3.1 + $(DefineConstants);IMPORTER;EMITTERS + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/IKVM.Tools.Importer/IkvmImporterContext.NetCore.cs b/src/IKVM.Tools.Importer/IkvmImporterContext.NetCore.cs new file mode 100644 index 0000000000..a62c2a486c --- /dev/null +++ b/src/IKVM.Tools.Importer/IkvmImporterContext.NetCore.cs @@ -0,0 +1,126 @@ +#if NETCOREAPP + +using System; +using System.Reflection; +using System.Runtime.Loader; +using System.Threading; +using System.Threading.Tasks; + +using System.Text.Json; + +namespace IKVM.Tools.Importer +{ + + public partial class IkvmImporterContext + { + + /// + /// Invokes the static instance on the internal type. + /// + class IkvmImporterDispatcher + { + + readonly string[] args; + + /// + /// Initializes a new instance. + /// + /// + /// + public IkvmImporterDispatcher(string args) + { + if (args is null) + throw new ArgumentNullException(nameof(args)); + + this.args = JsonSerializer.Deserialize(args); + } + + /// + /// Executes the exporter. + /// + /// + public int Execute() + { + return IkvmImporterInternal.Execute(args); + } + + } + + /// + /// Manages a separate isolated copy of the assemblies. + /// + class IsolatedAssemblyLoadContext : AssemblyLoadContext + { + + readonly AssemblyDependencyResolver resolver; + + /// + /// Initializes a new instance. + /// + /// + /// + public IsolatedAssemblyLoadContext(string name, bool isCollectible = true) : + base(name, isCollectible) + { + resolver = new AssemblyDependencyResolver(Assembly.GetEntryAssembly().Location); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + return resolver.ResolveAssemblyToPath(assemblyName) is string p ? base.LoadFromAssemblyPath(p) : base.Load(assemblyName); + } + + } + + IsolatedAssemblyLoadContext context; + object dispatcher; + + /// + /// Initializes a new instance. + /// + /// + /// + public IkvmImporterContext(string[] args) + { + // load the importer in a nested assembly context + context = new IsolatedAssemblyLoadContext("IkvmImporter", true); + var asm = context.LoadFromAssemblyName(typeof(IkvmImporterDispatcher).Assembly.GetName()); + var typ = asm.GetType(typeof(IkvmImporterDispatcher).FullName); + dispatcher = Activator.CreateInstance(typ, new[] { JsonSerializer.Serialize(args) }); + } + + /// + /// Creates a new context for the execution. + /// + /// + /// + /// + public partial async Task ExecuteAsync(CancellationToken cancellationToken) + { + return await Task.Run(() => (int)dispatcher.GetType().GetMethod(nameof(IkvmImporterDispatcher.Execute), BindingFlags.Public | BindingFlags.Instance).Invoke(dispatcher, Array.Empty())); + } + + /// + /// Disposes of the instance. + /// + public void Dispose() + { + try + { + dispatcher = null; + if (context != null) + context.Unload(); + } + finally + { + context = null; + } + + GC.SuppressFinalize(this); + } + + } + +} + +#endif diff --git a/src/IKVM.Tools.Importer/IkvmImporterContext.NetFramework.cs b/src/IKVM.Tools.Importer/IkvmImporterContext.NetFramework.cs new file mode 100644 index 0000000000..4eb1c9a242 --- /dev/null +++ b/src/IKVM.Tools.Importer/IkvmImporterContext.NetFramework.cs @@ -0,0 +1,106 @@ +#if NETFRAMEWORK + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace IKVM.Tools.Importer +{ + + public partial class IkvmImporterContext + { + + /// + /// Encapsulates a in a remote . + /// + class IkvmImporterDispatcher : MarshalByRefObject + { + + readonly string[] args; + + /// + /// Initializes a new instance. + /// + /// + /// + public IkvmImporterDispatcher(string[] args) + { + this.args = args ?? throw new ArgumentNullException(nameof(args)); + } + + /// + /// Executes the exporter. + /// + /// + public int Execute() + { + return IkvmImporterInternal.Execute(args); + } + + } + + AppDomain appDomain; + IkvmImporterDispatcher dispatcher; + + /// + /// Initializes a new instance. + /// + /// + /// + public IkvmImporterContext(string[] args) + { + appDomain = AppDomain.CreateDomain( + "IkvmImporter", + AppDomain.CurrentDomain.Evidence, + new AppDomainSetup() + { + ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, + ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, + }); + + dispatcher = (IkvmImporterDispatcher)appDomain.CreateInstanceAndUnwrap( + typeof(IkvmImporterDispatcher).Assembly.GetName().FullName, + typeof(IkvmImporterDispatcher).FullName, + false, + System.Reflection.BindingFlags.Default, + null, + new[] { args }, + null, + null); + } + + /// + /// Creates a new context for the execution. + /// + /// + /// + /// + public partial async Task ExecuteAsync(CancellationToken cancellationToken) + { + return await Task.Run(dispatcher.Execute); + } + + /// + /// Disposes of the instance. + /// + public void Dispose() + { + try + { + dispatcher = null; + if (appDomain != null) + AppDomain.Unload(appDomain); + } + finally + { + appDomain = null; + } + + GC.SuppressFinalize(this); + } + + } + +} + +#endif diff --git a/src/IKVM.Tools.Importer/IkvmImporterContext.cs b/src/IKVM.Tools.Importer/IkvmImporterContext.cs new file mode 100644 index 0000000000..2e2b8fb079 --- /dev/null +++ b/src/IKVM.Tools.Importer/IkvmImporterContext.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace IKVM.Tools.Importer +{ + + /// + /// Imports a Java classpath into a .NET assembly. + /// + public partial class IkvmImporterContext : IDisposable + { + + /// + /// Executes the importer logic. + /// + /// + /// + public partial Task ExecuteAsync(CancellationToken cancellationToken); + + /// + /// Finalizes the instance. + /// + ~IkvmImporterContext() + { + Dispose(); + } + + } + +} diff --git a/src/ikvmc/IkvmcCompiler.cs b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs similarity index 94% rename from src/ikvmc/IkvmcCompiler.cs rename to src/IKVM.Tools.Importer/IkvmImporterInternal.cs index 7960b90d3e..af3e12a143 100644 --- a/src/ikvmc/IkvmcCompiler.cs +++ b/src/IKVM.Tools.Importer/IkvmImporterInternal.cs @@ -24,34 +24,35 @@ Jeroen Frijters using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Text.RegularExpressions; using System.Threading; -using ICSharpCode.SharpZipLib.Zip; - +using IKVM.ByteCode.Reading; using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Runtime; using Type = IKVM.Reflection.Type; -namespace ikvmc +namespace IKVM.Tools.Importer { - public sealed class IkvmcCompiler + sealed class IkvmImporterInternal { - private bool nonleaf; - private string manifestMainClass; - private string defaultAssemblyName; - private static bool time; - private static string runtimeAssembly; - private static bool nostdlib; - private static bool nonDeterministicOutput; - private static readonly List libpaths = new List(); + bool nonleaf; + string manifestMainClass; + string defaultAssemblyName; + static bool time; + static string runtimeAssembly; + static bool nostdlib; + static bool nonDeterministicOutput; + static readonly List libpaths = new List(); internal static readonly AssemblyResolver resolver = new AssemblyResolver(); - private static void AddArg(List arglist, string s, int depth) + static void AddArg(List arglist, string s, int depth) { if (s.StartsWith("@")) { @@ -89,22 +90,21 @@ private static void AddArg(List arglist, string s, int depth) } } - private static List GetArgs(string[] args) + static List GetArgs(string[] args) { - List arglist = new List(); + var arglist = new List(); foreach (string s in args) - { AddArg(arglist, s, 0); - } return arglist; } - public static int Main(string[] args) + public static int Execute(string[] args) { DateTime start = DateTime.Now; System.Threading.Thread.CurrentThread.Name = "compiler"; Tracer.EnableTraceConsoleListener(); Tracer.EnableTraceForDebug(); + try { try @@ -167,7 +167,8 @@ static int Compile(string[] args) { PrintHeader(); } - IkvmcCompiler comp = new IkvmcCompiler(); + + IkvmImporterInternal comp = new IkvmImporterInternal(); List targets = new List(); CompilerOptions toplevel = new CompilerOptions(); StaticCompiler.toplevel = toplevel; @@ -282,20 +283,20 @@ internal static byte[] ReadAllBytes(FileInfo path) static string GetVersionAndCopyrightInfo() { - var asm = typeof(IkvmcCompiler).Assembly; + var asm = typeof(IkvmImporterInternal).Assembly; var desc = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); var copy = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); var info = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(asm); return $"{desc.Title} ({info.InformationalVersion}){Environment.NewLine}{copy.Copyright}"; // TODO: Add domain once we get one {Environment.NewLine}http://www.ikvm.org/ } - private static void PrintHeader() + static void PrintHeader() { Console.Error.WriteLine(GetVersionAndCopyrightInfo()); Console.Error.WriteLine(); } - private static void PrintHelp() + static void PrintHelp() { PrintHeader(); Console.Error.WriteLine("Usage: ikvmc [-options] ... "); @@ -413,7 +414,7 @@ void ContinueParseCommandLine(IEnumerator arglist, List ReadFiles(options, fileNames); nonleaf = true; } - IkvmcCompiler nestedLevel = new IkvmcCompiler(); + IkvmImporterInternal nestedLevel = new IkvmImporterInternal(); nestedLevel.manifestMainClass = manifestMainClass; nestedLevel.defaultAssemblyName = defaultAssemblyName; nestedLevel.ContinueParseCommandLine(arglist, targets, options.Copy()); @@ -1081,7 +1082,7 @@ static void ResolveReferences(List targets) { throw new FatalCompilerErrorException(Message.ReferenceNotFound, reference); } - next_reference:; + next_reference:; } } } @@ -1150,37 +1151,39 @@ private static void ArrayAppend(ref T[] array, T[] append) } } - private static byte[] ReadFromZip(ZipFile zf, ZipEntry ze) + private static byte[] ReadFromZip(ZipArchiveEntry ze) { - byte[] buf = new byte[ze.Size]; - int pos = 0; - Stream s = zf.GetInputStream(ze); - while (pos < buf.Length) - { - pos += s.Read(buf, pos, buf.Length - pos); - } - return buf; + using MemoryStream ms = new MemoryStream(); + + using Stream s = ze.Open(); + + s.CopyTo(ms); + + return ms.ToArray(); } private static bool EmitStubWarning(CompilerOptions options, byte[] buf) { ClassFile cf; + try { - cf = new ClassFile(buf, 0, buf.Length, "", ClassFileParseOptions.None, null); + cf = new ClassFile(ClassReader.Read(buf), "", ClassFileParseOptions.None, null); } catch (ClassFormatError) { return false; } + if (cf.IKVMAssemblyAttribute == null) { return false; } + if (cf.IKVMAssemblyAttribute.StartsWith("[[")) { - Regex r = new Regex(@"\[([^\[\]]+)\]"); - MatchCollection mc = r.Matches(cf.IKVMAssemblyAttribute); + var r = new Regex(@"\[([^\[\]]+)\]"); + var mc = r.Matches(cf.IKVMAssemblyAttribute); foreach (Match m in mc) { options.legacyStubReferences[m.Groups[1].Value] = null; @@ -1192,10 +1195,11 @@ private static bool EmitStubWarning(CompilerOptions options, byte[] buf) options.legacyStubReferences[cf.IKVMAssemblyAttribute] = null; StaticCompiler.IssueMessage(options, Message.StubsAreDeprecated, cf.IKVMAssemblyAttribute); } + return true; } - private static bool IsExcludedOrStubLegacy(CompilerOptions options, ZipEntry ze, byte[] data) + private static bool IsExcludedOrStubLegacy(CompilerOptions options, ZipArchiveEntry ze, byte[] data) { if (ze.Name.EndsWith(".class", StringComparison.OrdinalIgnoreCase)) { @@ -1216,13 +1220,14 @@ private static bool IsExcludedOrStubLegacy(CompilerOptions options, ZipEntry ze, return false; } - private void ProcessManifest(CompilerOptions options, ZipFile zf, ZipEntry ze) + private void ProcessManifest(CompilerOptions options, ZipArchiveEntry ze) { if (manifestMainClass == null) { // read main class from manifest // TODO find out if we can use other information from manifest - StreamReader rdr = new StreamReader(zf.GetInputStream(ze)); + using Stream stream = ze.Open(); + using StreamReader rdr = new StreamReader(stream); string line; while ((line = rdr.ReadLine()) != null) { @@ -1242,53 +1247,47 @@ private void ProcessManifest(CompilerOptions options, ZipFile zf, ZipEntry ze) } } - private bool ProcessZipFile(CompilerOptions options, string file, Predicate filter) + private bool ProcessZipFile(CompilerOptions options, string file, Predicate filter) { try { - ZipFile zf = new ZipFile(file); - try + using ZipArchive zf = ZipFile.OpenRead(file); + + bool found = false; + Jar jar = null; + foreach (ZipArchiveEntry ze in zf.Entries) { - bool found = false; - Jar jar = null; - foreach (ZipEntry ze in zf) + if (filter != null && !filter(ze)) + { + // skip + } + else { - if (filter != null && !filter(ze)) + found = true; + byte[] data = ReadFromZip(ze); + if (IsExcludedOrStubLegacy(options, ze, data)) { - // skip + continue; } - else + if (jar == null) { - found = true; - byte[] data = ReadFromZip(zf, ze); - if (IsExcludedOrStubLegacy(options, ze, data)) - { - continue; - } - if (jar == null) - { - jar = options.GetJar(zf); - } - jar.Add(ze, data); - if (ze.Name == "META-INF/MANIFEST.MF") - { - ProcessManifest(options, zf, ze); - } + jar = options.GetJar(file); + } + jar.Add(ze.FullName, data); + if (string.Equals(ze.FullName, "META-INF/MANIFEST.MF", StringComparison.OrdinalIgnoreCase)) + { + ProcessManifest(options, ze); } } - // include empty zip file if it has a comment - if (!found && !string.IsNullOrEmpty(zf.ZipFileComment)) - { - options.GetJar(zf); - } - return found; } - finally + // include empty zip file + if (!found) { - zf.Close(); + options.GetJar(file); } + return found; } - catch (ICSharpCode.SharpZipLib.SharpZipBaseException x) + catch (InvalidDataException x) { throw new FatalCompilerErrorException(Message.ErrorReadingFile, file, x.Message); } @@ -1373,12 +1372,12 @@ private bool RecurseJar(CompilerOptions options, string path) { string pathFilter = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar; string fileFilter = "^" + Regex.Escape(Path.GetFileName(file)).Replace("\\*", ".*").Replace("\\?", ".") + "$"; - return ProcessZipFile(options, path, delegate (ZipEntry ze) + return ProcessZipFile(options, path, delegate (ZipArchiveEntry ze) { // MONOBUG Path.GetDirectoryName() doesn't normalize / to \ on Windows - string name = ze.Name.Replace('/', Path.DirectorySeparatorChar); + string name = ze.FullName.Replace('/', Path.DirectorySeparatorChar); return (Path.GetDirectoryName(name) + Path.DirectorySeparatorChar).StartsWith(pathFilter) - && Regex.IsMatch(Path.GetFileName(ze.Name), fileFilter); + && Regex.IsMatch(Path.GetFileName(ze.FullName), fileFilter); }); } } @@ -1389,7 +1388,7 @@ private static void ProcessExclusionFile(ref string[] classesToExclude, string f { try { - List list = classesToExclude == null ? new List() : new List(classesToExclude); + var list = classesToExclude == null ? new List() : new List(classesToExclude); using (StreamReader file = new StreamReader(filename)) { String line; @@ -1410,12 +1409,12 @@ private static void ProcessExclusionFile(ref string[] classesToExclude, string f } } - private static void ProcessAttributeAnnotationsClass(ref object[] annotations, string filename) + static void ProcessAttributeAnnotationsClass(ref object[] annotations, string filename) { try { - byte[] buf = File.ReadAllBytes(filename); - ClassFile cf = new ClassFile(buf, 0, buf.Length, null, ClassFileParseOptions.None, null); + using var file = File.OpenRead(filename); + var cf = new ClassFile(ClassReader.Read(file), null, ClassFileParseOptions.None, null); ArrayAppend(ref annotations, cf.Annotations); } catch (Exception x) diff --git a/src/IKVM.Tools.Importer/IkvmImporterTool.cs b/src/IKVM.Tools.Importer/IkvmImporterTool.cs new file mode 100644 index 0000000000..7b9de35292 --- /dev/null +++ b/src/IKVM.Tools.Importer/IkvmImporterTool.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace IKVM.Tools.Importer +{ + + /// + /// Main entry point for the application. + /// + public class IkvmImporterTool + { + + /// + /// Executes the importer. + /// + /// + /// + /// + public static async Task Main(string[] args, CancellationToken cancellationToken) + { + return await new IkvmImporterTool().ExecuteAsync(args, cancellationToken); + } + + /// + /// Executes the exporter. + /// + /// + /// + /// + async Task ExecuteAsync(string[] args, CancellationToken cancellationToken) + { + using var context = new IkvmImporterContext(args); + return await context.ExecuteAsync(cancellationToken); + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Add.cs b/src/IKVM.Tools.Importer/MapXml/Add.cs new file mode 100644 index 0000000000..8d16993d07 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Add.cs @@ -0,0 +1,68 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("add")] + public sealed class Add : Simple + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Add Read(XElement element) + { + var inst = new Add(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Add inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// + public Add() : base(OpCodes.Add) + { + + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/And.cs b/src/IKVM.Tools.Importer/MapXml/And.cs new file mode 100644 index 0000000000..e0b869ce61 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/And.cs @@ -0,0 +1,68 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("and")] + public sealed class And : Simple + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new And Read(XElement element) + { + var inst = new And(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(And inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// + public And() : base(OpCodes.And) + { + + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Assembly.cs b/src/IKVM.Tools.Importer/MapXml/Assembly.cs new file mode 100644 index 0000000000..c36a6b9212 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Assembly.cs @@ -0,0 +1,64 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Assembly : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Assembly Read(XElement element) + { + var assembly = new Assembly(); + Load(assembly, element); + return assembly; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Assembly assembly, XElement element) + { + Load((MapXmlElement)assembly, element); + assembly.Classes = element.Elements(MapXmlSerializer.NS + "class").Select(Class.Read).ToArray(); + assembly.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + } + + public Class[] Classes { get; set; } + + public Attribute[] Attributes { get; set; } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Attribute.cs b/src/IKVM.Tools.Importer/MapXml/Attribute.cs new file mode 100644 index 0000000000..7f31b853c3 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Attribute.cs @@ -0,0 +1,76 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Attribute : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Attribute Read(XElement element) + { + var attribute = new Attribute(); + Load(attribute, element); + return attribute; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(Attribute attribute, XElement element) + { + Load((MapXmlElement)attribute, element); + attribute.Type = (string)element.Attribute("type"); + attribute.Class = (string)element.Attribute("class"); + attribute.Sig = (string)element.Attribute("sig"); + attribute.Params = element.Elements(MapXmlSerializer.NS + "parameter").Select(Parameter.Read).ToArray(); + attribute.Properties = element.Elements(MapXmlSerializer.NS + "property").Select(Parameter.Read).ToArray(); + attribute.Fields = element.Elements(MapXmlSerializer.NS + "field").Select(Parameter.Read).ToArray(); + } + + public string Type { get; set; } + + public string Class { get; set; } + + public string Sig { get; set; } + + public Parameter[] Params { get; set; } + + public Parameter[] Properties { get; set; } + + public Parameter[] Fields { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Beq.cs b/src/IKVM.Tools.Importer/MapXml/Beq.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/Beq.cs rename to src/IKVM.Tools.Importer/MapXml/Beq.cs index 5fbd5fbd0f..7816cd31ec 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Beq.cs +++ b/src/IKVM.Tools.Importer/MapXml/Beq.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("beq")] + + [Instruction("beq")] public sealed class Beq : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Beq Read(XElement element) + { + var inst = new Beq(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Beq inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBeq(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Bge_Un.cs b/src/IKVM.Tools.Importer/MapXml/Bge_Un.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Bge_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Bge_Un.cs index c4b650ab23..566e250848 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Bge_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Bge_Un.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("bge_un")] + + [Instruction("bge_un")] public sealed class Bge_Un : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Bge_Un Read(XElement element) + { + var inst = new Bge_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Bge_Un inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBge_Un(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ble_Un.cs b/src/IKVM.Tools.Importer/MapXml/Ble_Un.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Ble_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Ble_Un.cs index 8357f29754..ed3e51be4b 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ble_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ble_Un.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ble_un")] + + [Instruction("ble_un")] public sealed class Ble_Un : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ble_Un Read(XElement element) + { + var inst = new Ble_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ble_Un inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBle_Un(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Blt.cs b/src/IKVM.Tools.Importer/MapXml/Blt.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/Blt.cs rename to src/IKVM.Tools.Importer/MapXml/Blt.cs index 137b60172c..d6001e822c 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Blt.cs +++ b/src/IKVM.Tools.Importer/MapXml/Blt.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("blt")] + + [Instruction("blt")] public sealed class Blt : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Blt Read(XElement element) + { + var inst = new Blt(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Blt inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBlt(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Blt_Un.cs b/src/IKVM.Tools.Importer/MapXml/Blt_Un.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Blt_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Blt_Un.cs index 55ad5fe28a..352120577a 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Blt_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Blt_Un.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("blt_un")] + + [Instruction("blt_un")] public sealed class Blt_Un : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Blt_Un Read(XElement element) + { + var inst = new Blt_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Blt_Un inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBlt_Un(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Bne_Un.cs b/src/IKVM.Tools.Importer/MapXml/Bne_Un.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Bne_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Bne_Un.cs index 2b2a47ac32..273321f758 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Bne_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Bne_Un.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("bne_un")] + + [Instruction("bne_un")] public sealed class Bne_Un : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Bne_Un Read(XElement element) + { + var inst = new Bne_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Bne_Un inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBne_Un(label); } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Box.cs b/src/IKVM.Tools.Importer/MapXml/Box.cs new file mode 100644 index 0000000000..989b8a28d3 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Box.cs @@ -0,0 +1,68 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("box")] + public sealed class Box : TypeInstruction + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Box Read(XElement element) + { + var inst = new Box(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Box inst, XElement element) + { + Load((TypeInstruction)inst, element); + } + + /// + /// Initializes a new insance. + /// + public Box() : base(OpCodes.Box) + { + + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Br.cs b/src/IKVM.Tools.Importer/MapXml/Br.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/Br.cs rename to src/IKVM.Tools.Importer/MapXml/Br.cs index 678d920031..fa302e2c21 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Br.cs +++ b/src/IKVM.Tools.Importer/MapXml/Br.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("br")] + + [Instruction("br")] public sealed class Br : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Br Read(XElement element) + { + var inst = new Br(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Br inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBr(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/BrFalse.cs b/src/IKVM.Tools.Importer/MapXml/BrFalse.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/BrFalse.cs rename to src/IKVM.Tools.Importer/MapXml/BrFalse.cs index 0f7ff74e5f..0f300a8645 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/BrFalse.cs +++ b/src/IKVM.Tools.Importer/MapXml/BrFalse.cs @@ -22,16 +22,45 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("brfalse")] + + [Instruction("brfalse")] public sealed class BrFalse : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new BrFalse Read(XElement element) + { + var inst = new BrFalse(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(BrFalse inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBrfalse(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/BrLabel.cs b/src/IKVM.Tools.Importer/MapXml/BrLabel.cs similarity index 61% rename from src/ikvmc/IKVM/Internal/MapXml/BrLabel.cs rename to src/IKVM.Tools.Importer/MapXml/BrLabel.cs index 668317f2a8..55de8432db 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/BrLabel.cs +++ b/src/IKVM.Tools.Importer/MapXml/BrLabel.cs @@ -22,15 +22,43 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("label")] + + [Instruction("label")] public sealed class BrLabel : Instruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new BrLabel Read(XElement element) + { + var inst = new BrLabel(); + Load(inst, element); + return inst; + } + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static void Load(BrLabel inst, XElement element) + { + Load((Instruction)inst, element); + inst.Name = (string)element.Attribute("name"); + } + [XmlAttribute("name")] - public string Name; + public string Name { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { @@ -44,7 +72,10 @@ internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { l = (CodeEmitterLabel)context[Name]; } + ilgen.MarkLabel(l); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/BrTrue.cs b/src/IKVM.Tools.Importer/MapXml/BrTrue.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/BrTrue.cs rename to src/IKVM.Tools.Importer/MapXml/BrTrue.cs index 1d07a724c8..7cfd344ac9 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/BrTrue.cs +++ b/src/IKVM.Tools.Importer/MapXml/BrTrue.cs @@ -22,16 +22,44 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("brtrue")] + + [Instruction("brtrue")] public sealed class BrTrue : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new BrTrue Read(XElement element) + { + var inst = new BrTrue(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(BrTrue inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitBrtrue(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Branch.cs b/src/IKVM.Tools.Importer/MapXml/Branch.cs similarity index 74% rename from src/ikvmc/IKVM/Internal/MapXml/Branch.cs rename to src/IKVM.Tools.Importer/MapXml/Branch.cs index 3519861205..ea0e8598e3 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Branch.cs +++ b/src/IKVM.Tools.Importer/MapXml/Branch.cs @@ -22,12 +22,29 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { + public abstract class Branch : Instruction { + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Branch inst, XElement element) + { + Load((Instruction)inst, element); + inst.Name = (string)element.Attribute("name"); + } + + public string Name { get; set; } + internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen) { CodeEmitterLabel l; @@ -45,7 +62,6 @@ internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen internal abstract void Emit(CodeEmitter ilgen, CodeEmitterLabel label); - [XmlAttribute("name")] - public string Name; } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Call.cs b/src/IKVM.Tools.Importer/MapXml/Call.cs similarity index 78% rename from src/ikvmc/IKVM/Internal/MapXml/Call.cs rename to src/IKVM.Tools.Importer/MapXml/Call.cs index dfa5bd320d..ee7da26896 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Call.cs +++ b/src/IKVM.Tools.Importer/MapXml/Call.cs @@ -25,55 +25,92 @@ Jeroen Frijters using System; using System.Diagnostics; using System.Linq; -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("call")] + + [Instruction("call")] public class Call : Instruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Call Read(XElement element) + { + var inst = new Call(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Call inst, XElement element) + { + inst.Class = (string)element.Attribute("class"); + inst.Type = (string)element.Attribute("type"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + } + + readonly OpCode opcode; + + /// + /// Initializes a new instance. + /// public Call() : this(OpCodes.Call) { + } - internal Call(OpCode opcode) + /// + /// Initializes a new instance. + /// + /// + protected Call(OpCode opcode) { this.opcode = opcode; } - [XmlAttribute("class")] - public string Class; - [XmlAttribute("type")] - public string type; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + public string Class { get; set; } + + public string Type { get; set; } + + public string Name { get; set; } - private OpCode opcode; + public string Sig { get; set; } internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen) { - Debug.Assert(Name != null); + if (Name == null) + throw new MapXmlException("Missing name."); + if (Name == ".ctor") { - Debug.Assert(Class == null && type != null); + Debug.Assert(Class == null && Type != null); Type[] argTypes = context.ClassLoader.ArgTypeListFromSig(Sig); - ConstructorInfo ci = StaticCompiler.GetTypeForMapXml(context.ClassLoader, type).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, argTypes, null); + ConstructorInfo ci = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, argTypes, null); if (ci == null) { - throw new InvalidOperationException("Missing .ctor: " + type + "..ctor" + Sig); + throw new InvalidOperationException("Missing .ctor: " + Type + "..ctor" + Sig); } ilgen.Emit(opcode, ci); } else { - Debug.Assert(Class == null ^ type == null); + Debug.Assert(Class == null ^ Type == null); if (Class != null) { Debug.Assert(Sig != null); @@ -150,7 +187,7 @@ internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen } else if (Sig == "") { - argTypes = Type.EmptyTypes; + argTypes = Reflection.Type.EmptyTypes; } else { @@ -162,10 +199,10 @@ internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen } } - Type ti = StaticCompiler.GetTypeForMapXml(context.ClassLoader, type); + Type ti = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type); if (ti == null) { - throw new InvalidOperationException("Missing type: " + type); + throw new InvalidOperationException("Missing type: " + Type); } MethodInfo mi = ti.GetMethod(Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, null, argTypes, null); @@ -179,5 +216,7 @@ internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen } } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Callvirt.cs b/src/IKVM.Tools.Importer/MapXml/Callvirt.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Callvirt.cs rename to src/IKVM.Tools.Importer/MapXml/Callvirt.cs index f05a0504a1..3e235dceef 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Callvirt.cs +++ b/src/IKVM.Tools.Importer/MapXml/Callvirt.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("callvirt")] + + [Instruction("callvirt")] public sealed class Callvirt : Call { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Callvirt Read(XElement element) + { + var inst = new Callvirt(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Callvirt inst, XElement element) + { + Load((Call)inst, element); + } + + /// + /// Initializes a new instance. + /// public Callvirt() : base(OpCodes.Callvirt) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Castclass.cs b/src/IKVM.Tools.Importer/MapXml/Castclass.cs similarity index 70% rename from src/ikvmc/IKVM/Internal/MapXml/Castclass.cs rename to src/IKVM.Tools.Importer/MapXml/Castclass.cs index f3f55e59df..5823d0be44 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Castclass.cs +++ b/src/IKVM.Tools.Importer/MapXml/Castclass.cs @@ -22,30 +22,48 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("castclass")] + + [Instruction("castclass")] public sealed class Castclass : TypeOrTypeWrapperInstruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Castclass Read(XElement element) + { + var inst = new Castclass(); + Load(inst, element); + return inst; + } + + /// + /// Initializes a new instance. + /// public Castclass() { + } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { base.Generate(context, ilgen); + if (typeType != null) - { ilgen.Emit(OpCodes.Castclass, typeType); - } else - { typeWrapper.EmitCheckcast(ilgen); - } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Castclass_impl.cs b/src/IKVM.Tools.Importer/MapXml/Castclass_impl.cs similarity index 65% rename from src/ikvmc/IKVM/Internal/MapXml/Castclass_impl.cs rename to src/IKVM.Tools.Importer/MapXml/Castclass_impl.cs index 80bc888d10..13ce89d45e 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Castclass_impl.cs +++ b/src/IKVM.Tools.Importer/MapXml/Castclass_impl.cs @@ -22,25 +22,46 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("castclass_impl")] + + [Instruction("castclass_impl")] public sealed class Castclass_impl : Instruction { - [XmlAttribute("class")] - public string Class; + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Castclass_impl Read(XElement element) + { + var inst = new Castclass_impl(); + Load(inst, element); + inst.Class = (string)element.Attribute("class"); + return inst; + } + + /// + /// Initializes a new instance. + /// public Castclass_impl() { + } + public string Class { get; set; } + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.Emit(OpCodes.Castclass, context.ClassLoader.LoadClassByDottedName(Class).TypeAsBaseType); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/CatchBlock.cs b/src/IKVM.Tools.Importer/MapXml/CatchBlock.cs similarity index 50% rename from src/ikvmc/IKVM/Internal/MapXml/CatchBlock.cs rename to src/IKVM.Tools.Importer/MapXml/CatchBlock.cs index 1eec8212a0..dc46ef5e80 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/CatchBlock.cs +++ b/src/IKVM.Tools.Importer/MapXml/CatchBlock.cs @@ -22,15 +22,42 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + public sealed class CatchBlock : InstructionList { - [XmlAttribute("type")] - public string type; - [XmlAttribute("class")] - public string Class; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new CatchBlock Read(XElement element) + { + var block = new CatchBlock(); + Load(block, element); + return block; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(CatchBlock block, XElement element) + { + Load((InstructionList)block, element); + block.Type = (string)element.Attribute("type"); + block.Class = (string)element.Attribute("class"); + } + + public string Type { get; set; } + + public string Class { get; set; } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ceq.cs b/src/IKVM.Tools.Importer/MapXml/Ceq.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ceq.cs rename to src/IKVM.Tools.Importer/MapXml/Ceq.cs index 3111f9732e..8c6e1708a2 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ceq.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ceq.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ceq")] + + [Instruction("ceq")] public sealed class Ceq : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ceq Read(XElement element) + { + var list = new Ceq(); + Load(list, element); + return list; + } + + /// + /// Loads the XML element into a instance. + /// + /// + /// + public static void Load(Ceq inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ceq() : base(OpCodes.Ceq) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Class.cs b/src/IKVM.Tools.Importer/MapXml/Class.cs new file mode 100644 index 0000000000..82b6e328a3 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Class.cs @@ -0,0 +1,67 @@ +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public class Class : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Class Read(XElement element) + { + var clazz = new Class(); + Load(clazz, element); + return clazz; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(Class clazz, XElement element) + { + Load((MapXmlElement)clazz, element); + clazz.Name = (string)element.Attribute("name"); + clazz.Shadows = (string)element.Attribute("shadows"); + clazz.Modifiers = MapXmlSerializer.ReadMapModifiers((string)element.Attribute("modifiers")); + clazz.Scope = MapXmlSerializer.ReadScope((string)element.Attribute("scope")); + clazz.Constructors = element.Elements(MapXmlSerializer.NS + "constructor").Select(Constructor.Read).ToArray(); + clazz.Methods = element.Elements(MapXmlSerializer.NS + "method").Select(Method.Read).ToArray(); + clazz.Fields = element.Elements(MapXmlSerializer.NS + "field").Select(Field.Read).ToArray(); + clazz.Properties = element.Elements(MapXmlSerializer.NS + "property").Select(Property.Read).ToArray(); + clazz.Interfaces = element.Elements(MapXmlSerializer.NS + "implements").Select(Implements.Read).ToArray(); + clazz.Clinit = element.Elements(MapXmlSerializer.NS + "clinit").Select(ClassInitializer.Read).FirstOrDefault(); + clazz.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + } + + public string Name { get; set; } + + public string Shadows { get; set; } + + public MapModifiers Modifiers { get; set; } + + public Scope Scope { get; set; } + + public Constructor[] Constructors { get; set; } + + public Method[] Methods { get; set; } + + public Field[] Fields { get; set; } + + public Property[] Properties { get; set; } + + public Implements[] Interfaces { get; set; } + + public ClassInitializer Clinit { get; set; } + + public Attribute[] Attributes { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/ClassInitializer.cs b/src/IKVM.Tools.Importer/MapXml/ClassInitializer.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/ClassInitializer.cs rename to src/IKVM.Tools.Importer/MapXml/ClassInitializer.cs index ef8a3c602c..f2948ead67 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/ClassInitializer.cs +++ b/src/IKVM.Tools.Importer/MapXml/ClassInitializer.cs @@ -22,13 +22,45 @@ Jeroen Frijters */ -namespace IKVM.Internal.MapXml +using System.Linq; +using System.Xml.Linq; + +using IKVM.Internal; +using IKVM.Runtime; + +namespace IKVM.Tools.Importer.MapXml { + public sealed class ClassInitializer : MethodBase { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static ClassInitializer Read(XElement element) + { + var clinit = new ClassInitializer(); + Load(clinit, element); + return clinit; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(ClassInitializer clinit, XElement element) + { + Load((MethodBase)clinit, element); + } + internal override MethodKey ToMethodKey(string className) { return new MethodKey(className, StringConstants.CLINIT, StringConstants.SIG_VOID); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/CodeGenContext.cs b/src/IKVM.Tools.Importer/MapXml/CodeGenContext.cs similarity index 73% rename from src/ikvmc/IKVM/Internal/MapXml/CodeGenContext.cs rename to src/IKVM.Tools.Importer/MapXml/CodeGenContext.cs index 27166672fd..6b351fa802 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/CodeGenContext.cs +++ b/src/IKVM.Tools.Importer/MapXml/CodeGenContext.cs @@ -24,13 +24,21 @@ Jeroen Frijters using System.Collections.Generic; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { + sealed class CodeGenContext { - private ClassLoaderWrapper classLoader; - private readonly Dictionary h = new Dictionary(); + readonly ClassLoaderWrapper classLoader; + readonly Dictionary h = new Dictionary(); + + /// + /// Initializes a new instance. + /// + /// internal CodeGenContext(ClassLoaderWrapper classLoader) { this.classLoader = classLoader; @@ -38,15 +46,12 @@ internal CodeGenContext(ClassLoaderWrapper classLoader) internal object this[string key] { - get - { - object val; - h.TryGetValue(key, out val); - return val; - } + get => h.TryGetValue(key, out var val) ? val : null; set { h[key] = value; } } - internal ClassLoaderWrapper ClassLoader { get { return classLoader; } } + internal ClassLoaderWrapper ClassLoader => classLoader; + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/ConditionalInstruction.cs b/src/IKVM.Tools.Importer/MapXml/ConditionalInstruction.cs new file mode 100644 index 0000000000..ee4fb7d284 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/ConditionalInstruction.cs @@ -0,0 +1,76 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System; +using System.Linq; +using System.Xml.Linq; + +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("conditional")] + public sealed class ConditionalInstruction : Instruction + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new ConditionalInstruction Read(XElement element) + { + var inst = new ConditionalInstruction(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(ConditionalInstruction inst, XElement element) + { + Load((Instruction)inst, element); + inst.Framework = (string)element.Attribute("framework"); + inst.Code = element.Elements(MapXmlSerializer.NS + "code").Select(InstructionList.Read).FirstOrDefault(); + } + + public string Framework { get; set; } + + public InstructionList Code { get; set; } + + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) + { + if (Environment.Version.ToString().StartsWith(Framework)) + { + Code.Generate(context, ilgen); + } + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Constructor.cs b/src/IKVM.Tools.Importer/MapXml/Constructor.cs similarity index 60% rename from src/ikvmc/IKVM/Internal/MapXml/Constructor.cs rename to src/IKVM.Tools.Importer/MapXml/Constructor.cs index c4b0209960..121217834f 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Constructor.cs +++ b/src/IKVM.Tools.Importer/MapXml/Constructor.cs @@ -22,13 +22,44 @@ Jeroen Frijters */ -namespace IKVM.Internal.MapXml +using System.Xml.Linq; + +using IKVM.Internal; +using IKVM.Runtime; + +namespace IKVM.Tools.Importer.MapXml { + public sealed class Constructor : MethodConstructorBase { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Constructor Read(XElement element) + { + var ctor = new Constructor(); + Load(ctor, element); + return ctor; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(Constructor ctor, XElement element) + { + Load((MethodConstructorBase)ctor, element); + } + internal override MethodKey ToMethodKey(string className) { return new MethodKey(className, StringConstants.INIT, Sig); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_I.cs b/src/IKVM.Tools.Importer/MapXml/Conv_I.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_I.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_I.cs index 4d84303e3d..73749adda5 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_I.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_I.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_i")] + + [Instruction("conv_i")] public sealed class Conv_I : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_I Read(XElement element) + { + var inst = new Conv_I(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_I inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_I() : base(OpCodes.Conv_I) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_I1.cs b/src/IKVM.Tools.Importer/MapXml/Conv_I1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_I1.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_I1.cs index 1b2d77fc56..856a49a398 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_I1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_I1.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_i1")] + + [Instruction("conv_i1")] public sealed class Conv_I1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_I1 Read(XElement element) + { + var e = new Conv_I1(); + Load(e, element); + return e; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_I1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_I1() : base(OpCodes.Conv_I1) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_I2.cs b/src/IKVM.Tools.Importer/MapXml/Conv_I2.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_I2.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_I2.cs index 58c3c05db3..29c660e99d 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_I2.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_I2.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_i2")] + + [Instruction("conv_i2")] public sealed class Conv_I2 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_I2 Read(XElement element) + { + var inst = new Conv_I2(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_I2 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_I2() : base(OpCodes.Conv_I2) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_I4.cs b/src/IKVM.Tools.Importer/MapXml/Conv_I4.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_I4.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_I4.cs index fee07884e8..c0dbd48d2e 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_I4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_I4.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_i4")] + + [Instruction("conv_i4")] public sealed class Conv_I4 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_I4 Read(XElement element) + { + var inst = new Conv_I4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_I4 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_I4() : base(OpCodes.Conv_I4) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_I8.cs b/src/IKVM.Tools.Importer/MapXml/Conv_I8.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_I8.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_I8.cs index db575e2f60..0072cb2e99 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_I8.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_I8.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_i8")] + + [Instruction("conv_i8")] public sealed class Conv_I8 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_I8 Read(XElement element) + { + var inst = new Conv_I8(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_I8 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_I8() : base(OpCodes.Conv_I8) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_U1.cs b/src/IKVM.Tools.Importer/MapXml/Conv_U1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_U1.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_U1.cs index a5de2d64cf..83d6dd75b7 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_U1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_U1.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_u1")] + + [Instruction("conv_u1")] public sealed class Conv_U1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_U1 Read(XElement element) + { + var inst = new Conv_U1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_U1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_U1() : base(OpCodes.Conv_U1) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_U2.cs b/src/IKVM.Tools.Importer/MapXml/Conv_U2.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_U2.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_U2.cs index 10dd6ec736..6299552b21 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_U2.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_U2.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_u2")] + + [Instruction("conv_u2")] public sealed class Conv_U2 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_U2 Read(XElement element) + { + var inst = new Conv_U2(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_U2 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_U2() : base(OpCodes.Conv_U2) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_U4.cs b/src/IKVM.Tools.Importer/MapXml/Conv_U4.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_U4.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_U4.cs index d84947fdf5..e91580b721 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_U4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_U4.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_u4")] + + [Instruction("conv_u4")] public sealed class Conv_U4 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_U4 Read(XElement element) + { + var inst = new Conv_U4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_U4 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_U4() : base(OpCodes.Conv_U4) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Conv_U8.cs b/src/IKVM.Tools.Importer/MapXml/Conv_U8.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Conv_U8.cs rename to src/IKVM.Tools.Importer/MapXml/Conv_U8.cs index 277f550b47..37626248f2 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Conv_U8.cs +++ b/src/IKVM.Tools.Importer/MapXml/Conv_U8.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("conv_u8")] + + [Instruction("conv_u8")] public sealed class Conv_U8 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Conv_U8 Read(XElement element) + { + var inst = new Conv_U8(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Conv_U8 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Conv_U8() : base(OpCodes.Conv_U8) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Cpblk.cs b/src/IKVM.Tools.Importer/MapXml/Cpblk.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Cpblk.cs rename to src/IKVM.Tools.Importer/MapXml/Cpblk.cs index 22561b194e..f10e19ea49 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Cpblk.cs +++ b/src/IKVM.Tools.Importer/MapXml/Cpblk.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("cpblk")] + + [Instruction("cpblk")] public sealed class Cpblk : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Cpblk Read(XElement element) + { + var inst = new Cpblk(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Cpblk inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Cpblk() : base(OpCodes.Cpblk) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Div_Un.cs b/src/IKVM.Tools.Importer/MapXml/Div_Un.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Div_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Div_Un.cs index 4be867a56b..b625ecc836 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Div_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Div_Un.cs @@ -22,18 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("div_un")] + + [Instruction("div_un")] public sealed class Div_Un : Simple { - public Div_Un() - : base(OpCodes.Div_Un) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Div_Un Read(XElement element) + { + var inst = new Div_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Div_Un inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Div_Un() : base(OpCodes.Div_Un) + { + + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Dup.cs b/src/IKVM.Tools.Importer/MapXml/Dup.cs similarity index 57% rename from src/ikvmc/IKVM/Internal/MapXml/Dup.cs rename to src/IKVM.Tools.Importer/MapXml/Dup.cs index 31c39a33dc..2b6760e048 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Dup.cs +++ b/src/IKVM.Tools.Importer/MapXml/Dup.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("dup")] + + [Instruction("dup")] public sealed class Dup : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Dup Read(XElement element) + { + var inst = new Dup(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Dup inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Dup() : base(OpCodes.Dup) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Element.cs b/src/IKVM.Tools.Importer/MapXml/Element.cs new file mode 100644 index 0000000000..48577933a4 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Element.cs @@ -0,0 +1,60 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Element : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Element Read(XElement element) + { + var e = new Element(); + Load(e, element); + return e; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Element e, XElement element) + { + Load((MapXmlElement)e, element); + e.Value = element.Value; + } + + public string Value { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/EmitExceptionMapping.cs b/src/IKVM.Tools.Importer/MapXml/EmitExceptionMapping.cs similarity index 53% rename from src/ikvmc/IKVM/Internal/MapXml/EmitExceptionMapping.cs rename to src/IKVM.Tools.Importer/MapXml/EmitExceptionMapping.cs index 48f6f2cf78..4ec630c3e3 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/EmitExceptionMapping.cs +++ b/src/IKVM.Tools.Importer/MapXml/EmitExceptionMapping.cs @@ -22,19 +22,52 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("exceptionMapping")] + + [Instruction("exceptionMapping")] public sealed class EmitExceptionMapping : Instruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new EmitExceptionMapping Read(XElement element) + { + var inst = new EmitExceptionMapping(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(EmitExceptionMapping inst, XElement element) + { + Load((Instruction)inst, element); + } + + /// + /// Initializes a new instance. + /// + /// + internal ExceptionMapping[] mapping; internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { - CompilerClassLoader.ExceptionMapEmitter emitter = new CompilerClassLoader.ExceptionMapEmitter(mapping); + var emitter = new CompilerClassLoader.ExceptionMapEmitter(mapping); emitter.Emit(context, ilgen); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Endfinally.cs b/src/IKVM.Tools.Importer/MapXml/Endfinally.cs similarity index 57% rename from src/ikvmc/IKVM/Internal/MapXml/Endfinally.cs rename to src/IKVM.Tools.Importer/MapXml/Endfinally.cs index cc6f795542..fac18f2ee6 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Endfinally.cs +++ b/src/IKVM.Tools.Importer/MapXml/Endfinally.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("endfinally")] + + [Instruction("endfinally")] public sealed class Endfinally : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Endfinally Read(XElement element) + { + var e = new Endfinally(); + Load(e, element); + return e; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Endfinally e, XElement element) + { + Load((Simple)e, element); + } + + /// + /// Initializes a new instance. + /// public Endfinally() : base(OpCodes.Endfinally) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/ExceptionBlock.cs b/src/IKVM.Tools.Importer/MapXml/ExceptionBlock.cs new file mode 100644 index 0000000000..c9556fbea4 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/ExceptionBlock.cs @@ -0,0 +1,104 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; +using System.Xml.Serialization; + +using IKVM.Internal; + +using Type = IKVM.Reflection.Type; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("exceptionBlock")] + public sealed class ExceptionBlock : Instruction + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new ExceptionBlock Read(XElement element) + { + var inst = new ExceptionBlock(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(ExceptionBlock inst, XElement element) + { + Load((Instruction)inst, element); + inst.Try = element.Elements(MapXmlSerializer.NS + "try").Select(InstructionList.Read).FirstOrDefault(); + inst.Catch = element.Elements(MapXmlSerializer.NS + "catch").Select(CatchBlock.Read).FirstOrDefault(); + inst.Finally = element.Elements(MapXmlSerializer.NS + "finally").Select(InstructionList.Read).FirstOrDefault(); + } + + public InstructionList Try { get; set; } + + public CatchBlock Catch { get; set; } + + public InstructionList Finally { get; set; } + + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) + { + ilgen.BeginExceptionBlock(); + + Try.Generate(context, ilgen); + + if (Catch != null) + { + Type type; + if (Catch.Type != null) + { + type = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Catch.Type); + } + else + { + type = context.ClassLoader.LoadClassByDottedName(Catch.Class).TypeAsExceptionType; + } + + ilgen.BeginCatchBlock(type); + Catch.Generate(context, ilgen); + } + + if (Finally != null) + { + ilgen.BeginFinallyBlock(); + Finally.Generate(context, ilgen); + } + + ilgen.EndExceptionBlock(); + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/ExceptionMapping.cs b/src/IKVM.Tools.Importer/MapXml/ExceptionMapping.cs new file mode 100644 index 0000000000..9c15d5c96a --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/ExceptionMapping.cs @@ -0,0 +1,67 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class ExceptionMapping : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static ExceptionMapping Read(XElement element) + { + var mapping = new ExceptionMapping(); + Load(mapping, element); + return mapping; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(ExceptionMapping exception, XElement element) + { + Load((MapXmlElement)exception, element); + exception.Source = (string)element.Attribute("src"); + exception.Destination = (string)element.Attribute("dst"); + exception.Code = element.Elements(MapXmlSerializer.NS + "code").Select(InstructionList.Read).FirstOrDefault(); + } + + public string Source { get; set; } + + public string Destination { get; set; } + + public InstructionList Code { get; set; } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Field.cs b/src/IKVM.Tools.Importer/MapXml/Field.cs new file mode 100644 index 0000000000..84e05e2c5f --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Field.cs @@ -0,0 +1,73 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Field : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Field Read(XElement element) + { + var field = new Field(); + Load(field, element); + return field; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Field field, XElement element) + { + Load((MapXmlElement)field, element); + field.Name = (string)element.Attribute("name"); + field.Sig = (string)element.Attribute("sig"); + field.Modifiers = MapXmlSerializer.ReadMapModifiers((string)element.Attribute("modifiers")); + field.Constant = (string)element.Attribute("constant"); + field.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + } + + public string Name { get; set; } + + public string Sig { get; set; } + + public MapModifiers Modifiers { get; set; } + + public string Constant { get; set; } + + public Attribute[] Attributes { get; set; } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Implements.cs b/src/IKVM.Tools.Importer/MapXml/Implements.cs new file mode 100644 index 0000000000..e04e39b368 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Implements.cs @@ -0,0 +1,64 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Implements : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Implements Read(XElement element) + { + var iface = new Implements(); + Load(iface, element); + return iface; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Implements iface, XElement element) + { + Load((MapXmlElement)iface, element); + iface.Class = (string)element.Attribute("class"); + iface.Methods = element.Elements(MapXmlSerializer.NS + "method").Select(Method.Read).ToArray(); + } + + public string Class { get; set; } + + public Method[] Methods { get; set; } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Instruction.cs b/src/IKVM.Tools.Importer/MapXml/Instruction.cs new file mode 100644 index 0000000000..1cf46bd2cf --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Instruction.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; + +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml +{ + + /// + /// Base class for MapXML instruction instances. + /// + public abstract class Instruction : MapXmlElement + { + + static readonly Dictionary readMethodsByName; + + /// + /// Initializes the static instance. + /// + static Instruction() + { + readMethodsByName = typeof(Instruction).Assembly.GetTypes() + .Where(i => i.IsSubclassOf(typeof(Instruction))) + .Where(i => i.GetCustomAttribute() is InstructionAttribute ia) + .Select(i => new { ElementName = MapXmlSerializer.NS + i.GetCustomAttribute().ElementName, Type = i }) + .Select(i => new { i.ElementName, Method = i.Type.GetMethod("Read", BindingFlags.Public | BindingFlags.Static) }) + .ToDictionary(i => i.ElementName, i => i.Method); + } + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Instruction Read(XElement element) + { + if (readMethodsByName.TryGetValue(element.Name, out var m) == false) + throw new MapXmlException($"Could not find instruction type for '{element.Name}'."); + + return (Instruction)m.Invoke(null, new[] { element }); + } + + /// + /// Emits the instruction to the code generator. + /// + /// + /// + internal abstract void Generate(CodeGenContext context, CodeEmitter ilgen); + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/InstructionAttribute.cs b/src/IKVM.Tools.Importer/MapXml/InstructionAttribute.cs new file mode 100644 index 0000000000..52d72d6f6e --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/InstructionAttribute.cs @@ -0,0 +1,32 @@ +using System; + +namespace IKVM.Tools.Importer.MapXml +{ + + /// + /// Describes a map XML instruction. + /// + [AttributeUsage(AttributeTargets.Class)] + class InstructionAttribute : System.Attribute + { + + readonly string elementName; + + /// + /// Initializes a new instance. + /// + /// + /// + public InstructionAttribute(string elementName) + { + this.elementName = elementName ?? throw new ArgumentNullException(nameof(elementName)); + } + + /// + /// Gets the name of the XML element corresponding to the instruction. + /// + public string ElementName => elementName; + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/InstructionList.cs b/src/IKVM.Tools.Importer/MapXml/InstructionList.cs new file mode 100644 index 0000000000..6ffd10db88 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/InstructionList.cs @@ -0,0 +1,75 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml +{ + + public class InstructionList : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static InstructionList Read(XElement element) + { + var list = new InstructionList(); + Load(list, element); + return list; + } + + /// + /// Loads the XML element into a instance. + /// + /// + /// + public static void Load(InstructionList list, XElement element) + { + Load((MapXmlElement)list, element); + list.Instructions = element.Elements().Select(Instruction.Read).ToArray(); + } + + public Instruction[] Instructions { get; set; } + + internal void Generate(CodeGenContext context, CodeEmitter ilgen) + { + if (Instructions != null) + for (int i = 0; i < Instructions.Length; i++) + Instructions[i].Generate(context, ilgen); + } + + internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) + { + Generate(new CodeGenContext(loader), ilgen); + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/IsInst.cs b/src/IKVM.Tools.Importer/MapXml/IsInst.cs similarity index 69% rename from src/ikvmc/IKVM/Internal/MapXml/IsInst.cs rename to src/IKVM.Tools.Importer/MapXml/IsInst.cs index c9cbd47623..3d3f00fdca 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/IsInst.cs +++ b/src/IKVM.Tools.Importer/MapXml/IsInst.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("isinst")] + + [Instruction("isinst")] public sealed class IsInst : TypeOrTypeWrapperInstruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new IsInst Read(XElement element) + { + var inst = new IsInst(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(IsInst inst, XElement element) + { + Load((TypeOrTypeWrapperInstruction)inst, element); + } + + /// + /// Initializes a new instance. + /// public IsInst() { + } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) @@ -60,5 +90,7 @@ internal override void Generate(CodeGenContext context, CodeEmitter ilgen) } } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArg_0.cs b/src/IKVM.Tools.Importer/MapXml/LdArg_0.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/LdArg_0.cs rename to src/IKVM.Tools.Importer/MapXml/LdArg_0.cs index b41c339b0f..5aa356dff4 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArg_0.cs +++ b/src/IKVM.Tools.Importer/MapXml/LdArg_0.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldarg_0")] + + [Instruction("ldarg_0")] public sealed class LdArg_0 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArg_0 Read(XElement element) + { + var inst = new LdArg_0(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArg_0 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public LdArg_0() : base(OpCodes.Ldarg_0) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/LdArg_1.cs b/src/IKVM.Tools.Importer/MapXml/LdArg_1.cs new file mode 100644 index 0000000000..598cc5153a --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/LdArg_1.cs @@ -0,0 +1,69 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; +using System.Xml.Serialization; + +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("ldarg_1")] + public sealed class LdArg_1 : Simple + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArg_1 Read(XElement element) + { + var inst = new LdArg_1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArg_1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// + public LdArg_1() : base(OpCodes.Ldarg_1) + { + + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArg_2.cs b/src/IKVM.Tools.Importer/MapXml/LdArg_2.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/LdArg_2.cs rename to src/IKVM.Tools.Importer/MapXml/LdArg_2.cs index b797dff80f..7997962de8 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArg_2.cs +++ b/src/IKVM.Tools.Importer/MapXml/LdArg_2.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldarg_2")] + + [Instruction("ldarg_2")] public sealed class LdArg_2 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArg_2 Read(XElement element) + { + var inst = new LdArg_2(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArg_2 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public LdArg_2() : base(OpCodes.Ldarg_2) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArg_3.cs b/src/IKVM.Tools.Importer/MapXml/LdArg_3.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/LdArg_3.cs rename to src/IKVM.Tools.Importer/MapXml/LdArg_3.cs index 6918f16b58..b93ca145da 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArg_3.cs +++ b/src/IKVM.Tools.Importer/MapXml/LdArg_3.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldarg_3")] + + [Instruction("ldarg_3")] public sealed class LdArg_3 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArg_3 Read(XElement element) + { + var inst = new LdArg_3(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArg_3 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public LdArg_3() : base(OpCodes.Ldarg_3) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArg_S.cs b/src/IKVM.Tools.Importer/MapXml/LdArg_S.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/LdArg_S.cs rename to src/IKVM.Tools.Importer/MapXml/LdArg_S.cs index 01da5a57be..71a1ed4a3b 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArg_S.cs +++ b/src/IKVM.Tools.Importer/MapXml/LdArg_S.cs @@ -22,19 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldarg_s")] + + [Instruction("ldarg_s")] public sealed class LdArg_S : Instruction { - [XmlAttribute("argNum")] - public byte ArgNum; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArg_S Read(XElement element) + { + var inst = new LdArg_S(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArg_S inst, XElement element) + { + Load((Instruction)inst, element); + inst.ArgNum = (byte)(int)element.Attribute("argNum"); + } + + public byte ArgNum { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.EmitLdarg(ArgNum); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/LdArga.cs b/src/IKVM.Tools.Importer/MapXml/LdArga.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/LdArga.cs rename to src/IKVM.Tools.Importer/MapXml/LdArga.cs index 0432c0ef07..c4fef0720d 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/LdArga.cs +++ b/src/IKVM.Tools.Importer/MapXml/LdArga.cs @@ -22,19 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldarga")] + + [Instruction("ldarga")] public sealed class LdArga : Instruction { - [XmlAttribute("argNum")] - public ushort ArgNum; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdArga Read(XElement element) + { + var inst = new LdArga(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdArga inst, XElement element) + { + Load((Instruction)inst, element); + inst.ArgNum = (ushort)(int)element.Attribute("argNum"); + } + + public ushort ArgNum { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.EmitLdarga(ArgNum); } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/LdLoc.cs b/src/IKVM.Tools.Importer/MapXml/LdLoc.cs new file mode 100644 index 0000000000..c0abf8f9c8 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/LdLoc.cs @@ -0,0 +1,71 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; +using System.Xml.Serialization; + +using IKVM.Internal; +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("ldloc")] + public sealed class LdLoc : Instruction + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new LdLoc Read(XElement element) + { + var inst = new LdLoc(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(LdLoc inst, XElement element) + { + Load((Instruction)inst, element); + inst.Name = (string)element.Attribute("name"); + } + + + public string Name { get; set; } + + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Ldloc, (CodeEmitterLocal)context[Name]); + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4.cs b/src/IKVM.Tools.Importer/MapXml/Ldc_I4.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Ldc_I4.cs rename to src/IKVM.Tools.Importer/MapXml/Ldc_I4.cs index c86f469a1a..9fdbb23946 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldc_I4.cs @@ -22,19 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldc_i4")] + + [Instruction("ldc_i4")] public sealed class Ldc_I4 : Instruction { - [XmlAttribute("value")] - public int val; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldc_I4 Read(XElement element) + { + var inst = new Ldc_I4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldc_I4 inst, XElement element) + { + Load((Instruction)inst, element); + inst.Value = (int)element.Attribute("value"); + } + + public int Value { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { - ilgen.EmitLdc_I4(val); + ilgen.EmitLdc_I4(Value); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_0.cs b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_0.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_0.cs rename to src/IKVM.Tools.Importer/MapXml/Ldc_I4_0.cs index d0a4857435..afe746b5e8 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_0.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_0.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldc_i4_0")] + + [Instruction("ldc_i4_0")] public sealed class Ldc_I4_0 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldc_I4_0 Read(XElement element) + { + var inst = new Ldc_I4_0(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldc_I4_0 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldc_I4_0() : base(OpCodes.Ldc_I4_0) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_1.cs b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_1.cs rename to src/IKVM.Tools.Importer/MapXml/Ldc_I4_1.cs index ad3c358105..2e055bd77e 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_1.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldc_i4_1")] + + [Instruction("ldc_i4_1")] public sealed class Ldc_I4_1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldc_I4_1 Read(XElement element) + { + var inst = new Ldc_I4_1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldc_I4_1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldc_I4_1() : base(OpCodes.Ldc_I4_1) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_M1.cs b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_M1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_M1.cs rename to src/IKVM.Tools.Importer/MapXml/Ldc_I4_M1.cs index 980eb8db16..c9cb248b5c 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldc_I4_M1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldc_I4_M1.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldc_i4_m1")] + + [Instruction("ldc_i4_m1")] public sealed class Ldc_I4_M1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldc_I4_M1 Read(XElement element) + { + var inst = new Ldc_I4_M1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldc_I4_M1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldc_I4_M1() : base(OpCodes.Ldc_I4_M1) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Ldelema.cs b/src/IKVM.Tools.Importer/MapXml/Ldelema.cs new file mode 100644 index 0000000000..d1bebcfc66 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Ldelema.cs @@ -0,0 +1,70 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +using IKVM.Internal; +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("ldelema")] + public sealed class Ldelema : Instruction + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldelema Read(XElement element) + { + var inst = new Ldelema(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldelema inst, XElement element) + { + Load((Instruction)inst, element); + inst.Sig = (string)element.Attribute("sig"); + } + + + public string Sig { get; set; } + + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Ldelema, context.ClassLoader.FieldTypeWrapperFromSig(Sig, LoadMode.LoadOrThrow).TypeAsArrayType); + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldfld.cs b/src/IKVM.Tools.Importer/MapXml/Ldfld.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Ldfld.cs rename to src/IKVM.Tools.Importer/MapXml/Ldfld.cs index f007de71bb..ae9dbb5682 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldfld.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldfld.cs @@ -22,26 +22,55 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldfld")] + + [Instruction("ldfld")] public sealed class Ldfld : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldfld Read(XElement element) + { + var inst = new Ldfld(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldfld inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Name= (string)element.Attribute("name"); + inst.Sig= (string)element.Attribute("sig"); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { // we don't use fw.EmitGet because we don't want automatic unboxing and whatever ilgen.Emit(OpCodes.Ldfld, StaticCompiler.GetFieldForMapXml(context.ClassLoader, Class, Name, Sig).GetField()); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldflda.cs b/src/IKVM.Tools.Importer/MapXml/Ldflda.cs similarity index 53% rename from src/ikvmc/IKVM/Internal/MapXml/Ldflda.cs rename to src/IKVM.Tools.Importer/MapXml/Ldflda.cs index c20fc6aa1d..4ddcf2db3c 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldflda.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldflda.cs @@ -22,25 +22,54 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldflda")] + + [Instruction("ldflda")] public sealed class Ldflda : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldflda Read(XElement element) + { + var inst = new Ldflda(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldflda inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.Emit(OpCodes.Ldflda, StaticCompiler.GetFieldForMapXml(context.ClassLoader, Class, Name, Sig).GetField()); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldftn.cs b/src/IKVM.Tools.Importer/MapXml/Ldftn.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldftn.cs rename to src/IKVM.Tools.Importer/MapXml/Ldftn.cs index 7cbfeac623..0c3395bae8 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldftn.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldftn.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldftn")] + + [Instruction("ldftn")] public sealed class Ldftn : Call { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldftn Read(XElement element) + { + var inst = new Ldftn(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldftn inst, XElement element) + { + Load((Call)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldftn() : base(OpCodes.Ldftn) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i1.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_i1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_i1.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_i1.cs index 224a9d5462..5ee93d4cb1 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_i1.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_i1")] + + [Instruction("ldind_i1")] public sealed class Ldind_i1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_i1 Read(XElement element) + { + var inst = new Ldind_i1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_i1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_i1() : base(OpCodes.Ldind_I1) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i2.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_i2.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_i2.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_i2.cs index 712970a7b5..02f96718c6 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i2.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_i2.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_i2")] + + [Instruction("ldind_i2")] public sealed class Ldind_i2 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_i2 Read(XElement element) + { + var inst = new Ldind_i2(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_i2 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_i2() : base(OpCodes.Ldind_I2) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i4.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_i4.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_i4.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_i4.cs index 701fe3ddc2..d2631f1a85 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_i4.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_i4")] + + [Instruction("ldind_i4")] public sealed class Ldind_i4 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_i4 Read(XElement element) + { + var inst = new Ldind_i4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_i4 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_i4() : base(OpCodes.Ldind_I4) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i8.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_i8.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_i8.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_i8.cs index 49d727e7dc..4dc7dee7d7 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_i8.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_i8.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_i8")] + + [Instruction("ldind_i8")] public sealed class Ldind_i8 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_i8 Read(XElement element) + { + var inst = new Ldind_i8(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_i8 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_i8() : base(OpCodes.Ldind_I8) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_r4.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_r4.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_r4.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_r4.cs index 7927517c19..9e9194e722 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_r4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_r4.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_r4")] + + [Instruction("ldind_r4")] public sealed class Ldind_r4 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_r4 Read(XElement element) + { + var inst = new Ldind_r4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_r4 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_r4() : base(OpCodes.Ldind_R4) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_r8.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_r8.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_r8.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_r8.cs index 21dacee2ff..638c8254da 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_r8.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_r8.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_r8")] + + [Instruction("ldind_r8")] public sealed class Ldind_r8 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_r8 Read(XElement element) + { + var inst = new Ldind_r8(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_r8 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_r8() : base(OpCodes.Ldind_R8) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldind_ref.cs b/src/IKVM.Tools.Importer/MapXml/Ldind_ref.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldind_ref.cs rename to src/IKVM.Tools.Importer/MapXml/Ldind_ref.cs index 5950cd75d3..2e1e048784 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldind_ref.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldind_ref.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldind_ref")] + + [Instruction("ldind_ref")] public sealed class Ldind_ref : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldind_ref Read(XElement element) + { + var inst = new Ldind_ref(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldind_ref inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldind_ref() : base(OpCodes.Ldind_Ref) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldlen.cs b/src/IKVM.Tools.Importer/MapXml/Ldlen.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldlen.cs rename to src/IKVM.Tools.Importer/MapXml/Ldlen.cs index 4bc8551629..2c1b6621a0 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldlen.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldlen.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldlen")] + + [Instruction("ldlen")] public sealed class Ldlen : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldlen Read(XElement element) + { + var inst = new Ldlen(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldlen inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldlen() : base(OpCodes.Ldlen) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldnull.cs b/src/IKVM.Tools.Importer/MapXml/Ldnull.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldnull.cs rename to src/IKVM.Tools.Importer/MapXml/Ldnull.cs index d023a01856..6034da1d3e 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldnull.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldnull.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldnull")] + + [Instruction("ldnull")] public sealed class Ldnull : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldnull Read(XElement element) + { + var inst = new Ldnull(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldnull inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldnull() : base(OpCodes.Ldnull) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldobj.cs b/src/IKVM.Tools.Importer/MapXml/Ldobj.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldobj.cs rename to src/IKVM.Tools.Importer/MapXml/Ldobj.cs index e54a397c2f..6977dbbe71 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldobj.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldobj.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldobj")] + + [Instruction("ldobj")] public sealed class Ldobj : TypeInstruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldobj Read(XElement element) + { + var inst = new Ldobj(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldobj inst, XElement element) + { + Load((TypeInstruction)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ldobj() : base(OpCodes.Ldobj) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldsfld.cs b/src/IKVM.Tools.Importer/MapXml/Ldsfld.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Ldsfld.cs rename to src/IKVM.Tools.Importer/MapXml/Ldsfld.cs index d342326409..96ca7e9567 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldsfld.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldsfld.cs @@ -22,24 +22,52 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldsfld")] + + [Instruction("ldsfld")] public sealed class Ldsfld : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("type")] - public string Type; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldsfld Read(XElement element) + { + var inst = new Ldsfld(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldsfld inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Type = (string)element.Attribute("type"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Class { get; set; } + + public string Type { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { @@ -53,5 +81,7 @@ internal override void Generate(CodeGenContext context, CodeEmitter ilgen) ilgen.Emit(OpCodes.Ldsfld, StaticCompiler.GetFieldForMapXml(context.ClassLoader, Class, Name, Sig).GetField()); } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldstr.cs b/src/IKVM.Tools.Importer/MapXml/Ldstr.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ldstr.cs rename to src/IKVM.Tools.Importer/MapXml/Ldstr.cs index 0e68d660e1..b895fa4458 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldstr.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldstr.cs @@ -22,21 +22,48 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldstr")] + + [Instruction("ldstr")] public sealed class Ldstr : Instruction { - [XmlAttribute("value")] - public string Value; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldstr Read(XElement element) + { + var inst = new Ldstr(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldstr inst, XElement element) + { + Load((Instruction)inst, element); + inst.Value = (string)element.Attribute("value"); + } + + public string Value { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.Emit(OpCodes.Ldstr, Value); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldtoken.cs b/src/IKVM.Tools.Importer/MapXml/Ldtoken.cs similarity index 77% rename from src/ikvmc/IKVM/Internal/MapXml/Ldtoken.cs rename to src/IKVM.Tools.Importer/MapXml/Ldtoken.cs index 8ba1910f94..430586cae7 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldtoken.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldtoken.cs @@ -23,28 +23,58 @@ Jeroen Frijters */ using System; +using System.Xml.Linq; using System.Xml.Serialization; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldtoken")] + + [Instruction("ldtoken")] public sealed class Ldtoken : Instruction { - [XmlAttribute("type")] - public string type; - [XmlAttribute("class")] - public string Class; - [XmlAttribute("method")] - public string Method; - [XmlAttribute("field")] - public string Field; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldtoken Read(XElement element) + { + var inst = new Ldtoken(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldtoken inst, XElement element) + { + Load((Instruction)inst, element); + inst.Type = (string)element.Attribute("type"); + inst.Class = (string)element.Attribute("class"); + inst.Method = (string)element.Attribute("method"); + inst.Field = (string)element.Attribute("field"); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Type { get; set; } + + public string Class { get; set; } + + public string Method { get; set; } + + public string Field { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { @@ -83,7 +113,7 @@ internal override void Generate(CodeGenContext context, CodeEmitter ilgen) private bool Validate() { - if (type != null && Class == null) + if (Type != null && Class == null) { if (Method != null || Field != null || Sig != null) { @@ -92,7 +122,7 @@ private bool Validate() } return true; } - else if (Class != null && type == null) + else if (Class != null && Type == null) { if (Method == null && Field == null) { @@ -118,13 +148,13 @@ private bool Validate() private MemberInfo Resolve(CodeGenContext context) { - if (type != null) + if (Type != null) { if (Class != null || Method != null || Field != null || Sig != null) { throw new NotImplementedException(); } - return StaticCompiler.GetTypeForMapXml(context.ClassLoader, type); + return StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type); } else if (Class != null) { @@ -161,5 +191,7 @@ private MemberInfo Resolve(CodeGenContext context) return null; } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ldvirtftn.cs b/src/IKVM.Tools.Importer/MapXml/Ldvirtftn.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/Ldvirtftn.cs rename to src/IKVM.Tools.Importer/MapXml/Ldvirtftn.cs index 9e2e80d502..27737ce64d 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ldvirtftn.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ldvirtftn.cs @@ -22,17 +22,45 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ldvirtftn")] + + [Instruction("ldvirtftn")] public sealed class Ldvirtftn : Call { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ldvirtftn Read(XElement element) + { + var inst = new Ldvirtftn(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ldvirtftn inst, XElement element) + { + Load((Call)inst, element); + } + public Ldvirtftn() : base(OpCodes.Ldvirtftn) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Leave.cs b/src/IKVM.Tools.Importer/MapXml/Leave.cs similarity index 59% rename from src/ikvmc/IKVM/Internal/MapXml/Leave.cs rename to src/IKVM.Tools.Importer/MapXml/Leave.cs index a0fc960127..cbb87f7a15 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Leave.cs +++ b/src/IKVM.Tools.Importer/MapXml/Leave.cs @@ -22,16 +22,45 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("leave")] + + [Instruction("leave")] public sealed class Leave : Branch { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Leave Read(XElement element) + { + var inst = new Leave(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Leave inst, XElement element) + { + Load((Branch)inst, element); + } + internal override void Emit(CodeEmitter ilgen, CodeEmitterLabel label) { ilgen.EmitLeave(label); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/MapModifiers.cs b/src/IKVM.Tools.Importer/MapXml/MapModifiers.cs similarity index 80% rename from src/ikvmc/IKVM/Internal/MapXml/MapModifiers.cs rename to src/IKVM.Tools.Importer/MapXml/MapModifiers.cs index 16a18ea50f..6950442022 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/MapModifiers.cs +++ b/src/IKVM.Tools.Importer/MapXml/MapModifiers.cs @@ -23,32 +23,26 @@ Jeroen Frijters */ using System; -using System.Xml.Serialization; using IKVM.Attributes; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + [Flags] public enum MapModifiers { - [XmlEnum("public")] + Public = Modifiers.Public, - [XmlEnum("protected")] Protected = Modifiers.Protected, - [XmlEnum("private")] Private = Modifiers.Private, - [XmlEnum("final")] Final = Modifiers.Final, - [XmlEnum("interface")] Interface = Modifiers.Interface, - [XmlEnum("static")] Static = Modifiers.Static, - [XmlEnum("abstract")] Abstract = Modifiers.Abstract, - [XmlEnum("ACC_BRIDGE")] Bridge = Modifiers.Bridge, - [XmlEnum("ACC_SYNTHETIC")] Synthetic = Modifiers.Synthetic, + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/MapXmlElement.cs b/src/IKVM.Tools.Importer/MapXml/MapXmlElement.cs new file mode 100644 index 0000000000..303991d8cb --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/MapXmlElement.cs @@ -0,0 +1,57 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public abstract class MapXmlElement + { + + /// + /// Loads the XML element into the instance. + /// + /// + /// + protected static void Load(MapXmlElement e, XElement element) + { + e.LineNumber = ((IXmlLineInfo)element).LineNumber; + e.LinePosition = ((IXmlLineInfo)element).LinePosition; + } + + /// + /// Gets the line number in the source file where this element occurs. + /// + public int LineNumber { get; set; } + + /// + /// Gets the line position in the source file where this element occurs. + /// + public int LinePosition { get; set; } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Tools.Importer/MapXml/MapXmlException.cs b/src/IKVM.Tools.Importer/MapXml/MapXmlException.cs new file mode 100644 index 0000000000..1af9d02e75 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/MapXmlException.cs @@ -0,0 +1,38 @@ +using System; + +namespace IKVM.Tools.Importer.MapXml +{ + + class MapXmlException : Exception + { + + /// + /// Initializes a new instance. + /// + public MapXmlException() + { + + } + + /// + /// Initializes a new instance. + /// + /// + public MapXmlException(string message) : base(message) + { + + } + + /// + /// Initializes a new instance. + /// + /// + /// + public MapXmlException(string message, Exception innerException) : base(message, innerException) + { + + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.Tools.Importer/MapXml/MapXmlSerializer.cs b/src/IKVM.Tools.Importer/MapXml/MapXmlSerializer.cs new file mode 100644 index 0000000000..865777f230 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/MapXmlSerializer.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using System.Xml.Linq; + +using IKVM.Reflection; + +namespace IKVM.Tools.Importer.MapXml +{ + + /// + /// Provides the ability to serialize MapXML. + /// + class MapXmlSerializer + { + + public static XNamespace NS = "http://ikvm.net/schemas/mapxml"; + + /// + /// Reads a list of values. + /// + /// + /// + public static MapModifiers ReadMapModifiers(string value) => value != null ? value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Aggregate((MapModifiers)0, (i, j) => i | (Enum.TryParse(j, true, out var v) ? v : throw new MapXmlException($"Invalid modifiers value '{j}'."))) : 0; + + /// + /// Reads a single value. + /// + /// + /// + public static Scope ReadScope(string value) => value != null ? (Enum.TryParse(value, true, out var v) ? v : throw new MapXmlException($"Invalid scope value '{value}'.")) : 0; + + /// + /// Reads a list of values. + /// + /// + /// + public static MethodAttributes ReadMethodAttributes(string value) => value != null ? value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Aggregate((MethodAttributes)0, (i, j) => i | (Enum.TryParse(j, true, out var v) ? v : throw new MapXmlException($"Invalid attributes value '{j}'."))) : 0; + + /// + /// Deserializes the given XML into a instance. + /// + /// + /// + public Root Read(XDocument document) + { + return Read(document.Root); + } + + /// + /// Deserializes the given XML into a instance. + /// + /// + /// + public Root Read(XElement element) + { + return Root.Read(element); + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Method.cs b/src/IKVM.Tools.Importer/MapXml/Method.cs new file mode 100644 index 0000000000..a4fcb22ad1 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Method.cs @@ -0,0 +1,77 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +using IKVM.Runtime; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Method : MethodConstructorBase + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Method Read(XElement element) + { + var redirect = new Method(); + Load(redirect, element); + return redirect; + } + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(Method method, XElement element) + { + Load((MethodConstructorBase)method, element); + method.Name = (string)element.Attribute("name"); + method.NoNullCheck = (bool?)element.Attribute("nonullcheck") ?? true; + method.NonVirtualAlternateBody = element.Elements(MapXmlSerializer.NS + "nonvirtualAlternateBody").Select(InstructionList.Read).FirstOrDefault(); + method.Override = element.Elements(MapXmlSerializer.NS + "override").Select(Override.Read).FirstOrDefault(); + } + + public string Name { get; set; } + + public bool NoNullCheck { get; set; } + + public InstructionList NonVirtualAlternateBody { get; set; } + + public Override Override { get; set; } + + internal override MethodKey ToMethodKey(string className) + { + return new MethodKey(className, Name, Sig); + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/MethodBase.cs b/src/IKVM.Tools.Importer/MapXml/MethodBase.cs new file mode 100644 index 0000000000..fb710de508 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/MethodBase.cs @@ -0,0 +1,69 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +using IKVM.Reflection; +using IKVM.Runtime; + +namespace IKVM.Tools.Importer.MapXml +{ + + public abstract class MethodBase : MapXmlElement + { + + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(MethodBase method, XElement element) + { + Load((MapXmlElement)method, element); + method.MethodAttributes = MapXmlSerializer.ReadMethodAttributes((string)element.Attribute("attributes")); + method.Body = element.Elements(MapXmlSerializer.NS + "body").Select(InstructionList.Read).FirstOrDefault(); + method.Throws = element.Elements(MapXmlSerializer.NS + "throws").Select(MapXml.Throws.Read).ToArray(); + method.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + method.ReplaceMethodCalls = element.Elements(MapXmlSerializer.NS + "replace-method-call").Select(ReplaceMethodCall.Read).ToArray(); + method.Prologue = element.Elements(MapXmlSerializer.NS + "prologue").Select(InstructionList.Read).FirstOrDefault(); + } + + public MethodAttributes MethodAttributes { get; set; } + + public InstructionList Body { get; set; } + + public Throws[] Throws { get; set; } + + public Attribute[] Attributes { get; set; } + + public ReplaceMethodCall[] ReplaceMethodCalls { get; set; } + + public InstructionList Prologue { get; set; } + + internal abstract MethodKey ToMethodKey(string className); + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/MethodConstructorBase.cs b/src/IKVM.Tools.Importer/MapXml/MethodConstructorBase.cs new file mode 100644 index 0000000000..dc576f7907 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/MethodConstructorBase.cs @@ -0,0 +1,69 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml +{ + + public abstract class MethodConstructorBase : MethodBase + { + + public static void Load(MethodConstructorBase o, XElement element) + { + Load((MethodBase)o, element); + o.Sig = (string)element.Attribute("sig"); + o.Modifiers = MapXmlSerializer.ReadMapModifiers((string)element.Attribute("modifiers")); + o.Parameters = element.Elements(MapXmlSerializer.NS + "parameter").Select(Parameter.Read).ToArray(); + o.AlternateBody = element.Elements(MapXmlSerializer.NS + "alternateBody").Select(InstructionList.Read).FirstOrDefault(); + o.Redirect = element.Elements(MapXmlSerializer.NS + "redirect").Select(Redirect.Read).FirstOrDefault(); + } + + public string Sig { get; set; } + + public MapModifiers Modifiers { get; set; } + + public Parameter[] Parameters { get; set; } + + public InstructionList AlternateBody { get; set; } + + public Redirect Redirect { get; set; } + + internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) + { + if (Prologue != null) + Prologue.Emit(loader, ilgen); + + if (Redirect != null) + Redirect.Emit(loader, ilgen); + else + Body.Emit(loader, ilgen); + } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Mul.cs b/src/IKVM.Tools.Importer/MapXml/Mul.cs new file mode 100644 index 0000000000..dabdba1069 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Mul.cs @@ -0,0 +1,68 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +using IKVM.Reflection.Emit; + +namespace IKVM.Tools.Importer.MapXml +{ + + [Instruction("mul")] + public sealed class Mul : Simple + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Mul Read(XElement element) + { + var inst = new Mul(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Mul inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// + public Mul() : base(OpCodes.Mul) + { + + } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/NewObj.cs b/src/IKVM.Tools.Importer/MapXml/NewObj.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/NewObj.cs rename to src/IKVM.Tools.Importer/MapXml/NewObj.cs index 56d3fa0831..3cea26ab55 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/NewObj.cs +++ b/src/IKVM.Tools.Importer/MapXml/NewObj.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("newobj")] + + [Instruction("newobj")] public sealed class NewObj : Call { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new NewObj Read(XElement element) + { + var inst = new NewObj(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(NewObj inst, XElement element) + { + Load((Call)inst, element); + } + + /// + /// Initializes a new instance. + /// public NewObj() : base(OpCodes.Newobj) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Newarr.cs b/src/IKVM.Tools.Importer/MapXml/Newarr.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Newarr.cs rename to src/IKVM.Tools.Importer/MapXml/Newarr.cs index ab2a851256..44ec264757 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Newarr.cs +++ b/src/IKVM.Tools.Importer/MapXml/Newarr.cs @@ -22,21 +22,48 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("newarr")] + + [Instruction("newarr")] public sealed class Newarr : Instruction { - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Newarr Read(XElement element) + { + var inst = new Newarr(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Newarr inst, XElement element) + { + Load((Instruction)inst, element); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.Emit(OpCodes.Newarr, context.ClassLoader.FieldTypeWrapperFromSig(Sig, LoadMode.LoadOrThrow).TypeAsArrayType); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Not.cs b/src/IKVM.Tools.Importer/MapXml/Not.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Not.cs rename to src/IKVM.Tools.Importer/MapXml/Not.cs index ec9f212d3e..1890f95e33 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Not.cs +++ b/src/IKVM.Tools.Importer/MapXml/Not.cs @@ -22,18 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("not")] + + [Instruction("not")] public sealed class Not : Simple { - public Not() - : base(OpCodes.Not) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Not Read(XElement element) + { + var inst = new Not(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Not inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Not() : base(OpCodes.Not) + { + + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Or.cs b/src/IKVM.Tools.Importer/MapXml/Or.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Or.cs rename to src/IKVM.Tools.Importer/MapXml/Or.cs index 6f2628b425..9f397086ef 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Or.cs +++ b/src/IKVM.Tools.Importer/MapXml/Or.cs @@ -22,18 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("or")] + + [Instruction("or")] public sealed class Or : Simple { - public Or() - : base(OpCodes.Or) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Or Read(XElement element) + { + var inst = new Or(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Or inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Or() : base(OpCodes.Or) + { + + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Override.cs b/src/IKVM.Tools.Importer/MapXml/Override.cs new file mode 100644 index 0000000000..4aa0b4636d --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Override.cs @@ -0,0 +1,63 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Override + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Override Read(XElement element) + { + var inst = new Override(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Override inst, XElement element) + { + inst.Class = (string)element.Attribute("class"); + inst.Name = (string)element.Attribute("name"); + } + + + public string Class { get; set; } + + public string Name { get; set; } + + } + +} diff --git a/src/IKVM.Tools.Importer/MapXml/Param.cs b/src/IKVM.Tools.Importer/MapXml/Param.cs new file mode 100644 index 0000000000..b6b4c3f117 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Param.cs @@ -0,0 +1,72 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Parameter + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Parameter Read(XElement element) + { + var param = new Parameter(); + Load(param, element); + return param; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Parameter param, XElement element) + { + param.Name = (string)element.Attribute("name"); + param.Sig = (string)element.Attribute("sig"); + param.Elements = element.Elements(MapXmlSerializer.NS + "element").Select(Element.Read).ToArray(); + param.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + param.Value = element.Value; + } + + public string Name { get; set; } + + public string Sig { get; set; } + + public Element[] Elements { get; set; } + + public Attribute[] Attributes { get; set; } + + public string Value { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Pop.cs b/src/IKVM.Tools.Importer/MapXml/Pop.cs similarity index 58% rename from src/ikvmc/IKVM/Internal/MapXml/Pop.cs rename to src/IKVM.Tools.Importer/MapXml/Pop.cs index 0f14913091..3768edcfe0 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Pop.cs +++ b/src/IKVM.Tools.Importer/MapXml/Pop.cs @@ -22,18 +22,49 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("pop")] + + [Instruction("pop")] public sealed class Pop : Instruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Pop Read(XElement element) + { + var inst = new Pop(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Pop inst, XElement element) + { + Load((Instruction)inst, element); + } + + /// + /// Initializes a new instance. + /// internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.Emit(OpCodes.Pop); } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Property.cs b/src/IKVM.Tools.Importer/MapXml/Property.cs new file mode 100644 index 0000000000..8532c9d480 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Property.cs @@ -0,0 +1,72 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Property + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Property Read(XElement element) + { + var property = new Property(); + Load(property, element); + return property; + } + + /// + /// Loads the XML element into the class. + /// + /// + /// + public static void Load(Property property, XElement element) + { + property.Name = (string)element.Attribute("name"); + property.Sig = (string)element.Attribute("sig"); + property.Getter = element.Elements(MapXmlSerializer.NS + "getter").Select(Method.Read).FirstOrDefault(); + property.Setter = element.Elements(MapXmlSerializer.NS + "setter").Select(Method.Read).FirstOrDefault(); + property.Attributes = element.Elements(MapXmlSerializer.NS + "attribute").Select(Attribute.Read).ToArray(); + } + + public string Name { get; set; } + + public string Sig { get; set; } + + public Method Getter { get; set; } + + public Method Setter { get; set; } + + public Attribute[] Attributes { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Redirect.cs b/src/IKVM.Tools.Importer/MapXml/Redirect.cs similarity index 67% rename from src/ikvmc/IKVM/Internal/MapXml/Redirect.cs rename to src/IKVM.Tools.Importer/MapXml/Redirect.cs index 923bcc614d..b72769bc71 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Redirect.cs +++ b/src/IKVM.Tools.Importer/MapXml/Redirect.cs @@ -23,42 +23,59 @@ Jeroen Frijters */ using System; -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - public sealed class Redirect + + public sealed class Redirect : MapXmlElement { - private int linenum = Root.LineNumber; - internal int LineNumber + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Redirect Read(XElement element) { - get - { - return linenum; - } + var redirect = new Redirect(); + Load(redirect, element); + return redirect; } - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; - [XmlAttribute("type")] - public string Type; + /// + /// Loads the XML element into the instance. + /// + /// + /// + public static void Load(Redirect redirect, XElement element) + { + Load((MapXmlElement)redirect, element); + redirect.Class = (string)element.Attribute("class"); + redirect.Name = (string)element.Attribute("name"); + redirect.Sig = (string)element.Attribute("sig"); + redirect.Type = (string)element.Attribute("type"); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } + + public string Type { get; set; } internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) { if (Type != "static" || Class == null || Name == null || Sig == null) - { throw new NotImplementedException(); - } + Type[] redirParamTypes = loader.ArgTypeListFromSig(Sig); for (int i = 0; i < redirParamTypes.Length; i++) { @@ -92,5 +109,7 @@ internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) // TODO we may need a cast here (or a stack to return type conversion) ilgen.Emit(OpCodes.Ret); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Rem_Un.cs b/src/IKVM.Tools.Importer/MapXml/Rem_Un.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Rem_Un.cs rename to src/IKVM.Tools.Importer/MapXml/Rem_Un.cs index 64f3fd11f2..5011c91e95 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Rem_Un.cs +++ b/src/IKVM.Tools.Importer/MapXml/Rem_Un.cs @@ -22,18 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("rem_un")] + + [Instruction("rem_un")] public sealed class Rem_Un : Simple { - public Rem_Un() - : base(OpCodes.Rem_Un) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Rem_Un Read(XElement element) + { + var inst = new Rem_Un(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Rem_Un inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Rem_Un() : base(OpCodes.Rem_Un) + { + + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/ReplaceMethodCall.cs b/src/IKVM.Tools.Importer/MapXml/ReplaceMethodCall.cs new file mode 100644 index 0000000000..d60ab833a0 --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/ReplaceMethodCall.cs @@ -0,0 +1,70 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class ReplaceMethodCall : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static ReplaceMethodCall Read(XElement element) + { + var inst = new ReplaceMethodCall(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(ReplaceMethodCall inst, XElement element) + { + Load((MapXmlElement)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + inst.Code = element.Elements(MapXmlSerializer.NS + "code").Select(InstructionList.Read).FirstOrDefault(); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } + + public InstructionList Code { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Ret.cs b/src/IKVM.Tools.Importer/MapXml/Ret.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Ret.cs rename to src/IKVM.Tools.Importer/MapXml/Ret.cs index 33514a81ba..5241e3283c 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Ret.cs +++ b/src/IKVM.Tools.Importer/MapXml/Ret.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("ret")] + + [Instruction("ret")] public sealed class Ret : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Ret Read(XElement element) + { + var inst = new Ret(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Ret inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Ret() : base(OpCodes.Ret) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Root.cs b/src/IKVM.Tools.Importer/MapXml/Root.cs new file mode 100644 index 0000000000..3576b3824c --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Root.cs @@ -0,0 +1,64 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Linq; +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Root : MapXmlElement + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Root Read(XElement element) + { + var root = new Root(); + Load(root, element); + return root; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Root root, XElement element) + { + Load((MapXmlElement)root, element); + root.Assembly = element.Elements(MapXmlSerializer.NS + "assembly").Select(Assembly.Read).FirstOrDefault(); + root.ExceptionMappings = element.Elements(MapXmlSerializer.NS + "exceptionMappings").Elements(MapXmlSerializer.NS + "exception").Select(ExceptionMapping.Read).ToArray(); + } + + public Assembly Assembly { get; set; } + + public ExceptionMapping[] ExceptionMappings { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/RunClassInit.cs b/src/IKVM.Tools.Importer/MapXml/RunClassInit.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/RunClassInit.cs rename to src/IKVM.Tools.Importer/MapXml/RunClassInit.cs index 812b15aafc..2f900dff42 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/RunClassInit.cs +++ b/src/IKVM.Tools.Importer/MapXml/RunClassInit.cs @@ -22,19 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("runclassinit")] + + [Instruction("runclassinit")] public sealed class RunClassInit : Instruction { - [XmlAttribute("class")] - public string Class; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new RunClassInit Read(XElement element) + { + var inst = new RunClassInit(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(RunClassInit inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + } + + public string Class { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { context.ClassLoader.LoadClassByDottedName(Class).EmitRunClassConstructor(ilgen); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Scope.cs b/src/IKVM.Tools.Importer/MapXml/Scope.cs similarity index 89% rename from src/ikvmc/IKVM/Internal/MapXml/Scope.cs rename to src/IKVM.Tools.Importer/MapXml/Scope.cs index 875903dc68..3bc0bab976 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Scope.cs +++ b/src/IKVM.Tools.Importer/MapXml/Scope.cs @@ -22,15 +22,16 @@ Jeroen Frijters */ -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + public enum Scope { - [XmlEnum("public")] + Public = 0, - [XmlEnum("private")] + Private = 1 + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Simple.cs b/src/IKVM.Tools.Importer/MapXml/Simple.cs similarity index 67% rename from src/ikvmc/IKVM/Internal/MapXml/Simple.cs rename to src/IKVM.Tools.Importer/MapXml/Simple.cs index 3dc9e62df3..6e9f0ae44a 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Simple.cs +++ b/src/IKVM.Tools.Importer/MapXml/Simple.cs @@ -22,14 +22,34 @@ Jeroen Frijters */ +using System.Xml.Linq; + +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + public abstract class Simple : Instruction { - private OpCode opcode; + /// + /// Loads the XML element into a instance. + /// + /// + /// + /// + public static void Load(Simple inst, XElement element) + { + Load((Instruction)inst, element); + } + + readonly OpCode opcode; + + /// + /// Initializes a new instance. + /// + /// public Simple(OpCode opcode) { this.opcode = opcode; @@ -39,5 +59,7 @@ internal sealed override void Generate(CodeGenContext context, CodeEmitter ilgen { ilgen.Emit(opcode); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/StLoc.cs b/src/IKVM.Tools.Importer/MapXml/StLoc.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/StLoc.cs rename to src/IKVM.Tools.Importer/MapXml/StLoc.cs index 22b607edda..ec1fe3085c 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/StLoc.cs +++ b/src/IKVM.Tools.Importer/MapXml/StLoc.cs @@ -23,48 +23,75 @@ Jeroen Frijters */ using System.Diagnostics; -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stloc")] + + [Instruction("stloc")] public sealed class StLoc : Instruction { - [XmlAttribute("name")] - public string Name; - [XmlAttribute("class")] - public string Class; - [XmlAttribute("type")] - public string type; - private TypeWrapper typeWrapper; - private Type typeType; + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new StLoc Read(XElement element) + { + var inst = new StLoc(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(StLoc inst, XElement element) + { + Load((Instruction)inst, element); + inst.Name = (string)element.Attribute("name"); + inst.Class = (string)element.Attribute("class"); + inst.Type = (string)element.Attribute("type"); + } + + TypeWrapper typeWrapper; + Type typeType; + + public string Name { get; set; } + + public string Class { get; set; } + + public string Type { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { - CodeEmitterLocal lb = (CodeEmitterLocal)context[Name]; + var lb = (CodeEmitterLocal)context[Name]; if (lb == null) { if (typeWrapper == null && typeType == null) { - Debug.Assert(Class == null ^ type == null); - if (type != null) - { - typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, type); - } + Debug.Assert(Class == null ^ Type == null); + if (Type != null) + typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type); else - { typeWrapper = context.ClassLoader.LoadClassByDottedName(Class); - } } + lb = ilgen.DeclareLocal(typeType != null ? typeType : typeWrapper.TypeAsTBD); context[Name] = lb; } + ilgen.Emit(OpCodes.Stloc, lb); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stfld.cs b/src/IKVM.Tools.Importer/MapXml/Stfld.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Stfld.cs rename to src/IKVM.Tools.Importer/MapXml/Stfld.cs index a9b8ab1eae..7adbecfd06 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stfld.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stfld.cs @@ -22,26 +22,55 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stfld")] + + [Instruction("stfld")] public sealed class Stfld : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stfld Read(XElement element) + { + var inst = new Stfld(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stfld inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { // we don't use fw.EmitSet because we don't want automatic unboxing and whatever ilgen.Emit(OpCodes.Stfld, StaticCompiler.GetFieldForMapXml(context.ClassLoader, Class, Name, Sig).GetField()); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stind_i1.cs b/src/IKVM.Tools.Importer/MapXml/Stind_i1.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Stind_i1.cs rename to src/IKVM.Tools.Importer/MapXml/Stind_i1.cs index 7c9206bd17..a58f813dbe 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stind_i1.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stind_i1.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stind_i1")] + + [Instruction("stind_i1")] public sealed class Stind_i1 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stind_i1 Read(XElement element) + { + var inst = new Stind_i1(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stind_i1 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Stind_i1() : base(OpCodes.Stind_I1) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stind_i2.cs b/src/IKVM.Tools.Importer/MapXml/Stind_i2.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Stind_i2.cs rename to src/IKVM.Tools.Importer/MapXml/Stind_i2.cs index a45f9a0cec..dc1bb863a3 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stind_i2.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stind_i2.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stind_i2")] + + [Instruction("stind_i2")] public sealed class Stind_i2 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stind_i2 Read(XElement element) + { + var inst = new Stind_i2(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stind_i2 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Stind_i2() : base(OpCodes.Stind_I2) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stind_i4.cs b/src/IKVM.Tools.Importer/MapXml/Stind_i4.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Stind_i4.cs rename to src/IKVM.Tools.Importer/MapXml/Stind_i4.cs index 1c5ecfd691..67f4231535 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stind_i4.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stind_i4.cs @@ -22,17 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stind_i4")] + + [Instruction("stind_i4")] public sealed class Stind_i4 : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stind_i4 Read(XElement element) + { + var inst = new Stind_i4(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stind_i4 inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Stind_i4() : base(OpCodes.Stind_I4) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stind_i8.cs b/src/IKVM.Tools.Importer/MapXml/Stind_i8.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Stind_i8.cs rename to src/IKVM.Tools.Importer/MapXml/Stind_i8.cs index 0843bf2bc9..36fb4abec9 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stind_i8.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stind_i8.cs @@ -22,18 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stind_i8")] + + [Instruction("stind_i8")] public sealed class Stind_i8 : Simple { - public Stind_i8() - : base(OpCodes.Stind_I8) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stind_i8 Read(XElement element) + { + var inst = new Stind_i8(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stind_i8 inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Stind_i8() : base(OpCodes.Stind_I8) + { + + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stind_ref.cs b/src/IKVM.Tools.Importer/MapXml/Stind_ref.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Stind_ref.cs rename to src/IKVM.Tools.Importer/MapXml/Stind_ref.cs index d1396017b5..1d653482b2 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stind_ref.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stind_ref.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stind_ref")] + + [Instruction("stind_ref")] public sealed class Stind_ref : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stind_ref Read(XElement element) + { + var inst = new Stind_ref(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stind_ref inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Stind_ref() : base(OpCodes.Stind_Ref) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Stsfld.cs b/src/IKVM.Tools.Importer/MapXml/Stsfld.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Stsfld.cs rename to src/IKVM.Tools.Importer/MapXml/Stsfld.cs index 183c2d40a9..ef37b37f96 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Stsfld.cs +++ b/src/IKVM.Tools.Importer/MapXml/Stsfld.cs @@ -22,26 +22,55 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("stsfld")] + + [Instruction("stsfld")] public sealed class Stsfld : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Stsfld Read(XElement element) + { + var inst = new Stsfld(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Stsfld inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Name = (string)element.Attribute("name"); + inst.Sig = (string)element.Attribute("sig"); + } + + public string Class { get; set; } + + public string Name { get; set; } + + public string Sig { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { // we don't use fw.EmitSet because we don't want automatic unboxing and whatever ilgen.Emit(OpCodes.Stsfld, StaticCompiler.GetFieldForMapXml(context.ClassLoader, Class, Name, Sig).GetField()); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Sub.cs b/src/IKVM.Tools.Importer/MapXml/Sub.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Sub.cs rename to src/IKVM.Tools.Importer/MapXml/Sub.cs index ddba74ab23..1c2af5ec3a 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Sub.cs +++ b/src/IKVM.Tools.Importer/MapXml/Sub.cs @@ -22,18 +22,48 @@ Jeroen Frijters */ +using System.Xml.Linq; using System.Xml.Serialization; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("sub")] + + [Instruction("sub")] public sealed class Sub : Simple { - public Sub() - : base(OpCodes.Sub) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Sub Read(XElement element) + { + var inst = new Sub(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Sub inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Sub() : base(OpCodes.Sub) + { + + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Throw.cs b/src/IKVM.Tools.Importer/MapXml/Throw.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Throw.cs rename to src/IKVM.Tools.Importer/MapXml/Throw.cs index 6e7dd8efc8..0f384cf827 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Throw.cs +++ b/src/IKVM.Tools.Importer/MapXml/Throw.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("throw")] + + [Instruction("throw")] public sealed class Throw : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Throw Read(XElement element) + { + var inst = new Throw(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Throw inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Throw() : base(OpCodes.Throw) { + } + } + } diff --git a/src/IKVM.Tools.Importer/MapXml/Throws.cs b/src/IKVM.Tools.Importer/MapXml/Throws.cs new file mode 100644 index 0000000000..83d6cc757a --- /dev/null +++ b/src/IKVM.Tools.Importer/MapXml/Throws.cs @@ -0,0 +1,59 @@ +/* + Copyright (C) 2002-2010 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +using System.Xml.Linq; + +namespace IKVM.Tools.Importer.MapXml +{ + + public sealed class Throws + { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static Throws Read(XElement element) + { + var inst = new Throws(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Throws inst, XElement element) + { + inst.Class = (string)element.Attribute("class"); + } + + public string Class { get; set; } + + } + +} diff --git a/src/ikvmc/IKVM/Internal/MapXml/TypeInstruction.cs b/src/IKVM.Tools.Importer/MapXml/TypeInstruction.cs similarity index 78% rename from src/ikvmc/IKVM/Internal/MapXml/TypeInstruction.cs rename to src/IKVM.Tools.Importer/MapXml/TypeInstruction.cs index 099e879455..3a04670656 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/TypeInstruction.cs +++ b/src/IKVM.Tools.Importer/MapXml/TypeInstruction.cs @@ -23,21 +23,28 @@ Jeroen Frijters */ using System.Diagnostics; -using System.Xml.Serialization; +using System.Xml.Linq; +using IKVM.Internal; using IKVM.Reflection.Emit; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + public abstract class TypeInstruction : Instruction { - [XmlAttribute("type")] - public string type; - private OpCode opcode; - private Type typeType; + public static void Load(TypeInstruction inst, XElement element) + { + inst.Type = (string)element.Attribute("type"); + } + + readonly OpCode opcode; + Type typeType; + + public string Type { get; set; } internal TypeInstruction(OpCode opcode) { @@ -48,10 +55,13 @@ internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { if (typeType == null) { - Debug.Assert(type != null); - typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, type); + Debug.Assert(Type != null); + typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type); } + ilgen.Emit(opcode, typeType); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/TypeOrTypeWrapperInstruction.cs b/src/IKVM.Tools.Importer/MapXml/TypeOrTypeWrapperInstruction.cs similarity index 67% rename from src/ikvmc/IKVM/Internal/MapXml/TypeOrTypeWrapperInstruction.cs rename to src/IKVM.Tools.Importer/MapXml/TypeOrTypeWrapperInstruction.cs index 9f352511eb..ec7583e744 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/TypeOrTypeWrapperInstruction.cs +++ b/src/IKVM.Tools.Importer/MapXml/TypeOrTypeWrapperInstruction.cs @@ -23,36 +23,54 @@ Jeroen Frijters */ using System.Diagnostics; -using System.Xml.Serialization; +using System.Xml.Linq; + +using IKVM.Internal; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { + public abstract class TypeOrTypeWrapperInstruction : Instruction { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("type")] - public string type; + + /// + /// Loads the XML element into a instance. + /// + /// + /// + /// + public static void Load(TypeOrTypeWrapperInstruction inst, XElement element) + { + Load((Instruction)inst, element); + inst.Class = (string)element.Attribute("class"); + inst.Type = (string)element.Attribute("type"); + } internal TypeWrapper typeWrapper; internal Type typeType; + public string Class { get; set; } + + public string Type { get; set; } + internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { if (typeWrapper == null && typeType == null) { - Debug.Assert(Class == null ^ type == null); + Debug.Assert(Class == null ^ Type == null); if (Class != null) { typeWrapper = context.ClassLoader.LoadClassByDottedName(Class); } else { - typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, type); + typeType = StaticCompiler.GetTypeForMapXml(context.ClassLoader, Type); } } } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Unaligned.cs b/src/IKVM.Tools.Importer/MapXml/Unaligned.cs similarity index 55% rename from src/ikvmc/IKVM/Internal/MapXml/Unaligned.cs rename to src/IKVM.Tools.Importer/MapXml/Unaligned.cs index aa5471f1a8..1e84c35c1b 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Unaligned.cs +++ b/src/IKVM.Tools.Importer/MapXml/Unaligned.cs @@ -22,19 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; -namespace IKVM.Internal.MapXml +using IKVM.Internal; + +namespace IKVM.Tools.Importer.MapXml { - [XmlType("unaligned")] + + [Instruction("unaligned")] public sealed class Unaligned : Instruction { - [XmlAttribute("alignment")] - public int Alignment; + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Unaligned Read(XElement element) + { + var inst = new Unaligned(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Unaligned inst, XElement element) + { + Load((Instruction)inst, element); + inst.Alignment = (int)element.Attribute("alignment"); + } + + public int Alignment { get; set; } internal override void Generate(CodeGenContext context, CodeEmitter ilgen) { ilgen.EmitUnaligned((byte)Alignment); } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Unbox.cs b/src/IKVM.Tools.Importer/MapXml/Unbox.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Unbox.cs rename to src/IKVM.Tools.Importer/MapXml/Unbox.cs index 0ee1b55480..cdf4c43bc9 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Unbox.cs +++ b/src/IKVM.Tools.Importer/MapXml/Unbox.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("unbox")] + + [Instruction("unbox")] public sealed class Unbox : TypeInstruction { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Unbox Read(XElement element) + { + var inst = new Unbox(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Unbox inst, XElement element) + { + Load((TypeInstruction)inst, element); + } + + /// + /// Initializes a new instance. + /// public Unbox() : base(OpCodes.Unbox) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Volatile.cs b/src/IKVM.Tools.Importer/MapXml/Volatile.cs similarity index 56% rename from src/ikvmc/IKVM/Internal/MapXml/Volatile.cs rename to src/IKVM.Tools.Importer/MapXml/Volatile.cs index 5793a26cac..e27751f585 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Volatile.cs +++ b/src/IKVM.Tools.Importer/MapXml/Volatile.cs @@ -22,17 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("volatile")] + + [Instruction("volatile")] public sealed class Volatile : Simple { + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Volatile Read(XElement element) + { + var inst = new Volatile(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Volatile inst, XElement element) + { + Load((Simple)inst, element); + } + + /// + /// Initializes a new instance. + /// public Volatile() : base(OpCodes.Volatile) { + } + } + } diff --git a/src/ikvmc/IKVM/Internal/MapXml/Xor.cs b/src/IKVM.Tools.Importer/MapXml/Xor.cs similarity index 54% rename from src/ikvmc/IKVM/Internal/MapXml/Xor.cs rename to src/IKVM.Tools.Importer/MapXml/Xor.cs index 9a16179056..07087346a9 100644 --- a/src/ikvmc/IKVM/Internal/MapXml/Xor.cs +++ b/src/IKVM.Tools.Importer/MapXml/Xor.cs @@ -22,18 +22,47 @@ Jeroen Frijters */ -using System.Xml.Serialization; +using System.Xml.Linq; using IKVM.Reflection.Emit; -namespace IKVM.Internal.MapXml +namespace IKVM.Tools.Importer.MapXml { - [XmlType("xor")] + + [Instruction("xor")] public sealed class Xor : Simple { - public Xor() - : base(OpCodes.Xor) + + /// + /// Reads the XML element into a new instance. + /// + /// + /// + public static new Xor Read(XElement element) + { + var inst = new Xor(); + Load(inst, element); + return inst; + } + + /// + /// Loads the XML element into the instruction. + /// + /// + /// + public static void Load(Xor inst, XElement element) { + Load((Simple)inst, element); } + + /// + /// Initializes a new instance. + /// + public Xor() : base(OpCodes.Xor) + { + + } + } + } diff --git a/src/ikvmc/IKVM/Internal/Proxy.cs b/src/IKVM.Tools.Importer/Proxy.cs similarity index 99% rename from src/ikvmc/IKVM/Internal/Proxy.cs rename to src/IKVM.Tools.Importer/Proxy.cs index 4684bdf483..aec88fba97 100644 --- a/src/ikvmc/IKVM/Internal/Proxy.cs +++ b/src/IKVM.Tools.Importer/Proxy.cs @@ -25,15 +25,19 @@ Jeroen Frijters using System.Collections.Generic; using IKVM.Attributes; +using IKVM.Internal; using IKVM.Reflection; using IKVM.Reflection.Emit; +using IKVM.Runtime; using Type = IKVM.Reflection.Type; -namespace IKVM.Internal +namespace IKVM.Tools.Importer { - static class ProxyGenerator + + static class ProxyGenerator { + private static readonly TypeWrapper proxyClass; private static readonly TypeWrapper errorClass; private static readonly TypeWrapper runtimeExceptionClass; diff --git a/src/IKVM.Tools.Runner/Compiler/IkvmCompilerLauncher.cs b/src/IKVM.Tools.Runner/Compiler/IkvmCompilerLauncher.cs index 3b9c13e761..92cd647b62 100644 --- a/src/IKVM.Tools.Runner/Compiler/IkvmCompilerLauncher.cs +++ b/src/IKVM.Tools.Runner/Compiler/IkvmCompilerLauncher.cs @@ -243,6 +243,9 @@ public async Task ExecuteAsync(IkvmCompilerOptions options, CancellationTok if (options.Remap is not null) w.WriteLine($"-remap:{options.Remap}"); + if (options.NoLogo) + w.WriteLine($"-nologo"); + if (options.Input != null) foreach (var i in options.Input) w.WriteLine(i); @@ -282,11 +285,9 @@ public async Task ExecuteAsync(IkvmCompilerOptions options, CancellationTok // configure CLI var cli = Cli.Wrap(exe).WithWorkingDirectory(Environment.CurrentDirectory); - var args = new List(); // execute the contents of the response file - args.Add($"@{response}"); - cli = cli.WithArguments(args); + cli = cli.WithArguments(new[] { $"@{response}" }); cli = cli.WithValidation(CommandResultValidation.None); await LogEvent(IkvmToolDiagnosticEventLevel.Debug, "Executing {0} {1}", cli.TargetFilePath, cli.Arguments); diff --git a/src/IKVM.Tools.Runner/Compiler/IkvmCompilerOptions.cs b/src/IKVM.Tools.Runner/Compiler/IkvmCompilerOptions.cs index 8ea8cbfc5d..91a0c4b0c4 100644 --- a/src/IKVM.Tools.Runner/Compiler/IkvmCompilerOptions.cs +++ b/src/IKVM.Tools.Runner/Compiler/IkvmCompilerOptions.cs @@ -169,6 +169,8 @@ public class IkvmCompilerOptions public string Remap { get; set; } + public bool NoLogo { get; set; } + } } diff --git a/src/IKVM.Tools.Runner/Exporter/IkvmExporterLauncher.cs b/src/IKVM.Tools.Runner/Exporter/IkvmExporterLauncher.cs index 3689169762..2583f1e10d 100644 --- a/src/IKVM.Tools.Runner/Exporter/IkvmExporterLauncher.cs +++ b/src/IKVM.Tools.Runner/Exporter/IkvmExporterLauncher.cs @@ -68,40 +68,46 @@ public async Task ExecuteAsync(IkvmExporterOptions options, CancellationTok var args = new List(); if (options.Output is not null) - args.Add($"-out:{options.Output}"); + args.Add($"--out:{options.Output}"); if (options.References is not null) foreach (var reference in options.References) - args.Add($"-reference:{reference}"); + args.Add($"--reference:{reference}"); if (options.Namespaces is not null) foreach (var ns in options.Namespaces) - args.Add($"-ns:{ns}"); - - if (options.SkipError) - args.Add("-skiperror"); + args.Add($"--ns:{ns}"); if (options.Shared) - args.Add("-shared"); + args.Add("--shared"); if (options.NoStdLib) - args.Add("-nostdlib"); + args.Add("--nostdlib"); if (options.Forwarders) - args.Add("-forwarders"); + args.Add("--forwarders"); + + if (options.IncludeNonPublicTypes) + args.Add("--non-public-types"); - if (options.Parameters) - args.Add("-parameters"); + if (options.IncludeNonPublicInterfaces) + args.Add("--non-public-interfaces"); - if (options.JApi) - args.Add("-japi"); + if (options.IncludeNonPublicMembers) + args.Add("--non-public-members"); + + if (options.IncludeParameterNames) + args.Add("--parameters"); if (options.Bootstrap) - args.Add("-bootstrap"); + args.Add("--bootstrap"); if (options.Lib is not null) foreach (var i in options.Lib) - args.Add($"-lib:{i}"); + args.Add($"--lib:{i}"); + + if (options.ContinueOnError) + args.Add("--skiperror"); if (options.Input is not null) args.Add(options.Input); diff --git a/src/IKVM.Tools.Runner/Exporter/IkvmExporterOptions.cs b/src/IKVM.Tools.Runner/Exporter/IkvmExporterOptions.cs index 337409174a..92fa7c9ca2 100644 --- a/src/IKVM.Tools.Runner/Exporter/IkvmExporterOptions.cs +++ b/src/IKVM.Tools.Runner/Exporter/IkvmExporterOptions.cs @@ -35,45 +35,55 @@ public class IkvmExporterOptions public List Namespaces { get; } = new List(); /// - /// Continue when errors are encountered. + /// Additional directories to search for references. /// - public bool SkipError { get; set; } + public IList Lib { get; set; } = new List(); /// - /// Process all assemblies in shared group. + /// Whether to emit non-public types. /// - public bool Shared { get; set; } + public bool IncludeNonPublicTypes { get; set; } /// - /// Do not reference standard libraries. + /// Whether to emit non-public interface implementations. /// - public bool NoStdLib { get; set; } + public bool IncludeNonPublicInterfaces { get; set; } /// - /// Additional directories to search for references. + /// Whether to emit non-public members. /// - public IList Lib { get; set; } = new List(); + public bool IncludeNonPublicMembers { get; set; } /// - /// Export forwarded types too. + /// Emit Java 8 classes with parameter names. /// - public bool Forwarders { get; set; } + public bool IncludeParameterNames { get; set; } /// - /// Emit Java 8 classes with parameter names. + /// Process all assemblies in shared group. /// - public bool Parameters { get; set; } + public bool Shared { get; set; } /// - /// Generate jar suitable for comparison with japitools. + /// Do not reference standard libraries. /// - public bool JApi { get; set; } + public bool NoStdLib { get; set; } + + /// + /// Export forwarded types too. + /// + public bool Forwarders { get; set; } /// /// Run in bootstrap mode. /// public bool Bootstrap { get; set; } + /// + /// Continue when errors are encountered. + /// + public bool ContinueOnError { get; set; } + } } diff --git a/src/IKVM.Tools.Runner/IKVM.Tools.Runner.csproj b/src/IKVM.Tools.Runner/IKVM.Tools.Runner.csproj index ec626b04c8..acc56adee5 100644 --- a/src/IKVM.Tools.Runner/IKVM.Tools.Runner.csproj +++ b/src/IKVM.Tools.Runner/IKVM.Tools.Runner.csproj @@ -1,6 +1,7 @@  net461;netcoreapp3.1 + true diff --git a/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj b/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj index 578c01c775..6805c372ac 100644 --- a/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj +++ b/src/IKVM.Tools.Tests/IKVM.Tools.Tests.csproj @@ -42,39 +42,50 @@ - + TargetFramework=net461 RuntimeIdentifier=win7-x64 ikvmc\net461\win7-x64 - + TargetFramework=net461 RuntimeIdentifier=win7-x64 ikvmstub\net461\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=win7-x64 ikvmc\netcoreapp3.1\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=win7-x64 ikvmstub\netcoreapp3.1\win7-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-x64 ikvmc\netcoreapp3.1\linux-x64 - + TargetFramework=netcoreapp3.1 RuntimeIdentifier=linux-x64 ikvmstub\netcoreapp3.1\linux-x64 + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmc\netcoreapp3.1\osx-x64 + + + TargetFramework=netcoreapp3.1 + RuntimeIdentifier=osx-x64 + ikvmstub\netcoreapp3.1\osx-x64 + + \ No newline at end of file diff --git a/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs b/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs index 2e72c695ed..69b15b19ab 100644 --- a/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs +++ b/src/IKVM.Tools.Tests/Runner/Compiler/IkvmCompilerLauncherTests.cs @@ -33,6 +33,8 @@ async Task CompileJar(string tfm) rid = "win7-x64"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) rid = "linux-x64"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + rid = "osx-x64"; var e = new List(); var l = new IkvmCompilerLauncher(Path.Combine(Path.GetDirectoryName(typeof(IkvmCompilerLauncherTests).Assembly.Location), "ikvmc", tfm, rid), new IkvmToolDelegateDiagnosticListener(evt => { e.Add(evt); TestContext.WriteLine(evt.Message, evt.MessageArgs); })); diff --git a/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs b/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs index 96648ac741..dc717ebb41 100644 --- a/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs +++ b/src/IKVM.Tools.Tests/Runner/Exporter/IkvmExporterLauncherTests.cs @@ -35,6 +35,8 @@ async Task Can_export_dll(string tfm) rid = "win7-x64"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) rid = "linux-x64"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + rid = "osx-x64"; var e = new List(); var l = new IkvmExporterLauncher(Path.Combine(Path.GetDirectoryName(typeof(IkvmExporterLauncherTests).Assembly.Location), "ikvmstub", tfm, rid), new IkvmToolDelegateDiagnosticListener(evt => { e.Add(evt); TestContext.WriteLine(evt.Message, evt.MessageArgs); })); diff --git a/src/IKVM.Util/IKVM.Util.csproj b/src/IKVM.Util/IKVM.Util.csproj index ff0a9f18d1..8668ca5519 100644 --- a/src/IKVM.Util/IKVM.Util.csproj +++ b/src/IKVM.Util/IKVM.Util.csproj @@ -1,6 +1,7 @@ net461;netcoreapp3.1 + true @@ -9,4 +10,8 @@ + + + + diff --git a/src/IKVM.Util/Jar/JarFile.cs b/src/IKVM.Util/Jar/JarFile.cs index dfda10e058..cc21dbbfaf 100644 --- a/src/IKVM.Util/Jar/JarFile.cs +++ b/src/IKVM.Util/Jar/JarFile.cs @@ -48,6 +48,16 @@ public JarFile(string path) : } + /// + /// Returns the entry with the given name. + /// + /// + /// + public ZipArchiveEntry GetEntry(string entryName) + { + return archive.GetEntry(entryName); + } + /// /// Returns the jar file manifest, or null if none. /// diff --git a/src/IKVM.Util/Jar/JarFileExtensions.cs b/src/IKVM.Util/Jar/JarFileExtensions.cs index 6942a12cae..876a5db536 100644 --- a/src/IKVM.Util/Jar/JarFileExtensions.cs +++ b/src/IKVM.Util/Jar/JarFileExtensions.cs @@ -1,5 +1,9 @@ using System; +using IKVM.ByteCode; +using IKVM.ByteCode.Reading; +using IKVM.Util.Modules; + using static IKVM.Util.Jar.JarFileUtil; namespace IKVM.Util.Jar @@ -21,20 +25,39 @@ public static ModuleInfo GetModuleInfo(this JarFile jar) if (jar is null) throw new ArgumentNullException(nameof(jar)); - return jar.Manifest is Manifest m ? GetModuleInfoFromManifest(m) : null; + return GetModuleInfoFromClass(jar) ?? GetModuleInfoFromManifest(jar); + } + + /// + /// Attempts to get the module name of the JAR file from the module-info.class file. + /// + /// + /// + static ModuleInfo GetModuleInfoFromClass(JarFile jar) + { + var e = jar.GetEntry("module-info.class"); + if (e == null) + return null; + + using var s = e.Open(); + var c = ClassReader.Read(s); + if ((c.AccessFlags & AccessFlag.ACC_MODULE) != 0 && c.Attributes.Module != null) + return new ModuleInfo(c.Attributes.Module.Name.Name.Value, c.Attributes.Module.Version != null && ModuleVersion.TryParse(c.Attributes.Module.Version.Value.AsSpan(), out var version) ? version : null); + + return null; } /// /// Attempts to get the module name of the JAR file from the manifest. /// - /// + /// /// - static ModuleInfo GetModuleInfoFromManifest(Manifest manifest) + static ModuleInfo GetModuleInfoFromManifest(JarFile jar) { - if (manifest is null) - throw new ArgumentNullException(nameof(manifest)); + if (jar.Manifest is null) + return null; - return new ModuleInfo(manifest.MainAttributes.TryGetValue("Automatic-Module-Name", out var name) ? name : null, null); + return new ModuleInfo(jar.Manifest.MainAttributes.TryGetValue("Automatic-Module-Name", out var name) ? name : null, null); } } diff --git a/src/IKVM/IKVM.csproj b/src/IKVM/IKVM.csproj index 96a91c5ac5..d1fcb2f6c3 100644 --- a/src/IKVM/IKVM.csproj +++ b/src/IKVM/IKVM.csproj @@ -1,6 +1,6 @@  - - + + net461;netcoreapp3.1 diff --git a/src/IKVM/buildTransitive/netstandard2.0/IKVM.NoTasks.targets b/src/IKVM/buildTransitive/netstandard2.0/IKVM.NoTasks.targets index b247f9f1be..87fd5c8f5c 100644 --- a/src/IKVM/buildTransitive/netstandard2.0/IKVM.NoTasks.targets +++ b/src/IKVM/buildTransitive/netstandard2.0/IKVM.NoTasks.targets @@ -48,7 +48,7 @@ $([MSBuild]::Unescape('$(___IkvmReferenceItemPrepareCs)')) <_IkvmCompilerArgs Include="-assembly:%(IkvmReferenceItem.AssemblyName)" /> <_IkvmCompilerArgs Include="-version:%(IkvmReferenceItem.AssemblyVersion)" Condition=" '%(IkvmReferenceItem.AssemblyVersion)' != '' " /> <_IkvmCompilerArgs Include="-fileversion:%(IkvmReferenceItem.AssemblyFileVersion)" Condition=" '%(IkvmReferenceItem.AssemblyFileVersion)' != '' " /> - <_IkvmCompilerArgs Include="-runtime:$(IkvmRuntimeAssembly)" /> + <_IkvmCompilerArgs Include="-runtime:$([System.IO.Path]::GetFullPath('$(IkvmRuntimeAssembly)'))" /> <_IkvmCompilerArgs Include="-keyfile:%(IkvmReferenceItem.KeyFile)" Condition=" '%(IkvmReferenceItem.KeyFile)' != '' " /> <_IkvmCompilerArgs Include="-delaysign:%(IkvmReferenceItem.DelaySign)" Condition=" '%(IkvmReferenceItem.DelaySign)' == 'true' " /> <_IkvmCompilerArgs Include="-compressresources" /> @@ -89,4 +89,4 @@ $([MSBuild]::Unescape('$(___IkvmReferenceItemPrepareCs)')) - \ No newline at end of file + diff --git a/src/IKVM/buildTransitive/netstandard2.0/IKVM.Tasks.targets b/src/IKVM/buildTransitive/netstandard2.0/IKVM.Tasks.targets index ae2a5c7286..aa600d45af 100644 --- a/src/IKVM/buildTransitive/netstandard2.0/IKVM.Tasks.targets +++ b/src/IKVM/buildTransitive/netstandard2.0/IKVM.Tasks.targets @@ -21,8 +21,10 @@ + <_IkvmCompilerReferencePath Remove="@(_IkvmCompilerReferencePath)" /> <_IkvmReferenceItemResolvedReference Remove="@(_IkvmReferenceItemResolvedReference)" /> <_IkvmReferenceItemResolvedReference Include="%(IkvmReferenceItem.ResolvedReferences)" /> + <_IkvmCompilerReferencePath Include="@(IkvmFrameworkReference);$(IkvmBaseAssembly);$(IkvmRuntimeAssembly);$(IkvmRuntimeJNIAssembly);@(_IkvmReferenceItemResolvedReference)" /> @@ -36,7 +38,7 @@ Assembly="%(IkvmReferenceItem.AssemblyName)" Version="%(IkvmReferenceItem.AssemblyVersion)" FileVersion="%(IkvmReferenceItem.AssemblyFileVersion)" - Runtime="$(IkvmRuntimeAssembly)" + Runtime="$([System.IO.Path]::GetFullPath('$(IkvmRuntimeAssembly)'))" Target="library" Debug="%(IkvmReferenceItem.Debug)" KeyFile="%(IkvmReferenceItem.KeyFile)" @@ -44,7 +46,7 @@ CompressResources="true" ClassLoader="%(IkvmReferenceItem.ClassLoader)" NoStdLib="true" - References="@(IkvmFrameworkReference);$(IkvmBaseAssembly);$(IkvmRuntimeAssembly);$(IkvmRuntimeJNIAssembly);@(_IkvmReferenceItemResolvedReference)" + References="@(_IkvmCompilerReferencePath->'%(FullPath)')" Input="%(IkvmReferenceItem.Compile)" Condition=" '%(IkvmReferenceItem.Compile)' != '' "/> diff --git a/src/IKVM/buildTransitive/netstandard2.0/IKVM.targets b/src/IKVM/buildTransitive/netstandard2.0/IKVM.targets index 43fbc2361b..d11854f191 100644 --- a/src/IKVM/buildTransitive/netstandard2.0/IKVM.targets +++ b/src/IKVM/buildTransitive/netstandard2.0/IKVM.targets @@ -3,15 +3,6 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - PreserveNewest - runtimes\%(RecursiveDir)%(FileName)%(Extension) - false - - - diff --git a/src/dist-image/dist-image.csproj b/src/dist-image/dist-image.csproj index dcd4a30b57..eb46f19ac2 100644 --- a/src/dist-image/dist-image.csproj +++ b/src/dist-image/dist-image.csproj @@ -12,38 +12,45 @@ + + + + + + + + <_ContentFromProject Include="ikvm\**\*" /> + <_ContentFromSolution Include="..\..\README.md;..\..\LICENSE.md;..\..\THIRD_PARTY_README;..\..\TRADEMARK" /> - + <_TargetFramework>%(ImageTarget.TargetFramework) <_RuntimeIdentifier>%(ImageTarget.RuntimeIdentifier) - + - <_ContentFromProject Include="content\**\*" /> PreserveNewest - $(_TargetFramework)\$(_RuntimeIdentifier)\%(RecursiveDir)%(Filename)%(Extension) + $(_TargetFramework)\$(_RuntimeIdentifier)\%(_ContentFromProject.RecursiveDir)%(_ContentFromProject.Filename)%(_ContentFromProject.Extension) - <_ContentFromSolution Include="..\..\README.md;..\..\LICENSE.md;..\..\THIRD_PARTY_README;..\..\TRADEMARK" /> PreserveNewest - $(_TargetFramework)\$(_RuntimeIdentifier)\%(RecursiveDir)%(Filename)%(Extension) + $(_TargetFramework)\$(_RuntimeIdentifier)\%(_ContentFromSolution.RecursiveDir)%(_ContentFromSolution.Filename)%(_ContentFromSolution.Extension) diff --git a/src/dist-image/ikvm/bin/ikvm.properties b/src/dist-image/ikvm/bin/ikvm.properties new file mode 100644 index 0000000000..8473a67ad4 --- /dev/null +++ b/src/dist-image/ikvm/bin/ikvm.properties @@ -0,0 +1 @@ +ikvm.home=.. diff --git a/src/dist-image/ikvm/lib/security/java.security b/src/dist-image/ikvm/lib/security/java.security new file mode 100644 index 0000000000..602ef68389 --- /dev/null +++ b/src/dist-image/ikvm/lib/security/java.security @@ -0,0 +1,569 @@ +# +# This is the "master security properties file". +# +# An alternate java.security properties file may be specified +# from the command line via the system property +# +# -Djava.security.properties= +# +# This properties file appends to the master security properties file. +# If both properties files specify values for the same key, the value +# from the command-line properties file is selected, as it is the last +# one loaded. +# +# Also, if you specify +# +# -Djava.security.properties== (2 equals), +# +# then that properties file completely overrides the master security +# properties file. +# +# To disable the ability to specify an additional properties file from +# the command line, set the key security.overridePropertiesFile +# to false in the master security properties file. It is set to true +# by default. + +# In this file, various security properties are set for use by +# java.security classes. This is where users can statically register +# Cryptography Package Providers ("providers" for short). The term +# "provider" refers to a package or set of packages that supply a +# concrete implementation of a subset of the cryptography aspects of +# the Java Security API. A provider may, for example, implement one or +# more digital signature algorithms or message digest algorithms. +# +# Each provider must implement a subclass of the Provider class. +# To register a provider in this master security properties file, +# specify the Provider subclass name and priority in the format +# +# security.provider.= +# +# This declares a provider, and specifies its preference +# order n. The preference order is the order in which providers are +# searched for requested algorithms (when no specific provider is +# requested). The order is 1-based; 1 is the most preferred, followed +# by 2, and so on. +# +# must specify the subclass of the Provider class whose +# constructor sets the values of various properties that are required +# for the Java Security API to look up the algorithms or other +# facilities implemented by the provider. +# +# There must be at least one provider specification in java.security. +# There is a default provider that comes standard with the JDK. It +# is called the "SUN" provider, and its Provider subclass +# named Sun appears in the sun.security.provider package. Thus, the +# "SUN" provider is registered via the following: +# +# security.provider.1=sun.security.provider.Sun +# +# (The number 1 is used for the default provider.) +# +# Note: Providers can be dynamically registered instead by calls to +# either the addProvider or insertProviderAt method in the Security +# class. + +# +# List of providers and their preference orders (see above): +# +security.provider.1=sun.security.provider.Sun +security.provider.2=sun.security.rsa.SunRsaSign +security.provider.3=sun.security.ec.SunEC +security.provider.4=com.sun.net.ssl.internal.ssl.Provider +security.provider.5=com.sun.crypto.provider.SunJCE +security.provider.6=sun.security.jgss.SunProvider +security.provider.7=com.sun.security.sasl.Provider +security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI +security.provider.9=sun.security.smartcardio.SunPCSC + +# +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. +# +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. +# +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass +# +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. +# +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNG:SUN + +# +# Class to instantiate as the javax.security.auth.login.Configuration +# provider. +# +login.configuration.provider=sun.security.provider.ConfigFile + +# +# Default login configuration file +# +#login.config.url.1=file:${user.home}/.java.login.config + +# +# Class to instantiate as the system Policy. This is the name of the class +# that will be used as the Policy object. +# +policy.provider=sun.security.provider.PolicyFile + +# The default is to have a single system-wide policy file, +# and a policy file in the user's home directory. +policy.url.1=file:${java.home}/lib/security/java.policy +policy.url.2=file:${user.home}/.java.policy + +# whether or not we expand properties in the policy file +# if this is set to false, properties (${...}) will not be expanded in policy +# files. +policy.expandProperties=true + +# whether or not we allow an extra policy to be passed on the command line +# with -Djava.security.policy=somefile. Comment out this line to disable +# this feature. +policy.allowSystemProperty=true + +# whether or not we look into the IdentityScope for trusted Identities +# when encountering a 1.1 signed JAR file. If the identity is found +# and is trusted, we grant it AllPermission. +policy.ignoreIdentityScope=false + +# +# Default keystore type. +# +keystore.type=jks + +# +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, none of the class loaders supplied with the JDK call +# checkPackageDefinition. +# +package.definition=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + com.sun.activation.registries. + +# +# Determines whether this properties file can be appended to +# or overridden on the command line via -Djava.security.properties +# +security.overridePropertiesFile=true + +# +# Determines the default key and trust manager factory algorithms for +# the javax.net.ssl package. +# +ssl.KeyManagerFactory.algorithm=SunX509 +ssl.TrustManagerFactory.algorithm=PKIX + +# +# The Java-level namelookup cache policy for successful lookups: +# +# any negative value: caching forever +# any positive value: the number of seconds to cache an address for +# zero: do not cache +# +# default value is forever (FOREVER). For security reasons, this +# caching is made forever when a security manager is set. When a security +# manager is not set, the default behavior in this implementation +# is to cache for 30 seconds. +# +# NOTE: setting this to anything other than the default value can have +# serious security implications. Do not set it unless +# you are sure you are not exposed to DNS spoofing attack. +# +#networkaddress.cache.ttl=-1 + +# The Java-level namelookup cache policy for failed lookups: +# +# any negative value: cache forever +# any positive value: the number of seconds to cache negative lookup results +# zero: do not cache +# +# In some Microsoft Windows networking environments that employ +# the WINS name service in addition to DNS, name service lookups +# that fail may take a noticeably long time to return (approx. 5 seconds). +# For this reason the default caching policy is to maintain these +# results for 10 seconds. +# +# +networkaddress.cache.negative.ttl=10 + +# +# Properties to configure OCSP for certificate revocation checking +# + +# Enable OCSP +# +# By default, OCSP is not used for certificate revocation checking. +# This property enables the use of OCSP when set to the value "true". +# +# NOTE: SocketPermission is required to connect to an OCSP responder. +# +# Example, +# ocsp.enable=true + +# +# Location of the OCSP responder +# +# By default, the location of the OCSP responder is determined implicitly +# from the certificate being validated. This property explicitly specifies +# the location of the OCSP responder. The property is used when the +# Authority Information Access extension (defined in RFC 3280) is absent +# from the certificate or when it requires overriding. +# +# Example, +# ocsp.responderURL=http://ocsp.example.net:80 + +# +# Subject name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. In cases where +# the subject name alone is not sufficient to uniquely identify the certificate +# then both the "ocsp.responderCertIssuerName" and +# "ocsp.responderCertSerialNumber" properties must be used instead. When this +# property is set then those two properties are ignored. +# +# Example, +# ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp" + +# +# Issuer name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. When this +# property is set then the "ocsp.responderCertSerialNumber" property must also +# be set. When the "ocsp.responderCertSubjectName" property is set then this +# property is ignored. +# +# Example, +# ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp" + +# +# Serial number of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# of hexadecimal digits (colon or space separators may be present) which +# identifies a certificate in the set of certificates supplied during cert path +# validation. When this property is set then the "ocsp.responderCertIssuerName" +# property must also be set. When the "ocsp.responderCertSubjectName" property +# is set then this property is ignored. +# +# Example, +# ocsp.responderCertSerialNumber=2A:FF:00 + +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + +# Algorithm restrictions for certification path (CertPath) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# for certification path building and validation. For example, "MD2" is +# generally no longer considered to be a secure hash algorithm. This section +# describes the mechanism for disabling algorithms based on algorithm name +# and/or key length. This includes algorithms used in certificates, as well +# as revocation information such as CRLs and signed OCSP Responses. +# +# The syntax of the disabled algorithm string is described as this Java +# BNF-style: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint +# +# KeySizeConstraint: +# keySize Operator DecimalInteger +# +# Operator: +# <= | < | == | != | >= | > +# +# DecimalInteger: +# DecimalDigits +# +# DecimalDigits: +# DecimalDigit {DecimalDigit} +# +# DecimalDigit: one of +# 1 2 3 4 5 6 7 8 9 0 +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# algorithm. See "Java Cryptography Architecture Standard Algorithm Name +# Documentation" for information about Standard Algorithm Names. Matching +# is performed using a case-insensitive sub-element matching rule. (For +# example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and +# "ECDSA" for signatures.) If the assertion "AlgorithmName" is a +# sub-element of the certificate algorithm name, the algorithm will be +# rejected during certification path building and validation. For example, +# the assertion algorithm name "DSA" will disable all certificate algorithms +# that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion +# will not disable algorithms related to "ECDSA". +# +# A "Constraint" provides further guidance for the algorithm being specified. +# The "KeySizeConstraint" requires a key of a valid size range if the +# "AlgorithmName" is of a key algorithm. The "DecimalInteger" indicates the +# key size specified in number of bits. For example, "RSA keySize <= 1024" +# indicates that any RSA key with key size less than or equal to 1024 bits +# should be disabled, and "RSA keySize < 1024, RSA keySize > 2048" indicates +# that any RSA key with key size less than 1024 or greater than 2048 should +# be disabled. Note that the "KeySizeConstraint" only makes sense to key +# algorithms. +# +# Note: This property is currently used by Oracle's PKIX implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 +# +# +jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024 + +# Algorithm restrictions for Secure Socket Layer/Transport Layer Security +# (SSL/TLS) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# when using SSL/TLS. This section describes the mechanism for disabling +# algorithms during SSL/TLS security parameters negotiation, including +# protocol version negotiation, cipher suites selection, peer authentication +# and key exchange mechanisms. +# +# Disabled algorithms will not be negotiated for SSL/TLS connections, even +# if they are enabled explicitly in an application. +# +# For PKI-based peer authentication and key exchange mechanisms, this list +# of disabled algorithms will also be checked during certification path +# building and validation, including algorithms used in certificates, as +# well as revocation information such as CRLs and signed OCSP Responses. +# This is in addition to the jdk.certpath.disabledAlgorithms property above. +# +# See the specification of "jdk.certpath.disabledAlgorithms" for the +# syntax of the disabled algorithm string. +# +# Note: This property is currently used by Oracle's JSSE implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 +jdk.tls.disabledAlgorithms=SSLv3, RC4, DH keySize < 768 + +# Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) +# processing in JSSE implementation. +# +# In some environments, a certain algorithm may be undesirable but it +# cannot be disabled because of its use in legacy applications. Legacy +# algorithms may still be supported, but applications should not use them +# as the security strength of legacy algorithms are usually not strong enough +# in practice. +# +# During SSL/TLS security parameters negotiation, legacy algorithms will +# not be negotiated unless there are no other candidates. +# +# The syntax of the disabled algorithm string is described as this Java +# BNF-style: +# LegacyAlgorithms: +# " LegacyAlgorithm { , LegacyAlgorithm } " +# +# LegacyAlgorithm: +# AlgorithmName (standard JSSE algorithm name) +# +# See the specification of security property "jdk.certpath.disabledAlgorithms" +# for the syntax and description of the "AlgorithmName" notation. +# +# Per SSL/TLS specifications, cipher suites have the form: +# SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# or +# TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# +# For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the +# key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC +# mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest +# algorithm for HMAC. +# +# The LegacyAlgorithm can be one of the following standard algorithm names: +# 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA +# 2. JSSE key exchange algorithm name, e.g., RSA +# 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC +# 4. JSSE message digest algorithm name, e.g., SHA +# +# See SSL/TLS specifications and "Java Cryptography Architecture Standard +# Algorithm Name Documentation" for information about the algorithm names. +# +# Note: This property is currently used by Oracle's JSSE implementation. +# It is not guaranteed to be examined and used by other implementations. +# There is no guarantee the property will continue to exist or be of the +# same syntax in future releases. +# +# Example: +# jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 +# +jdk.tls.legacyAlgorithms= \ + K_NULL, C_NULL, M_NULL, \ + DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, \ + DH_RSA_EXPORT, RSA_EXPORT, \ + DH_anon, ECDH_anon, \ + RC4_128, RC4_40, DES_CBC, DES40_CBC diff --git a/src/dist-nuget/dist-nuget.csproj b/src/dist-nuget/dist-nuget.csproj index 41c531b713..afe9f9a9e5 100644 --- a/src/dist-nuget/dist-nuget.csproj +++ b/src/dist-nuget/dist-nuget.csproj @@ -24,6 +24,9 @@ . + + . + . @@ -54,6 +57,9 @@ . + + . + . @@ -75,6 +81,9 @@ . + + . + diff --git a/src/dist-tests/dist-tests.csproj b/src/dist-tests/dist-tests.csproj index 53a36e5737..57cb08b650 100644 --- a/src/dist-tests/dist-tests.csproj +++ b/src/dist-tests/dist-tests.csproj @@ -7,16 +7,19 @@ + + + - - + + diff --git a/src/dist-tools/dist-tools.csproj b/src/dist-tools/dist-tools.csproj index 5c61e66243..f91596b0c9 100644 --- a/src/dist-tools/dist-tools.csproj +++ b/src/dist-tools/dist-tools.csproj @@ -10,12 +10,16 @@ + + + - + + diff --git a/src/ikvm-native/ikvm-native-linux-arm.vcxproj b/src/ikvm-native/ikvm-native-linux-arm.vcxproj deleted file mode 100644 index bda98efedf..0000000000 --- a/src/ikvm-native/ikvm-native-linux-arm.vcxproj +++ /dev/null @@ -1,87 +0,0 @@ - - - - Debug - x86 - - - Release - x86 - - - - {8A321DFF-99AB-4279-828A-A9BA872010FD} - Linux - libikvm-native - 15.0 - Linux - 1.0 - Generic - {D51BCBC9-82E9-4017-911E-C93873C4EA2B} - ikvm-native-linux-arm - - - - true - WSL_1_0 - DynamicLibrary - - - false - WSL_1_0 - DynamicLibrary - - - - - - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-arm\ - $(ProjectDir)obj\$(Configuration)\linux-arm\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/arm-linux-gnueabi/include/ - default - .so - /usr/bin/arm-linux-gnueabihf-gcc - /usr/bin/arm-linux-gnueabihf-g++ - /usr/bin/arm-linux-gnueabihf-ld - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-arm\ - $(ProjectDir)obj\$(Configuration)\linux-arm\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/arm-linux-gnueabi/include/ - default - .so - /usr/bin/arm-linux-gnueabihf-gcc - /usr/bin/arm-linux-gnueabihf-g++ - /usr/bin/arm-linux-gnueabihf-ld - - - - false - false - - - false - - - - - false - false - - - false - - - - - - - - - - - \ No newline at end of file diff --git a/src/ikvm-native/ikvm-native-linux-arm64.vcxproj b/src/ikvm-native/ikvm-native-linux-arm64.vcxproj deleted file mode 100644 index 1934021192..0000000000 --- a/src/ikvm-native/ikvm-native-linux-arm64.vcxproj +++ /dev/null @@ -1,89 +0,0 @@ - - - - Debug - x64 - - - Release - x64 - - - - {BE117661-1954-42C3-8A16-3F89815F7222} - Linux - libikvm-native - 15.0 - Linux - 1.0 - Generic - {D51BCBC9-82E9-4017-911E-C93873C4EA2B} - ikvm-native-linux-arm64 - - - - true - WSL_1_0 - DynamicLibrary - - - false - WSL_1_0 - DynamicLibrary - - - - - - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-arm64\ - $(ProjectDir)obj\$(Configuration)\linux-arm64\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/aarch64-linux-gnu/include/ - false - default - .so - /usr/bin/aarch64-linux-gnu-gcc - /usr/bin/aarch64-linux-gnu-g++ - /usr/bin/aarch64-linux-gnu-ld - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-arm64\ - $(ProjectDir)obj\$(Configuration)\linux-arm64\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/aarch64-linux-gnu/include/ - false - default - .so - /usr/bin/aarch64-linux-gnu-gcc - /usr/bin/aarch64-linux-gnu-g++ - /usr/bin/aarch64-linux-gnu-ld - - - - false - false - - - false - - - - - false - false - - - false - - - - - - - - - - - \ No newline at end of file diff --git a/src/ikvm-native/ikvm-native-linux.vcxproj b/src/ikvm-native/ikvm-native-linux.vcxproj deleted file mode 100644 index ceb25cff85..0000000000 --- a/src/ikvm-native/ikvm-native-linux.vcxproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - Debug - x64 - - - Debug - x86 - - - Release - x64 - - - Release - x86 - - - - {F2A81A31-C65F-4B4E-A6FE-B14E71515EA3} - Linux - libikvm-native - 15.0 - Linux - 1.0 - Generic - {D51BCBC9-82E9-4017-911E-C93873C4EA2B} - ikvm-native-linux - - - - true - WSL_1_0 - DynamicLibrary - - - true - WSL_1_0 - DynamicLibrary - - - false - WSL_1_0 - DynamicLibrary - - - false - WSL_1_0 - DynamicLibrary - - - - - - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-x64\ - $(ProjectDir)obj\$(Configuration)\linux-x64\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/include;/usr/local/include - default - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-x86\ - $(ProjectDir)obj\$(Configuration)\linux-x86\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/include;/usr/local/include - default - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-x64\ - $(ProjectDir)obj\$(Configuration)\linux-x64\ - ../../openjdk/jdk/src/share/javavm/export/;../../openjdk/jdk/src/solaris/javavm/export/;/usr/include;/usr/local/include - default - - - libikvm-native - $(ProjectDir)bin\$(Configuration)\linux-x86\ - $(ProjectDir)obj\$(Configuration)\linux-x86\ - ../../openjdk/jdk/src/share/javavm/export/../../openjdk/jdk/src/solaris/javavm/export/;/usr/include;/usr/local/include - default - - - - - - - - - - \ No newline at end of file diff --git a/src/ikvm-native/ikvm-native-win.vcxproj b/src/ikvm-native/ikvm-native-win.vcxproj deleted file mode 100644 index 70a67d3385..0000000000 --- a/src/ikvm-native/ikvm-native-win.vcxproj +++ /dev/null @@ -1,223 +0,0 @@ - - - - Debug - x64 - - - Release - x64 - - - Debug - Win32 - - - Release - Win32 - - - Debug - ARM - - - Release - ARM - - - - 16.0 - Win32Proj - {D8B580D1-D12B-39CD-A42B-BAEE36602AA1} - ikvm-native - 10.0 - ikvm-native-win - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - $(ProjectDir)bin\$(Configuration)\win-x64\ - $(ProjectDir)obj\$(Configuration)\win-x64\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - $(ProjectDir)bin\$(Configuration)\win-x64\ - $(ProjectDir)obj\$(Configuration)\win-x64\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - $(ProjectDir)bin\$(Configuration)\win-x86\ - $(ProjectDir)obj\$(Configuration)\win-x86\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - $(ProjectDir)bin\$(Configuration)\win-x86\ - $(ProjectDir)obj\$(Configuration)\win-x86\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - $(ProjectDir)bin\$(Configuration)\win-arm\ - $(ProjectDir)obj\$(Configuration)\win-arm\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - $(ProjectDir)bin\$(Configuration)\win-arm\ - $(ProjectDir)obj\$(Configuration)\win-arm\ - ikvm-native - ..\..\openjdk\jdk\src\share\javavm\export;..\..\openjdk\jdk\src\windows\javavm\export;$(VC_IncludePath);$(WindowsSDK_IncludePath) - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_WIN64 - stdc11 - - - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_WIN64 - stdc11 - - - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdc11 - - - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdc11 - - - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdc11 - - - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - stdc11 - - - true - true - true - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ikvm-native/ikvm.c b/src/ikvm-native/ikvm.c deleted file mode 100644 index 939fe9ec6b..0000000000 --- a/src/ikvm-native/ikvm.c +++ /dev/null @@ -1,467 +0,0 @@ -#include "ikvm.h" - -typedef int (*GetMethodArgs_t)(JNIEnv* pEnv, jmethodID method, char* sig); -#define GET_METHOD_ARGS(pEnv, method, sig) (((GetMethodArgs_t)((*pEnv)->reserved0))(pEnv, methodID, sig)) - -#define MAKE_ARG_ARRAY(pEnv, args, argarray) \ -do { \ - jbyte sig[257];\ - int argc = GET_METHOD_ARGS(pEnv, methodID, sig);\ - int i;\ - argarray = (jvalue*)ALLOCA((long unsigned int)argc * sizeof(jvalue));\ - for (i = 0; i < argc; i++)\ - {\ - switch (sig[i])\ - {\ - case 'Z':\ - argarray[i].z = (jboolean)va_arg(args, int);\ - break;\ - case 'B':\ - argarray[i].b = (jbyte)va_arg(args, int);\ - break;\ - case 'S':\ - argarray[i].s = (jshort)va_arg(args, int);\ - break;\ - case 'C':\ - argarray[i].i = (jchar)va_arg(args, int);\ - break;\ - case 'I':\ - argarray[i].i = (jint)va_arg(args, int);\ - break;\ - case 'J':\ - argarray[i].j = (jlong)va_arg(args, long);\ - break;\ - case 'D':\ - argarray[i].d = (jdouble)va_arg(args, double);\ - break;\ - case 'F':\ - argarray[i].f = (jfloat)va_arg(args, double);\ - break;\ - case 'L':\ - argarray[i].l = (jobject)va_arg(args, void*);\ - break;\ - }\ - }\ -} while(0); - -#define MAKE_METHOD(Type, type) \ -static type JNICALL Call##Type##Method(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...)\ -{\ - type ret;\ - va_list args;\ - va_start(args, methodID);\ - ret = (*pEnv)->Call##Type##MethodV(pEnv, obj, methodID, args);\ - va_end(args);\ - return ret;\ -}\ -static type JNICALL Call##Type##MethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args)\ -{\ - jvalue* argarray;\ - MAKE_ARG_ARRAY(pEnv, args, argarray);\ - return (*pEnv)->Call##Type##MethodA(pEnv, obj, methodID, argarray);\ -}\ -static type JNICALL CallNonvirtual##Type##Method(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...)\ -{\ - type ret;\ - va_list args;\ - va_start(args, methodID);\ - ret = (*pEnv)->CallNonvirtual##Type##MethodV(pEnv, obj, clazz, methodID, args);\ - va_end(args);\ - return ret;\ -}\ -static type JNICALL CallNonvirtual##Type##MethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args)\ -{\ - jvalue* argarray;\ - MAKE_ARG_ARRAY(pEnv, args, argarray);\ - return (*pEnv)->CallNonvirtual##Type##MethodA(pEnv, obj, clazz, methodID, argarray);\ -}\ -static type JNICALL CallStatic##Type##Method(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...)\ -{\ - type ret;\ - va_list args;\ - va_start(args, methodID);\ - ret = (*pEnv)->CallStatic##Type##MethodV(pEnv, clazz, methodID, args);\ - va_end(args);\ - return ret;\ -}\ -static type JNICALL CallStatic##Type##MethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args)\ -{\ - jvalue* argarray;\ - MAKE_ARG_ARRAY(pEnv, args, argarray);\ - return (*pEnv)->CallStatic##Type##MethodA(pEnv, clazz, methodID, argarray);\ -} - -MAKE_METHOD(Object, jobject) -MAKE_METHOD(Boolean, jboolean) -MAKE_METHOD(Byte, jbyte) -MAKE_METHOD(Char, jchar) -MAKE_METHOD(Short, jshort) -MAKE_METHOD(Int, jint) -MAKE_METHOD(Long, jlong) -MAKE_METHOD(Float, jfloat) -MAKE_METHOD(Double, jdouble) - -static jobject JNICALL NewObject(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...) -{ - jobject o; - va_list args; - va_start(args, methodID); - o = (*pEnv)->NewObjectV(pEnv, clazz, methodID, args); - va_end(args); - return o; -} - -static jobject JNICALL NewObjectV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args) -{ - jvalue* argarray; - MAKE_ARG_ARRAY(pEnv, args, argarray); - return (*pEnv)->NewObjectA(pEnv, clazz, methodID, argarray); -} - -static void JNICALL CallVoidMethod(JNIEnv* pEnv, jobject obj, jmethodID methodID, ...) -{ - va_list args; - va_start(args, methodID); - (*pEnv)->CallVoidMethodV(pEnv, obj, methodID, args); - va_end(args); -} -static void JNICALL CallVoidMethodV(JNIEnv* pEnv, jobject obj, jmethodID methodID, va_list args) -{ - jvalue* argarray; - MAKE_ARG_ARRAY(pEnv, args, argarray); - (*pEnv)->CallVoidMethodA(pEnv, obj, methodID, argarray); -} -static void JNICALL CallNonvirtualVoidMethod(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, ...) -{ - va_list args; - va_start(args, methodID); - (*pEnv)->CallNonvirtualVoidMethodV(pEnv, obj, clazz, methodID, args); - va_end(args); -} -static void JNICALL CallNonvirtualVoidMethodV(JNIEnv* pEnv, jobject obj, jclass clazz, jmethodID methodID, va_list args) -{ - jvalue* argarray; - MAKE_ARG_ARRAY(pEnv, args, argarray); - (*pEnv)->CallNonvirtualVoidMethodA(pEnv, obj, clazz, methodID, argarray); -} -static void JNICALL CallStaticVoidMethod(JNIEnv* pEnv, jclass clazz, jmethodID methodID, ...) -{ - va_list args; - va_start(args, methodID); - (*pEnv)->CallStaticVoidMethodV(pEnv, clazz, methodID, args); - va_end(args); -} -static void JNICALL CallStaticVoidMethodV(JNIEnv* pEnv, jclass clazz, jmethodID methodID, va_list args) -{ - jvalue* argarray; - MAKE_ARG_ARRAY(pEnv, args, argarray); - (*pEnv)->CallStaticVoidMethodA(pEnv, clazz, methodID, argarray); -} - -static void* JNIEnv_vtable[] = -{ - 0, // void JNICALL reserved0(); - 0, // void JNICALL reserved1(); - 0, // void JNICALL reserved2(); - 0, // void JNICALL reserved3(); - - 0, // jint JNICALL GetVersion(); - - 0, // jclass JNICALL DefineClass(const char *name, jobject loader, const jbyte *buf, jsize len); - 0, // jclass JNICALL FindClass(const char *name); - - 0, // jmethodID JNICALL FromReflectedMethod(jobject method); - 0, // jfieldID JNICALL FromReflectedField(jobject field); - 0, // jobject JNICALL ToReflectedMethod(jclass clazz, jmethodID methodID, jboolean isStatic); - - 0, // jclass JNICALL GetSuperclass(jclass sub); - 0, // jboolean JNICALL IsAssignableFrom(jclass sub, jclass sup); - - 0, // jobject JNICALL ToReflectedField(jclass clazz, jfieldID fieldID, jboolean isStatic); - - 0, // jint JNICALL Throw(jthrowable obj); - 0, // jint JNICALL ThrowNew(jclass clazz, const char *msg); - 0, // jthrowable JNICALL ExceptionOccurred(); - 0, // void JNICALL ExceptionDescribe(); - 0, // void JNICALL ExceptionClear(); - 0, // void JNICALL FatalError(const char *msg); - - 0, // jint JNICALL PushLocalFrame(jint capacity); - 0, // jobject JNICALL PopLocalFrame(jobject result); - - 0, // jobject JNICALL NewGlobalRef(jobject lobj); - 0, // void JNICALL DeleteGlobalRef(jobject gref); - 0, // void JNICALL DeleteLocalRef(jobject obj); - 0, // jboolean JNICALL IsSameObject(jobject obj1, jobject obj2); - - 0, // jobject JNICALL NewLocalRef(jobject ref); - 0, // jint JNICALL EnsureLocalCapacity(jint capacity); - - 0, // jobject JNICALL AllocObject(jclass clazz); - NewObject, // jobject JNICALL NewObject(jclass clazz, jmethodID methodID, ...); - NewObjectV, // jobject JNICALL NewObjectV(jclass clazz, jmethodID methodID, va_list args); - 0, // jobject JNICALL NewObjectA(jclass clazz, jmethodID methodID, jvalue *args); - - 0, // jclass JNICALL GetObjectClass(jobject obj); - 0, // jboolean JNICALL IsInstanceOf(jobject obj, jclass clazz); - - 0, // jmethodID JNICALL GetMethodID(jclass clazz, const char *name, const char *sig); - - CallObjectMethod, // jobject JNICALL CallObjectMethod(jobject obj, jmethodID methodID, ...); - CallObjectMethodV, // jobject JNICALL CallObjectMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jobject JNICALL CallObjectMethodA(jobject obj, jmethodID methodID, jvalue * args); - - CallBooleanMethod, // jboolean JNICALL CallBooleanMethod(jobject obj, jmethodID methodID, ...); - CallBooleanMethodV, // jboolean JNICALL CallBooleanMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jboolean JNICALL CallBooleanMethodA(jobject obj, jmethodID methodID, jvalue * args); - - CallByteMethod, // jbyte JNICALL CallByteMethod(jobject obj, jmethodID methodID, ...); - CallByteMethodV, // jbyte JNICALL CallByteMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jbyte JNICALL CallByteMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallCharMethod, // jchar JNICALL CallCharMethod(jobject obj, jmethodID methodID, ...); - CallCharMethodV, // jchar JNICALL CallCharMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jchar JNICALL CallCharMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallShortMethod, // jshort JNICALL CallShortMethod(jobject obj, jmethodID methodID, ...); - CallShortMethodV, // jshort JNICALL CallShortMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jshort JNICALL CallShortMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallIntMethod, // jint JNICALL CallIntMethod(jobject obj, jmethodID methodID, ...); - CallIntMethodV, // jint JNICALL CallIntMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jint JNICALL CallIntMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallLongMethod, // jlong JNICALL CallLongMethod(jobject obj, jmethodID methodID, ...); - CallLongMethodV, // jlong JNICALL CallLongMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jlong JNICALL CallLongMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallFloatMethod, // jfloat JNICALL CallFloatMethod(jobject obj, jmethodID methodID, ...); - CallFloatMethodV, // jfloat JNICALL CallFloatMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jfloat JNICALL CallFloatMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallDoubleMethod, // jdouble JNICALL CallDoubleMethod(jobject obj, jmethodID methodID, ...); - CallDoubleMethodV, // jdouble JNICALL CallDoubleMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // jdouble JNICALL CallDoubleMethodA(jobject obj, jmethodID methodID, jvalue *args); - - CallVoidMethod, // void JNICALL CallVoidMethod(jobject obj, jmethodID methodID, ...); - CallVoidMethodV, // void JNICALL CallVoidMethodV(jobject obj, jmethodID methodID, va_list args); - 0, // void JNICALL CallVoidMethodA(jobject obj, jmethodID methodID, jvalue * args); - - CallNonvirtualObjectMethod, // jobject JNICALL CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualObjectMethodV, // jobject JNICALL CallNonvirtualObjectMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jobject JNICALL CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - CallNonvirtualBooleanMethod, // jboolean JNICALL CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualBooleanMethodV, // jboolean JNICALL CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jboolean JNICALL CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - CallNonvirtualByteMethod, // jbyte JNICALL CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualByteMethodV, // jbyte JNICALL CallNonvirtualByteMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jbyte JNICALL CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualCharMethod, // jchar JNICALL CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualCharMethodV, // jchar JNICALL CallNonvirtualCharMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jchar JNICALL CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualShortMethod, // jshort JNICALL CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualShortMethodV, // jshort JNICALL CallNonvirtualShortMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jshort JNICALL CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualIntMethod, // jint JNICALL CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualIntMethodV, // jint JNICALL CallNonvirtualIntMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jint JNICALL CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualLongMethod, // jlong JNICALL CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualLongMethodV, // jlong JNICALL CallNonvirtualLongMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jlong JNICALL CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualFloatMethod, // jfloat JNICALL CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualFloatMethodV, // jfloat JNICALL CallNonvirtualFloatMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jfloat JNICALL CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualDoubleMethod, // jdouble JNICALL CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualDoubleMethodV, // jdouble JNICALL CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // jdouble JNICALL CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); - - CallNonvirtualVoidMethod, // void JNICALL CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...); - CallNonvirtualVoidMethodV, // void JNICALL CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); - 0, // void JNICALL CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); - - 0, // jfieldID JNICALL GetFieldID(jclass clazz, const char *name, const char *sig); - - 0, // jobject JNICALL GetObjectField(jobject obj, jfieldID fieldID); - 0, // jboolean JNICALL GetBooleanField(jobject obj, jfieldID fieldID); - 0, // jbyte JNICALL GetByteField(jobject obj, jfieldID fieldID); - 0, // jchar JNICALL GetCharField(jobject obj, jfieldID fieldID); - 0, // jshort JNICALL GetShortField(jobject obj, jfieldID fieldID); - 0, // jint JNICALL GetIntField(jobject obj, jfieldID fieldID); - 0, // jlong JNICALL GetLongField(jobject obj, jfieldID fieldID); - 0, // jfloat JNICALL GetFloatField(jobject obj, jfieldID fieldID); - 0, // jdouble JNICALL GetDoubleField(jobject obj, jfieldID fieldID); - - 0, // void JNICALL SetObjectField(jobject obj, jfieldID fieldID, jobject val); - 0, // void JNICALL SetBooleanField(jobject obj, jfieldID fieldID, jboolean val); - 0, // void JNICALL SetByteField(jobject obj, jfieldID fieldID, jbyte val); - 0, // void JNICALL SetCharField(jobject obj, jfieldID fieldID, jchar val); - 0, // void JNICALL SetShortField(jobject obj, jfieldID fieldID, jshort val); - 0, // void JNICALL SetIntField(jobject obj, jfieldID fieldID, jint val); - 0, // void JNICALL SetLongField(jobject obj, jfieldID fieldID, jlong val); - 0, // void JNICALL SetFloatField(jobject obj, jfieldID fieldID, jfloat val); - 0, // void JNICALL SetDoubleField(jobject obj, jfieldID fieldID, jdouble val); - - 0, // jmethodID JNICALL GetStaticMethodID(jclass clazz, const char *name, const char *sig); - - CallStaticObjectMethod, // jobject JNICALL CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...); - CallStaticObjectMethodV, // jobject JNICALL CallStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jobject JNICALL CallStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticBooleanMethod, // jboolean JNICALL CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...); - CallStaticBooleanMethodV, // jboolean JNICALL CallStaticBooleanMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jboolean JNICALL CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticByteMethod, // jbyte JNICALL CallStaticByteMethod(jclass clazz, jmethodID methodID, ...); - CallStaticByteMethodV, // jbyte JNICALL CallStaticByteMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jbyte JNICALL CallStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticCharMethod, // jchar JNICALL CallStaticCharMethod(jclass clazz, jmethodID methodID, ...); - CallStaticCharMethodV, // jchar JNICALL CallStaticCharMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jchar JNICALL CallStaticCharMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticShortMethod, // jshort JNICALL CallStaticShortMethod(jclass clazz, jmethodID methodID, ...); - CallStaticShortMethodV, // jshort JNICALL CallStaticShortMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jshort JNICALL CallStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticIntMethod, // jint JNICALL CallStaticIntMethod(jclass clazz, jmethodID methodID, ...); - CallStaticIntMethodV, // jint JNICALL CallStaticIntMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jint JNICALL CallStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticLongMethod, // jlong JNICALL CallStaticLongMethod(jclass clazz, jmethodID methodID, ...); - CallStaticLongMethodV, // jlong JNICALL CallStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jlong JNICALL CallStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticFloatMethod, // jfloat JNICALL CallStaticFloatMethod(jclass clazz, jmethodID methodID, ...); - CallStaticFloatMethodV, // jfloat JNICALL CallStaticFloatMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jfloat JNICALL CallStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticDoubleMethod, // jdouble JNICALL CallStaticDoubleMethod(jclass clazz, jmethodID methodID, ...); - CallStaticDoubleMethodV, // jdouble JNICALL CallStaticDoubleMethodV(jclass clazz, jmethodID methodID, va_list args); - 0, // jdouble JNICALL CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue *args); - - CallStaticVoidMethod, // void JNICALL CallStaticVoidMethod(jclass cls, jmethodID methodID, ...); - CallStaticVoidMethodV, // void JNICALL CallStaticVoidMethodV(jclass cls, jmethodID methodID, va_list args); - 0, // void JNICALL CallStaticVoidMethodA(jclass cls, jmethodID methodID, jvalue * args); - - 0, // jfieldID JNICALL GetStaticFieldID(jclass clazz, const char *name, const char *sig); - - 0, // jobject JNICALL GetObjectField(jobject obj, jfieldID fieldID); - 0, // jboolean JNICALL GetBooleanField(jobject obj, jfieldID fieldID); - 0, // jbyte JNICALL GetByteField(jobject obj, jfieldID fieldID); - 0, // jchar JNICALL GetCharField(jobject obj, jfieldID fieldID); - 0, // jshort JNICALL GetShortField(jobject obj, jfieldID fieldID); - 0, // jint JNICALL GetIntField(jobject obj, jfieldID fieldID); - 0, // jlong JNICALL GetLongField(jobject obj, jfieldID fieldID); - 0, // jfloat JNICALL GetFloatField(jobject obj, jfieldID fieldID); - 0, // jdouble JNICALL GetDoubleField(jobject obj, jfieldID fieldID); - - 0, // void JNICALL SetObjectField(jobject obj, jfieldID fieldID, jobject val); - 0, // void JNICALL SetBooleanField(jobject obj, jfieldID fieldID, jboolean val); - 0, // void JNICALL SetByteField(jobject obj, jfieldID fieldID, jbyte val); - 0, // void JNICALL SetCharField(jobject obj, jfieldID fieldID, jchar val); - 0, // void JNICALL SetShortField(jobject obj, jfieldID fieldID, jshort val); - 0, // void JNICALL SetIntField(jobject obj, jfieldID fieldID, jint val); - 0, // void JNICALL SetLongField(jobject obj, jfieldID fieldID, jlong val); - 0, // void JNICALL SetFloatField(jobject obj, jfieldID fieldID, jfloat val); - 0, // void JNICALL SetDoubleField(jobject obj, jfieldID fieldID, jdouble val); - - 0, // jstring JNICALL NewString(const jchar *unicode, jsize len); - 0, // jsize JNICALL GetStringLength(jstring str); - 0, // const jchar *JNICALL GetStringChars(jstring str, jboolean *isCopy); - 0, // void JNICALL ReleaseStringChars(jstring str, const jchar *chars); - - 0, // jstring JNICALL NewStringUTF(const char *utf); - 0, // jsize JNICALL GetStringUTFLength(jstring str); - 0, // const char* JNICALL GetStringUTFChars(jstring str, jboolean *isCopy); - 0, // void JNICALL ReleaseStringUTFChars(jstring str, const char* chars); - - 0, // jsize JNICALL GetArrayLength(jarray array); - - 0, // jobjectArray JNICALL NewObjectArray(jsize len, jclass clazz, jobject init); - 0, // jobject JNICALL GetObjectArrayElement(jobjectArray array, jsize index); - 0, // void JNICALL SetObjectArrayElement(jobjectArray array, jsize index, jobject val); - - 0, // jbooleanArray JNICALL NewBooleanArray(jsize len); - 0, // jbyteArray JNICALL NewByteArray(jsize len); - 0, // jcharArray JNICALL NewCharArray(jsize len); - 0, // jshortArray JNICALL NewShortArray(jsize len); - 0, // jintArray JNICALL NewIntArray(jsize len); - 0, // jlongArray JNICALL NewLongArray(jsize len); - 0, // jfloatArray JNICALL NewFloatArray(jsize len); - 0, // jdoubleArray JNICALL NewDoubleArray(jsize len); - - 0, // jboolean * JNICALL GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy); - 0, // jbyte * JNICALL GetByteArrayElements(jbyteArray array, jboolean *isCopy); - 0, // jchar * JNICALL GetCharArrayElements(jcharArray array, jboolean *isCopy); - 0, // jshort * JNICALL GetShortArrayElements(jshortArray array, jboolean *isCopy); - 0, // jint * JNICALL GetIntArrayElements(jintArray array, jboolean *isCopy); - 0, // jlong * JNICALL GetLongArrayElements(jlongArray array, jboolean *isCopy); - 0, // jfloat * JNICALL GetFloatArrayElements(jfloatArray array, jboolean *isCopy); - 0, // jdouble * JNICALL GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy); - - 0, // void JNICALL ReleaseBooleanArrayElements(jbooleanArray array, jboolean *elems, jint mode); - 0, // void JNICALL ReleaseByteArrayElements(jbyteArray array, jbyte *elems, jint mode); - 0, // void JNICALL ReleaseCharArrayElements(jcharArray array, jchar *elems, jint mode); - 0, // void JNICALL ReleaseShortArrayElements(jshortArray array, jshort *elems, jint mode); - 0, // void JNICALL ReleaseIntArrayElements(jintArray array, jint *elems, jint mode); - 0, // void JNICALL ReleaseLongArrayElements(jlongArray array, jlong *elems, jint mode); - 0, // void JNICALL ReleaseFloatArrayElements(jfloatArray array, jfloat *elems, jint mode); - 0, // void JNICALL ReleaseDoubleArrayElements(jdoubleArray array, jdouble *elems, jint mode); - - 0, // void JNICALL GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); - 0, // void JNICALL GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); - 0, // void JNICALL GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); - 0, // void JNICALL GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); - 0, // void JNICALL GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); - 0, // void JNICALL GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); - 0, // void JNICALL GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); - 0, // void JNICALL GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); - - 0, // void JNICALL SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); - 0, // void JNICALL SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); - 0, // void JNICALL SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); - 0, // void JNICALL SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); - 0, // void JNICALL SetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); - 0, // void JNICALL SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); - 0, // void JNICALL SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); - 0, // void JNICALL SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); - - 0, // jint JNICALL RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods); - 0, // jint JNICALL UnregisterNatives(jclass clazz); - - 0, // jint JNICALL MonitorEnter(jobject obj); - 0, // jint JNICALL MonitorExit(jobject obj); - - 0, // jint JNICALL GetJavaVM(JavaVM **vm); - - 0, // void JNICALL GetStringRegion(jstring str, jsize start, jsize len, jchar *buf); - 0, // void JNICALL GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf); - - 0, // void* JNICALL GetPrimitiveArrayCritical(jarray array, jboolean *isCopy); - 0, // void JNICALL ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode); - - 0, // const jchar* JNICALL GetStringCritical(jstring string, jboolean *isCopy); - 0, // void JNICALL ReleaseStringCritical(jstring string, const jchar *cstring); - - 0, // jweak JNICALL NewWeakGlobalRef(jobject obj); - 0, // void JNICALL DeleteWeakGlobalRef(jweak ref); - - 0, // jboolean JNICALL ExceptionCheck(); - - 0, // jobject JNICALL NewDirectByteBuffer(void* address, jlong capacity); - 0, // void* JNICALL GetDirectBufferAddress(jobject buf); - 0 // jlong JNICALL GetDirectBufferCapacity(jobject buf); -}; - -EXPORT void** ikvm_GetJNIEnvVTable() -{ - return JNIEnv_vtable; -} diff --git a/src/ikvm-native/ikvm.h b/src/ikvm-native/ikvm.h deleted file mode 100644 index 96ca040031..0000000000 --- a/src/ikvm-native/ikvm.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef IKVM_H_INCLUDED -#define IKVM_H_INCLUDED - -#ifdef _WIN32 -#define EXPORT __declspec(dllexport) -#else -#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) -#define EXPORT __attribute__((visibility("default"))) -#else -#define EXPORT -#endif -#endif - -#include -#include - -#ifdef _WIN32 -#include -#define ALLOCA _alloca -#else -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -#include -#else -#include -#endif -#define ALLOCA alloca -#endif - -EXPORT void** ikvm_GetJNIEnvVTable(); - -#endif diff --git a/src/ikvm-tests-native/ikvm-tests-native-win.vcxproj b/src/ikvm-tests-native/ikvm-tests-native-win.vcxproj index c00e2f4c8b..79ca6e0020 100644 --- a/src/ikvm-tests-native/ikvm-tests-native-win.vcxproj +++ b/src/ikvm-tests-native/ikvm-tests-native-win.vcxproj @@ -32,13 +32,13 @@ DynamicLibrary true - v143 + ClangCL Unicode DynamicLibrary false - v143 + ClangCL true Unicode diff --git a/src/ikvmc/IKVM/Internal/FatalCompilerErrorException.cs b/src/ikvmc/IKVM/Internal/FatalCompilerErrorException.cs deleted file mode 100644 index 2743f926ab..0000000000 --- a/src/ikvmc/IKVM/Internal/FatalCompilerErrorException.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - Copyright (C) 2002-2014 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ -using System; - -using IKVM.Internal; - -sealed class FatalCompilerErrorException : Exception -{ - internal FatalCompilerErrorException(Message id, params object[] args) - : base(string.Format("fatal error IKVMC{0}: {1}", (int)id, args.Length == 0 ? GetMessage(id) : string.Format(GetMessage(id), args))) - { - } - - private static string GetMessage(Message id) - { - switch (id) - { - case IKVM.Internal.Message.ResponseFileDepthExceeded: - return "Response file nesting depth exceeded"; - case IKVM.Internal.Message.ErrorReadingFile: - return "Unable to read file: {0}\n\t({1})"; - case IKVM.Internal.Message.NoTargetsFound: - return "No targets found"; - case IKVM.Internal.Message.FileFormatLimitationExceeded: - return "File format limitation exceeded: {0}"; - case IKVM.Internal.Message.CannotSpecifyBothKeyFileAndContainer: - return "You cannot specify both a key file and container"; - case IKVM.Internal.Message.DelaySignRequiresKey: - return "You cannot delay sign without a key file or container"; - case IKVM.Internal.Message.InvalidStrongNameKeyPair: - return "Invalid key {0} specified.\n\t(\"{1}\")"; - case IKVM.Internal.Message.ReferenceNotFound: - return "Reference not found: {0}"; - case IKVM.Internal.Message.OptionsMustPreceedChildLevels: - return "You can only specify options before any child levels"; - case IKVM.Internal.Message.UnrecognizedTargetType: - return "Invalid value '{0}' for -target option"; - case IKVM.Internal.Message.UnrecognizedPlatform: - return "Invalid value '{0}' for -platform option"; - case IKVM.Internal.Message.UnrecognizedApartment: - return "Invalid value '{0}' for -apartment option"; - case IKVM.Internal.Message.MissingFileSpecification: - return "Missing file specification for '{0}' option"; - case IKVM.Internal.Message.PathTooLong: - return "Path too long: {0}"; - case IKVM.Internal.Message.PathNotFound: - return "Path not found: {0}"; - case IKVM.Internal.Message.InvalidPath: - return "Invalid path: {0}"; - case IKVM.Internal.Message.InvalidOptionSyntax: - return "Invalid option: {0}"; - case IKVM.Internal.Message.ExternalResourceNotFound: - return "External resource file does not exist: {0}"; - case IKVM.Internal.Message.ExternalResourceNameInvalid: - return "External resource file may not include path specification: {0}"; - case IKVM.Internal.Message.InvalidVersionFormat: - return "Invalid version specified: {0}"; - case IKVM.Internal.Message.InvalidFileAlignment: - return "Invalid value '{0}' for -filealign option"; - case IKVM.Internal.Message.ErrorWritingFile: - return "Unable to write file: {0}\n\t({1})"; - case IKVM.Internal.Message.UnrecognizedOption: - return "Unrecognized option: {0}"; - case IKVM.Internal.Message.NoOutputFileSpecified: - return "No output file specified"; - case IKVM.Internal.Message.SharedClassLoaderCannotBeUsedOnModuleTarget: - return "Incompatible options: -target:module and -sharedclassloader cannot be combined"; - case IKVM.Internal.Message.RuntimeNotFound: - return "Unable to load runtime assembly"; - case IKVM.Internal.Message.MainClassRequiresExe: - return "Main class cannot be specified for library or module"; - case IKVM.Internal.Message.ExeRequiresMainClass: - return "No main method found"; - case IKVM.Internal.Message.PropertiesRequireExe: - return "Properties cannot be specified for library or module"; - case IKVM.Internal.Message.ModuleCannotHaveClassLoader: - return "Cannot specify assembly class loader for modules"; - case IKVM.Internal.Message.ErrorParsingMapFile: - return "Unable to parse remap file: {0}\n\t({1})"; - case IKVM.Internal.Message.BootstrapClassesMissing: - return "Bootstrap classes missing and core assembly not found"; - case IKVM.Internal.Message.StrongNameRequiresStrongNamedRefs: - return "All referenced assemblies must be strong named, to be able to sign the output assembly"; - case IKVM.Internal.Message.MainClassNotFound: - return "Main class not found"; - case IKVM.Internal.Message.MainMethodNotFound: - return "Main method not found"; - case IKVM.Internal.Message.UnsupportedMainMethod: - return "Redirected main method not supported"; - case IKVM.Internal.Message.ExternalMainNotAccessible: - return "External main method must be public and in a public class"; - case IKVM.Internal.Message.ClassLoaderNotFound: - return "Custom assembly class loader class not found"; - case IKVM.Internal.Message.ClassLoaderNotAccessible: - return "Custom assembly class loader class is not accessible"; - case IKVM.Internal.Message.ClassLoaderIsAbstract: - return "Custom assembly class loader class is abstract"; - case IKVM.Internal.Message.ClassLoaderNotClassLoader: - return "Custom assembly class loader class does not extend java.lang.ClassLoader"; - case IKVM.Internal.Message.ClassLoaderConstructorMissing: - return "Custom assembly class loader constructor is missing"; - case IKVM.Internal.Message.MapFileTypeNotFound: - return "Type '{0}' referenced in remap file was not found"; - case IKVM.Internal.Message.MapFileClassNotFound: - return "Class '{0}' referenced in remap file was not found"; - case IKVM.Internal.Message.MaximumErrorCountReached: - return "Maximum error count reached"; - case IKVM.Internal.Message.LinkageError: - return "Link error: {0}"; - case IKVM.Internal.Message.RuntimeMismatch: - return "Referenced assembly {0} was compiled with an incompatible IKVM.Runtime version\n" + - "\tCurrent runtime: {1}\n" + - "\tReferenced assembly runtime: {2}"; - case IKVM.Internal.Message.CoreClassesMissing: - return "Failed to find core classes in core library"; - case IKVM.Internal.Message.CriticalClassNotFound: - return "Unable to load critical class '{0}'"; - case IKVM.Internal.Message.AssemblyContainsDuplicateClassNames: - return "Type '{0}' and '{1}' both map to the same name '{2}'\n" + - "\t({3})"; - case IKVM.Internal.Message.CallerIDRequiresHasCallerIDAnnotation: - return "CallerID.getCallerID() requires a HasCallerID annotation"; - case IKVM.Internal.Message.UnableToResolveInterface: - return "Unable to resolve interface '{0}' on type '{1}'"; - case IKVM.Internal.Message.MissingBaseType: - return "The base class or interface '{0}' in assembly '{1}' referenced by type '{2}' in '{3}' could not be resolved"; - case IKVM.Internal.Message.MissingBaseTypeReference: - return "The type '{0}' is defined in an assembly that is not referenced. You must add a reference to assembly '{1}'"; - case IKVM.Internal.Message.FileNotFound: - return "File not found: {0}"; - case IKVM.Internal.Message.RuntimeMethodMissing: - return "Runtime method '{0}' not found"; - case IKVM.Internal.Message.MapFileFieldNotFound: - return "Field '{0}' referenced in remap file was not found in class '{1}'"; - case IKVM.Internal.Message.GhostInterfaceMethodMissing: - return "Remapped class '{0}' does not implement ghost interface method\n" + - "\t({1}.{2}{3})"; - default: - return "Missing Error Message. Please file a bug."; - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Class.cs b/src/ikvmc/IKVM/Internal/MapXml/Class.cs deleted file mode 100644 index c2ba111ebc..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Class.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - [XmlType("class")] - public sealed class Class - { - [XmlAttribute("name")] - public string Name; - [XmlAttribute("shadows")] - public string Shadows; - [XmlAttribute("modifiers")] - public MapModifiers Modifiers; - [XmlAttribute("scope")] - public Scope scope; - [XmlElement("constructor")] - public Constructor[] Constructors; - [XmlElement("method")] - public Method[] Methods; - [XmlElement("field")] - public Field[] Fields; - [XmlElement("property")] - public Property[] Properties; - [XmlElement("implements")] - public Interface[] Interfaces; - [XmlElement("clinit")] - public ClassInitializer Clinit; - [XmlElement("attribute")] - public Attribute[] Attributes; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/ExceptionBlock.cs b/src/ikvmc/IKVM/Internal/MapXml/ExceptionBlock.cs deleted file mode 100644 index 9fc8b2e4db..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/ExceptionBlock.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -using Type = IKVM.Reflection.Type; - -namespace IKVM.Internal.MapXml -{ - [XmlType("exceptionBlock")] - public sealed class ExceptionBlock : Instruction - { - public InstructionList @try; - public CatchBlock @catch; - public InstructionList @finally; - - internal override void Generate(CodeGenContext context, CodeEmitter ilgen) - { - ilgen.BeginExceptionBlock(); - @try.Generate(context, ilgen); - if (@catch != null) - { - Type type; - if (@catch.type != null) - { - type = StaticCompiler.GetTypeForMapXml(context.ClassLoader, @catch.type); - } - else - { - type = context.ClassLoader.LoadClassByDottedName(@catch.Class).TypeAsExceptionType; - } - ilgen.BeginCatchBlock(type); - @catch.Generate(context, ilgen); - } - if (@finally != null) - { - ilgen.BeginFinallyBlock(); - @finally.Generate(context, ilgen); - } - ilgen.EndExceptionBlock(); - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/ExceptionMapping.cs b/src/ikvmc/IKVM/Internal/MapXml/ExceptionMapping.cs deleted file mode 100644 index a6fd60e8f4..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/ExceptionMapping.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - [XmlType("exception")] - public sealed class ExceptionMapping - { - [XmlAttribute] - public string src; - [XmlAttribute] - public string dst; - public InstructionList code; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Field.cs b/src/ikvmc/IKVM/Internal/MapXml/Field.cs deleted file mode 100644 index 9e152af2b3..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Field.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class Field - { - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; - [XmlAttribute("modifiers")] - public MapModifiers Modifiers; - [XmlAttribute("constant")] - public string Constant; - [XmlElement("attribute")] - public Attribute[] Attributes; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Instruction.cs b/src/ikvmc/IKVM/Internal/MapXml/Instruction.cs deleted file mode 100644 index e2257ba51d..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Instruction.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public abstract class Instruction - { - private int lineNumber = Root.LineNumber; - - internal int LineNumber - { - get - { - return lineNumber; - } - } - - internal abstract void Generate(CodeGenContext context, CodeEmitter ilgen); - - public override string ToString() - { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - sb.Append('<'); - object[] attr = GetType().GetCustomAttributes(typeof(XmlTypeAttribute), false); - if (attr.Length == 1) - { - sb.Append(((XmlTypeAttribute)attr[0]).TypeName); - } - else - { - sb.Append(GetType().Name); - } - foreach (System.Reflection.FieldInfo field in GetType().GetFields()) - { - if (!field.IsStatic) - { - object value = field.GetValue(this); - if (value != null) - { - attr = field.GetCustomAttributes(typeof(XmlAttributeAttribute), false); - if (attr.Length == 1) - { - sb.AppendFormat(" {0}=\"{1}\"", ((XmlAttributeAttribute)attr[0]).AttributeName, value); - } - } - } - } - sb.Append(" />"); - return sb.ToString(); - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/InstructionList.cs b/src/ikvmc/IKVM/Internal/MapXml/InstructionList.cs deleted file mode 100644 index 8a605299dd..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/InstructionList.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public class InstructionList - { - [XmlElement(typeof(Ldstr))] - [XmlElement(typeof(Call))] - [XmlElement(typeof(Callvirt))] - [XmlElement(typeof(Ldftn))] - [XmlElement(typeof(Ldvirtftn))] - [XmlElement(typeof(Dup))] - [XmlElement(typeof(Pop))] - [XmlElement(typeof(IsInst))] - [XmlElement(typeof(Castclass))] - [XmlElement(typeof(Castclass_impl))] - [XmlElement(typeof(Ldobj))] - [XmlElement(typeof(Unbox))] - [XmlElement(typeof(Box))] - [XmlElement(typeof(BrFalse))] - [XmlElement(typeof(BrTrue))] - [XmlElement(typeof(Br))] - [XmlElement(typeof(Beq))] - [XmlElement(typeof(Bne_Un))] - [XmlElement(typeof(Bge_Un))] - [XmlElement(typeof(Ble_Un))] - [XmlElement(typeof(Blt))] - [XmlElement(typeof(Blt_Un))] - [XmlElement(typeof(BrLabel))] - [XmlElement(typeof(NewObj))] - [XmlElement(typeof(StLoc))] - [XmlElement(typeof(LdLoc))] - [XmlElement(typeof(LdArga))] - [XmlElement(typeof(LdArg_S))] - [XmlElement(typeof(LdArg_0))] - [XmlElement(typeof(LdArg_1))] - [XmlElement(typeof(LdArg_2))] - [XmlElement(typeof(LdArg_3))] - [XmlElement(typeof(Ldind_i1))] - [XmlElement(typeof(Ldind_i2))] - [XmlElement(typeof(Ldind_i4))] - [XmlElement(typeof(Ldind_i8))] - [XmlElement(typeof(Ldind_r4))] - [XmlElement(typeof(Ldind_r8))] - [XmlElement(typeof(Ldind_ref))] - [XmlElement(typeof(Stind_i1))] - [XmlElement(typeof(Stind_i2))] - [XmlElement(typeof(Stind_i4))] - [XmlElement(typeof(Stind_i8))] - [XmlElement(typeof(Stind_ref))] - [XmlElement(typeof(Ret))] - [XmlElement(typeof(Throw))] - [XmlElement(typeof(Ldnull))] - [XmlElement(typeof(Ldflda))] - [XmlElement(typeof(Ldfld))] - [XmlElement(typeof(Ldsfld))] - [XmlElement(typeof(Stfld))] - [XmlElement(typeof(Stsfld))] - [XmlElement(typeof(Ldc_I4))] - [XmlElement(typeof(Ldc_I4_0))] - [XmlElement(typeof(Ldc_I4_1))] - [XmlElement(typeof(Ldc_I4_M1))] - [XmlElement(typeof(Conv_I))] - [XmlElement(typeof(Conv_I1))] - [XmlElement(typeof(Conv_U1))] - [XmlElement(typeof(Conv_I2))] - [XmlElement(typeof(Conv_U2))] - [XmlElement(typeof(Conv_I4))] - [XmlElement(typeof(Conv_U4))] - [XmlElement(typeof(Conv_I8))] - [XmlElement(typeof(Conv_U8))] - [XmlElement(typeof(Ldlen))] - [XmlElement(typeof(ExceptionBlock))] - [XmlElement(typeof(Add))] - [XmlElement(typeof(Sub))] - [XmlElement(typeof(Mul))] - [XmlElement(typeof(Div_Un))] - [XmlElement(typeof(Rem_Un))] - [XmlElement(typeof(And))] - [XmlElement(typeof(Or))] - [XmlElement(typeof(Xor))] - [XmlElement(typeof(Not))] - [XmlElement(typeof(Unaligned))] - [XmlElement(typeof(Cpblk))] - [XmlElement(typeof(Ceq))] - [XmlElement(typeof(ConditionalInstruction))] - [XmlElement(typeof(Volatile))] - [XmlElement(typeof(Ldelema))] - [XmlElement(typeof(Newarr))] - [XmlElement(typeof(Ldtoken))] - [XmlElement(typeof(Leave))] - [XmlElement(typeof(Endfinally))] - [XmlElement(typeof(RunClassInit))] - [XmlElement(typeof(EmitExceptionMapping))] - public Instruction[] invoke; - - internal void Generate(CodeGenContext context, CodeEmitter ilgen) - { - if (invoke != null) - { - for (int i = 0; i < invoke.Length; i++) - { - if (invoke[i].LineNumber != -1) - { - ilgen.SetLineNumber((ushort)invoke[i].LineNumber); - } - invoke[i].Generate(context, ilgen); - } - } - } - - internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) - { - Generate(new CodeGenContext(loader), ilgen); - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Interface.cs b/src/ikvmc/IKVM/Internal/MapXml/Interface.cs deleted file mode 100644 index f354603240..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Interface.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class Interface - { - [XmlAttribute("class")] - public string Name; - [XmlElement("method")] - public Method[] Methods; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Method.cs b/src/ikvmc/IKVM/Internal/MapXml/Method.cs deleted file mode 100644 index 5ab15059e6..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Method.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class Method : MethodConstructorBase - { - [XmlAttribute("name")] - public string Name; - [XmlAttribute("nonullcheck")] - public bool NoNullCheck; - public InstructionList nonvirtualAlternateBody; - public Override @override; - - internal override MethodKey ToMethodKey(string className) - { - return new MethodKey(className, Name, Sig); - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/MethodBase.cs b/src/ikvmc/IKVM/Internal/MapXml/MethodBase.cs deleted file mode 100644 index 398df73b47..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/MethodBase.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -using IKVM.Reflection; - -namespace IKVM.Internal.MapXml -{ - public abstract class MethodBase - { - [XmlAttribute("attributes")] - public MethodAttributes MethodAttributes; - public InstructionList body; - [XmlElement("throws", typeof(Throws))] - public Throws[] throws; - [XmlElement("attribute")] - public Attribute[] Attributes; - [XmlElement("replace-method-call")] - public ReplaceMethodCall[] ReplaceMethodCalls; - public InstructionList prologue; - - internal abstract MethodKey ToMethodKey(string className); - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/MethodConstructorBase.cs b/src/ikvmc/IKVM/Internal/MapXml/MethodConstructorBase.cs deleted file mode 100644 index 5ce911cff6..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/MethodConstructorBase.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public abstract class MethodConstructorBase : MethodBase - { - [XmlAttribute("sig")] - public string Sig; - [XmlAttribute("modifiers")] - public MapModifiers Modifiers; - [XmlElement("parameter")] - public Param[] Params; - public InstructionList alternateBody; - public Redirect redirect; - - internal void Emit(ClassLoaderWrapper loader, CodeEmitter ilgen) - { - if (prologue != null) - { - prologue.Emit(loader, ilgen); - } - if (redirect != null) - { - redirect.Emit(loader, ilgen); - } - else - { - body.Emit(loader, ilgen); - } - } - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Override.cs b/src/ikvmc/IKVM/Internal/MapXml/Override.cs deleted file mode 100644 index 3de04509d6..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Override.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class Override - { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Param.cs b/src/ikvmc/IKVM/Internal/MapXml/Param.cs deleted file mode 100644 index be223bf5d9..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Param.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class Param - { - [XmlText] - public string Value; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; // optional (for object type args) - [XmlElement("element")] - public Element[] Elements; - [XmlElement("attribute")] - public Attribute[] Attributes; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Property.cs b/src/ikvmc/IKVM/Internal/MapXml/Property.cs deleted file mode 100644 index 1a0f0b2127..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Property.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - - public sealed class Property - { - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; - public Method getter; - public Method setter; - [XmlElement("attribute")] - public Attribute[] Attributes; - } - -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/ReplaceMethodCall.cs b/src/ikvmc/IKVM/Internal/MapXml/ReplaceMethodCall.cs deleted file mode 100644 index 742642f681..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/ReplaceMethodCall.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - public sealed class ReplaceMethodCall - { - [XmlAttribute("class")] - public string Class; - [XmlAttribute("name")] - public string Name; - [XmlAttribute("sig")] - public string Sig; - public InstructionList code; - } -} diff --git a/src/ikvmc/IKVM/Internal/MapXml/Root.cs b/src/ikvmc/IKVM/Internal/MapXml/Root.cs deleted file mode 100644 index f67678fc38..0000000000 --- a/src/ikvmc/IKVM/Internal/MapXml/Root.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2002-2010 Jeroen Frijters - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jeroen Frijters - jeroen@frijters.net - -*/ - -using System.Xml.Serialization; - -namespace IKVM.Internal.MapXml -{ - [XmlRoot("root")] - public sealed class Root - { - internal static System.Xml.XmlTextReader xmlReader; - - internal static int LineNumber - { - get - { - return xmlReader == null ? -1 : xmlReader.LineNumber; - } - } - - [XmlElement("assembly")] - public Assembly assembly; - public ExceptionMapping[] exceptionMappings; - } -} diff --git a/src/ikvmc/Program.cs b/src/ikvmc/Program.cs new file mode 100644 index 0000000000..c7c0a826b3 --- /dev/null +++ b/src/ikvmc/Program.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +using IKVM.Tools.Importer; + +namespace ikvmc +{ + + public static class Program + { + + public static Task Main(string[] args) + { + return IkvmImporterTool.Main(args, CancellationToken.None); + } + + } + +} diff --git a/src/ikvmc/ikvmc.csproj b/src/ikvmc/ikvmc.csproj index 2a6f25e24a..4be8c3c7c1 100644 --- a/src/ikvmc/ikvmc.csproj +++ b/src/ikvmc/ikvmc.csproj @@ -1,34 +1,15 @@  - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 - $(DefineConstants);STATIC_COMPILER;EMITTERS + $(SupportedRuntimeIdentifiers) true - true - - - - - - - - - - - - - - - - - - + diff --git a/src/ikvmstub/Program.cs b/src/ikvmstub/Program.cs index ead16ee499..c99f4711d0 100644 --- a/src/ikvmstub/Program.cs +++ b/src/ikvmstub/Program.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using IKVM.Tools.Exporter; @@ -10,7 +11,7 @@ public static class Program public static Task Main(string[] args) { - return IkvmExporterTool.Main(args); + return IkvmExporterTool.Main(args, CancellationToken.None); } } diff --git a/src/ikvmstub/ikvmstub.csproj b/src/ikvmstub/ikvmstub.csproj index 0c0aa28af7..305b50dc32 100644 --- a/src/ikvmstub/ikvmstub.csproj +++ b/src/ikvmstub/ikvmstub.csproj @@ -1,15 +1,13 @@  + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) true - - - - diff --git a/src/jar/jar.msbuildproj b/src/jar/jar.msbuildproj index bffc09daa2..a606bab51b 100644 --- a/src/jar/jar.msbuildproj +++ b/src/jar/jar.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.tools.jar.Main ikvm.tools.jar diff --git a/src/jarsigner/jarsigner.msbuildproj b/src/jarsigner/jarsigner.msbuildproj index 68c8a12054..1d0de24c7b 100644 --- a/src/jarsigner/jarsigner.msbuildproj +++ b/src/jarsigner/jarsigner.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.security.tools.jarsigner.Main ikvm.tools.jarsigner diff --git a/src/java/Program.cs b/src/java/Program.cs index 119f611182..2fa263b360 100644 --- a/src/java/Program.cs +++ b/src/java/Program.cs @@ -1,12 +1,16 @@ -using java.util; -using ikvm.runtime; +using ikvm.runtime; -namespace ikvm.tools.java +using IKVM.Attributes; + +using java.util; + +namespace IKVM.Tools.Java { public static class Program { + [HideFromJava] public static int Main(string[] args) => Launcher.run(null, args, "", new Properties()); } diff --git a/src/java/Properties/launchSettings.json b/src/java/Properties/launchSettings.json new file mode 100644 index 0000000000..c57951f810 --- /dev/null +++ b/src/java/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "java": { + "commandName": "Project", + "commandLineArgs": "-cp foo" + } + } +} \ No newline at end of file diff --git a/src/java/java.csproj b/src/java/java.csproj index d152f2f91d..f76af1c354 100644 --- a/src/java/java.csproj +++ b/src/java/java.csproj @@ -1,11 +1,11 @@  - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) ikvm.tools.java diff --git a/src/javac-ref/javac-ref.msbuildproj b/src/javac-ref/javac-ref.msbuildproj index 14779f600e..b61f93e69a 100644 --- a/src/javac-ref/javac-ref.msbuildproj +++ b/src/javac-ref/javac-ref.msbuildproj @@ -2,14 +2,14 @@ - - + + Exe javac net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.javac.Main ikvm.tools.javac diff --git a/src/javac/javac.msbuildproj b/src/javac/javac.msbuildproj index c6a36b0071..c9d0a837a2 100644 --- a/src/javac/javac.msbuildproj +++ b/src/javac/javac.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.javac.Main ikvm.tools.javac diff --git a/src/javadoc/javadoc.msbuildproj b/src/javadoc/javadoc.msbuildproj index 2e025ebf1f..c97401f77e 100644 --- a/src/javadoc/javadoc.msbuildproj +++ b/src/javadoc/javadoc.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.javadoc.Main ikvm.tools.javadoc diff --git a/src/javah/javah.msbuildproj b/src/javah/javah.msbuildproj index 0af67bcf74..97a6ebc633 100644 --- a/src/javah/javah.msbuildproj +++ b/src/javah/javah.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.javah.Main ikvm.tools.javah diff --git a/src/javap/javap.msbuildproj b/src/javap/javap.msbuildproj index 241e8fc05a..dbc201490a 100644 --- a/src/javap/javap.msbuildproj +++ b/src/javap/javap.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.javap.Main ikvm.tools.javap diff --git a/src/jdeps/jdeps.msbuildproj b/src/jdeps/jdeps.msbuildproj index 7f50fed1a0..38294bf182 100644 --- a/src/jdeps/jdeps.msbuildproj +++ b/src/jdeps/jdeps.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.jdeps.Main ikvm.tools.jdeps diff --git a/src/keytool/keytool.msbuildproj b/src/keytool/keytool.msbuildproj index 512da28827..abfbf1f821 100644 --- a/src/keytool/keytool.msbuildproj +++ b/src/keytool/keytool.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.security.tools.keytool.Main ikvm.tools.keytool diff --git a/src/native2ascii/native2ascii.msbuildproj b/src/native2ascii/native2ascii.msbuildproj index 4df7f27e25..ae5cf4cc32 100644 --- a/src/native2ascii/native2ascii.msbuildproj +++ b/src/native2ascii/native2ascii.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.tools.native2ascii.Main ikvm.tools.native2ascii diff --git a/src/orbd/orbd.msbuildproj b/src/orbd/orbd.msbuildproj new file mode 100644 index 0000000000..c8caf7502a --- /dev/null +++ b/src/orbd/orbd.msbuildproj @@ -0,0 +1,20 @@ + + + + + + + + + Exe + net461;netcoreapp3.1 + $(SupportedRuntimeIdentifiers) + com.sun.corba.se.impl.activation.ORBD + ikvm.tools.orbd + + + + + + + diff --git a/src/policytool/policytool.msbuildproj b/src/policytool/policytool.msbuildproj index d2f1501b7a..05e69f919d 100644 --- a/src/policytool/policytool.msbuildproj +++ b/src/policytool/policytool.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.security.tools.policytool.PolicyTool ikvm.tools.policytool diff --git a/src/rmic/rmic.msbuildproj b/src/rmic/rmic.msbuildproj index a543e7c55a..e0e037d84a 100644 --- a/src/rmic/rmic.msbuildproj +++ b/src/rmic/rmic.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) sun.rmi.rmic.Main ikvm.tools.rmic diff --git a/src/schemagen/schemagen.msbuildproj b/src/schemagen/schemagen.msbuildproj new file mode 100644 index 0000000000..84858045d5 --- /dev/null +++ b/src/schemagen/schemagen.msbuildproj @@ -0,0 +1,20 @@ + + + + + + + + + Exe + net461;netcoreapp3.1 + $(SupportedRuntimeIdentifiers) + com.sun.tools.internal.jxc.SchemaGenerator + ikvm.tools.schemagen + + + + + + + diff --git a/src/wsgen/wsgen.msbuildproj b/src/wsgen/wsgen.msbuildproj index 2259bc3ccb..684ee41b4f 100644 --- a/src/wsgen/wsgen.msbuildproj +++ b/src/wsgen/wsgen.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.internal.ws.WsGen ikvm.tools.wsgen diff --git a/src/wsimport/wsimport.msbuildproj b/src/wsimport/wsimport.msbuildproj index 0b44a20fe3..e3480b3ce3 100644 --- a/src/wsimport/wsimport.msbuildproj +++ b/src/wsimport/wsimport.msbuildproj @@ -2,13 +2,13 @@ - - + + Exe net461;netcoreapp3.1 - win7-x64;win7-x86;win81-arm;linux-x64;linux-arm;linux-arm64 + $(SupportedRuntimeIdentifiers) com.sun.tools.internal.ws.WsImport ikvm.tools.wsimport