diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..a4148517c7be --- /dev/null +++ b/.clang-format @@ -0,0 +1,49 @@ +# This is a minimal clang-format file for Mono code. The code is not consistent +# in its formatting, so this file is most often useful with git-clang-format [1], +# which formats only changed lines. +# +# Usage: +# * Make some changes to Mono C code or headers +# * Run `git clang-format` (maybe add -f if there are line ending issues from git) +# * Your changed code should meet the formatting guidelines for Mono +# +# Installation: +# * Install LLVM tools (including clang-format) for your OS [2] +# * Install Pythong on your OS +# +# Code style: +# Our goal is to match the Mono code style, not define a new one. The style +# options [3] are many. Here we try to capture only a few. If you find something +# is missing, feel free to add configuration options. +# +# [1] https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/git-clang-format +# [2] http://releases.llvm.org/ +# [3] https://clang.llvm.org/docs/ClangFormatStyleOptions.html + +# All function declarations and calls should have a space after the name +SpaceBeforeParens: Always + +# Use four space tabs +UseTab: Always +TabWidth: 4 +IndentWidth: 4 + +# Handle curly braces, basically: functions get a line break, nothing else does +BreakBeforeBraces: Custom +BraceWrapping: + AfterControlStatement: false + AfterStruct: false + AfterFunction: true + AfterEnum: false + AfterStruct: false + AfterUnion: false + BeforeElse: false + +# Keep function return types on a different line from the function name +# Note that often static function forward declarations don't follow this +# approach, and have the return type on the same line. I'm not sure +# clang-format can handle these two cases differently. +AlwaysBreakAfterReturnType: TopLevel + +# Don't warp any long lines +ColumnLimit: 0 \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..f06c382dcef0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,28 @@ +# see http://editorconfig.org/ for docs on this file + +root = true + +[*] +end_of_line = lf +trim_trailing_whitespace = false +insert_final_newline = false +indent_style = tab +indent_size = 4 + +# this VS-specific stuff is based on experiments to see how VS will modify a file after it has been manually edited. +# the settings are meant to closely match what VS does to minimize unnecessary diffs. this duplicates some settings in * +# but let's be explicit here to be safe (in case someone wants to copy-paste this out to another .editorconfig). +[*.{vcxproj,vcxproj.filters,csproj,props,targets}] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8-bom +trim_trailing_whitespace = true +insert_final_newline = false +[*.{sln,sln.template}] +indent_style = tab +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false diff --git a/.gitignore b/.gitignore index c6ef19a849b6..29553b9d7f18 100644 --- a/.gitignore +++ b/.gitignore @@ -117,6 +117,14 @@ GTAGS docs/doxygen* docs/perlmod* +# Bee +artifacts +.vs +external/buildscripts/build.gen* +stevedore + +# Allow +!external/buildscripts/bee.exe ############################################################################## # Mono-specific patterns diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000000..26d65318e0b3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,266 @@ +stages: +- runall-and-report-to-github-pending +- build +- collate +- report-to-github-done + +############################################################################### +# report result to github +############################################################################### +runall-and-report-to-github-pending: + image: python:2.7 + stage: runall-and-report-to-github-pending + script: + - python reportCiResult.py "gitlab-ci" "pending" + when: manual + allow_failure: false + +# Build +build_osx_runtime: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_osx_runtime_vm + BOKKEN_JOB: | + resources: + - name: build_osx_runtime_vm + image: buildfarm/mac:latest + flavor: m1.mac + type: Unity::VM::osx + num_instances: 1 + config: + env_vars: + - PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin + script: + - git submodule update --init --recursive + - cp .yamato/config/Stevedore.conf ~/Stevedore.conf + - cd external/buildscripts + - ./bee + - cd ../.. + - perl external/buildscripts/build_runtime_osx.pl --stevedorebuilddeps=1 + - mkdir -p incomingbuilds/osx-i386 + - cp -r builds/ incomingbuilds/osx-i386/ + artifacts: + paths: + - incomingbuilds/osx-i386 + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - /opt/post_build_script.sh + +build_osx_classlibs: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_osx_classlibs_vm + BOKKEN_JOB: | + resources: + - name: build_osx_classlibs_vm + image: buildfarm/mac:latest + flavor: m1.mac + type: Unity::VM::osx + num_instances: 1 + config: + env_vars: + - PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin + script: + - git submodule update --init --recursive + - cp .yamato/config/Stevedore.conf ~/Stevedore.conf + - cd external/buildscripts + - ./bee + - cd ../.. + - perl external/buildscripts/build_classlibs_osx.pl --stevedorebuilddeps=1 + - mkdir -p incomingbuilds/classlibs + - cp -r ZippedClasslibs.tar.gz incomingbuilds/classlibs/ + - cd incomingbuilds/classlibs + - tar -pzxf ZippedClasslibs.tar.gz + - rm -f ZippedClasslibs.tar.gz + - cd ../.. + artifacts: + paths: + - incomingbuilds/classlibs + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - /opt/post_build_script.sh + +build_android: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_android_vm + BOKKEN_JOB: | + resources: + - name: build_android_vm + image: buildfarm/mac:latest + flavor: m1.mac + type: Unity::VM::osx + num_instances: 1 + config: + env_vars: + - PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin + script: + - git submodule update --init --recursive + - cp .yamato/config/Stevedore.conf ~/Stevedore.conf + - cd external/buildscripts + - ./bee + - cd ../.. + - perl external/buildscripts/build_runtime_android.pl --stevedorebuilddeps=1 + - mkdir -p incomingbuilds/android/ + - cp -r builds/* incomingbuilds/android/ + artifacts: + paths: + - incomingbuilds/android + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - /opt/post_build_script.sh + +build_win: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_win_vm + BOKKEN_JOB: | + resources: + - name: build_win_vm + image: platform-foundation/windows-mono-bokken:latest + flavor: b1.xlarge + type: Unity::VM + script: + - git submodule update --init --recursive + - perl external/buildscripts/build_runtime_win64.pl --stevedorebuilddeps=1 + - powershell mkdir -p incomingbuilds/win64 + - powershell cp -r builds/* incomingbuilds/win64/ + artifacts: + paths: + - incomingbuilds/win64 + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - C:\Users\builduser\post_build_script.bat + +build_win_x86: + stage: build + tags: + - bokken-job + variables: + BOKKEN_VM: build_win_x86_vm + BOKKEN_JOB: | + resources: + - name: build_win_x86_vm + image: platform-foundation/windows-mono-bokken:latest + flavor: b1.xlarge + type: Unity::VM + script: + - git submodule update --init --recursive + - perl external/buildscripts/build_runtime_win.pl --stevedorebuilddeps=1 + - powershell mkdir -p incomingbuilds/win32 + - powershell cp -r builds/* incomingbuilds/win32/ + artifacts: + paths: + - incomingbuilds/win32 + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - C:\Users\builduser\post_build_script.bat + +build_linux_x64: + stage: build + tags: + - buildfarm + - linux + script: + - git submodule update --init --recursive + - cd external/buildscripts + - ./bee + - cd ../.. + - perl external/buildscripts/build_runtime_linux.pl -build64=1 --stevedorebuilddeps=1 + - mkdir -p incomingbuilds/linux64 + - cp -r builds/* incomingbuilds/linux64/ + artifacts: + paths: + - incomingbuilds/linux64 + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - /opt/post_build_script.sh + +build_linux_x86: + stage: build + tags: + - buildfarm + - linux + script: + - git submodule update --init --recursive + - cd external/buildscripts + - ./bee + - cd ../.. + - perl external/buildscripts/build_runtime_linux.pl --stevedorebuilddeps=1 + - mkdir -p incomingbuilds/linux32 + - cp -r builds/* incomingbuilds/linux32/ + artifacts: + paths: + - incomingbuilds/linux32 + expire_in: 1 week +# Important! Do not remove this after_script!! + after_script: + - /opt/post_build_script.sh + +collate_builds: + stage: collate + tags: + - bokken-job + variables: + BOKKEN_VM: collate_ubuntu + BOKKEN_JOB: | + resources: + - name: collate_ubuntu + image: cds-ops/ubuntu-18.04-agent:v1.0.11-765607 + flavor: b1.large + type: Unity::VM + dependencies: + - build_android + - build_osx_runtime + - build_osx_classlibs + - build_win + - build_win_x86 + - build_win_bare_minimum + - build_linux_x86 + - build_linux_x64 + before_script: + - sudo DEBIAN_FRONTEND=noninteractive apt-get update -qy && sudo DEBIAN_FRONTEND=noninteractive apt-get -qy upgrade + - sudo apt-get install -qy zip unzip + - sudo apt-get install -qy p7zip-full p7zip-rar + script: + - perl external/buildscripts/collect_allbuilds.pl + - pwd + - ls -al + artifacts: + paths: + - collectedbuilds/builds.7z + - stevedore/MonoBleedingEdge.7z + - stevedore/artifactid.txt + expire_in: 1 week + + +############################################################################### +# report result to github +############################################################################### +report-to-github-done:failure: + image: python:2.7 + when: on_failure + stage: report-to-github-done + script: + - python reportCiResult.py "gitlab-ci" "failure" + +report-to-github-done:success: + image: python:2.7 + when: on_success + stage: report-to-github-done + script: + - python reportCiResult.py "gitlab-ci" "success" \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 4e968d7a0663..04b5228b6bef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,3 +58,6 @@ [submodule "external/api-snapshot"] path = external/api-snapshot url = git://github.com/mono/api-snapshot.git +[submodule "external/bdwgc"] + path = external/bdwgc + url = git://github.com/Unity-Technologies/bdwgc.git diff --git a/.yamato/Build Android.yml b/.yamato/Build Android.yml new file mode 100644 index 000000000000..07752ec1df0e --- /dev/null +++ b/.yamato/Build Android.yml @@ -0,0 +1,14 @@ +name: Build Android + +agent: + type: Unity::VM::osx + image: platform-foundation/mac-bokken:latest + flavor: m1.mac + +commands: + - .yamato/scripts/build_android.sh + +artifacts: + android: + paths: + - incomingbuilds/android/** diff --git a/.yamato/Build Classlibs OSX.yml b/.yamato/Build Classlibs OSX.yml new file mode 100644 index 000000000000..597446438e91 --- /dev/null +++ b/.yamato/Build Classlibs OSX.yml @@ -0,0 +1,15 @@ +name: Build Classlibs OSX + +agent: + type: Unity::VM::osx + image: platform-foundation/mac-bokken:latest + flavor: m1.mac + +commands: + - .yamato/scripts/build_osx_classlibs.sh + + +artifacts: + classlibs: + paths: + - incomingbuilds/classlibs/** diff --git a/.yamato/Build Linux x64.yml b/.yamato/Build Linux x64.yml new file mode 100644 index 000000000000..af17d65bb063 --- /dev/null +++ b/.yamato/Build Linux x64.yml @@ -0,0 +1,14 @@ +name: Build Linux x64 + +agent: + type: Unity::VM + image: platform-foundation/linux-ubuntu-18.04-mono-bokken:latest + flavor: b1.large + +commands: + - .yamato/scripts/build_linux_x64.sh + +artifacts: + linux64: + paths: + - incomingbuilds/linux64/** diff --git a/.yamato/Build Linux x86.yml b/.yamato/Build Linux x86.yml new file mode 100644 index 000000000000..38da145da50b --- /dev/null +++ b/.yamato/Build Linux x86.yml @@ -0,0 +1,14 @@ +name: Build Linux x86 + +agent: + type: Unity::VM + image: platform-foundation/linux-ubuntu-18.04-mono-bokken:latest + flavor: b1.large + +commands: + - .yamato/scripts/build_linux_x86.sh + +artifacts: + linux32: + paths: + - incomingbuilds/linux32/** diff --git a/.yamato/Build Runtime OSX.yml b/.yamato/Build Runtime OSX.yml new file mode 100644 index 000000000000..e3cf81489ebc --- /dev/null +++ b/.yamato/Build Runtime OSX.yml @@ -0,0 +1,14 @@ +name: Build Runtime OSX + +agent: + type: Unity::VM::osx + image: platform-foundation/mac-bokken:latest + flavor: m1.mac + +commands: + - .yamato/scripts/build_osx_runtime.sh + +artifacts: + osx-i386: + paths: + - incomingbuilds/osx-i386/** diff --git a/.yamato/Build Windows x64.yml b/.yamato/Build Windows x64.yml new file mode 100644 index 000000000000..63deff64c4d5 --- /dev/null +++ b/.yamato/Build Windows x64.yml @@ -0,0 +1,14 @@ +name: Build Windows x64 + +agent: + type: Unity::VM + image: platform-foundation/windows-mono-bokken:latest + flavor: b1.xlarge + +commands: + - .yamato/scripts/build_win.bat + +artifacts: + win64: + paths: + - incomingbuilds\win64\** \ No newline at end of file diff --git a/.yamato/Build Windows x86.yml b/.yamato/Build Windows x86.yml new file mode 100644 index 000000000000..3d57a4ba9123 --- /dev/null +++ b/.yamato/Build Windows x86.yml @@ -0,0 +1,14 @@ +name: Build Windows x86 + +agent: + type: Unity::VM + image: platform-foundation/windows-mono-bokken:latest + flavor: b1.xlarge + +commands: + - .yamato/scripts/build_win_x86.bat + +artifacts: + win32: + paths: + - incomingbuilds\win32\** \ No newline at end of file diff --git a/.yamato/Collate Builds.yml b/.yamato/Collate Builds.yml new file mode 100644 index 000000000000..8ab0dee51c67 --- /dev/null +++ b/.yamato/Collate Builds.yml @@ -0,0 +1,37 @@ +name: Collate Builds + +agent: + type: Unity::VM + image: cds-ops/ubuntu-18.04-agent:v1.0.11-765607 + flavor: b1.large + +dependencies: + - .yamato/Build Android.yml + - .yamato/Build Linux x64.yml + - .yamato/Build Linux x86.yml + - .yamato/Build Classlibs OSX.yml + - .yamato/Build Runtime OSX.yml + - .yamato/Build Windows x64.yml + - .yamato/Build Windows x86.yml + +commands: + - .yamato/scripts/collate_builds.sh + +triggers: + pull_requests: + - targets: + only: + - "unity-master" + cancel_old_ci: true + recurring: + - branch: unity-master + frequency: daily # Should run between midnight and 6AM UTC + +artifacts: + builds: + paths: + - collectedbuilds/builds.7z + stevedore: + paths: + - stevedore/MonoBleedingEdge.7z + - stevedore/artifactid.txt diff --git a/.yamato/Publish To Stevedore.yml b/.yamato/Publish To Stevedore.yml new file mode 100644 index 000000000000..d8759f21fd71 --- /dev/null +++ b/.yamato/Publish To Stevedore.yml @@ -0,0 +1,14 @@ +name: 'Publish to Stevedore' + +agent: + type: Unity::VM + image: cds-ops/cds-ubuntu-18.04-base:stable + flavor: b1.large + +dependencies: + - .yamato/Collate Builds.yml + +commands: + - curl -sSo StevedoreUpload.exe "$STEVEDORE_UPLOAD_TOOL_URL" + - mono StevedoreUpload.exe --repo=unity-internal --version-len=11 --version="$GIT_REVISION" stevedore/MonoBleedingEdge.7z + diff --git a/.yamato/Update Il2cpp-deps.yml b/.yamato/Update Il2cpp-deps.yml new file mode 100644 index 000000000000..3e3dbf916424 --- /dev/null +++ b/.yamato/Update Il2cpp-deps.yml @@ -0,0 +1,24 @@ +name: 'Update Il2cpp-deps' +agent: + type: Unity::VM + image: platform-foundation/windows-vs2019-bokken:latest + flavor: b1.xlarge +dependencies: + - .yamato/Collate Builds.yml + - .yamato/Publish To Stevedore.yml +commands: + - | + git clone git@github.cds.internal.unity3d.com:unity/prtools.git + cd prtools + git checkout master + nuget.exe restore PRTools.sln + cmd /c cibuildscript + cmd /c xcopy build %PRTOOLS_BUILD_DIR% /s /Y /E /I + cd %UNITY_SOURCE_PRTOOLS_DIR% + %PRTOOLS_BUILD_DIR%\prtools.exe --update-mbe-il2cpp-deps=%YAMATO_SOURCE_DIR%/stevedore/artifactid.txt --github-api-token=%GITHUB_TOKEN% --yamato-api-token=%YAMATO_TOKEN% --yamato-long-lived-token + if NOT %errorlevel% == 0 ( + echo "PRTools failed" + EXIT /B %errorlevel% + ) +timeout: 1 + diff --git a/.yamato/Update Unity.yml b/.yamato/Update Unity.yml new file mode 100644 index 000000000000..de831d13efb2 --- /dev/null +++ b/.yamato/Update Unity.yml @@ -0,0 +1,29 @@ +name: 'Update Unity' +agent: + type: Unity::VM + image: platform-foundation/windows-vs2019-bokken:latest + flavor: b1.xlarge +dependencies: + - .yamato/Collate Builds.yml + - .yamato/Publish To Stevedore.yml + - .yamato/Update Il2cpp-deps.yml +commands: + - choco install python3 --pre + - | + git clone git@github.cds.internal.unity3d.com:unity/prtools.git + cd prtools + git checkout master + nuget.exe restore PRTools.sln + cmd /c cibuildscript + cmd /c xcopy build %PRTOOLS_BUILD_DIR% /s /Y /E /I + cd %UNITY_SOURCE_PRTOOLS_DIR% + %PRTOOLS_BUILD_DIR%\prtools.exe --update-mbe-unity=%YAMATO_SOURCE_DIR%/collectedbuilds/builds.7z --github-api-token=%GITHUB_TOKEN% --yamato-api-token=%YAMATO_TOKEN% --yamato-long-lived-token + if NOT %errorlevel% == 0 ( + echo "PRTools failed" + EXIT /B %errorlevel% + ) + cd %YAMATO_SOURCE_DIR% + py -m pip install requests + py prtools/katana-ci/katana_launcher.py --verbose --dontwait --project="proj0-ABuildVerification" --repo=unity --branch=scripting/mbe/staging --user="ashwini ashwini@unity3d.com" +timeout: 1 + diff --git a/.yamato/config/LinuxBuildEnvironment-20170609.conf b/.yamato/config/LinuxBuildEnvironment-20170609.conf new file mode 100644 index 000000000000..ff554766d76e --- /dev/null +++ b/.yamato/config/LinuxBuildEnvironment-20170609.conf @@ -0,0 +1,11 @@ +[LinuxBuildEnvironment-20170609] +description=Unity Linux SDK 20170609 +directory=/home/bokken/build/output/Unity-Technologies/mono/external/buildscripts/artifacts/Stevedore/linux-sdk-20170609/linux-sdk-20170609/LinuxBuildEnvironment-20170609 +personality=linux +users=bokken,builduser +groups=sudo +root-groups=sudo +preserve-environment=true +type=directory +profile=linux-sdk-20170609 +script-config=linux-sdk-20170609/config diff --git a/.yamato/config/Stevedore.conf b/.yamato/config/Stevedore.conf new file mode 100644 index 000000000000..538eb2c305eb --- /dev/null +++ b/.yamato/config/Stevedore.conf @@ -0,0 +1,34 @@ +# ~/Stevedore.conf (contact: #devs-stevedore) +# Best practice config for Slough (SLO) machines + +# Do not use OAuth interactive authorization +oauth.client-id = + +# Prevent instabilities if artifacts are requested from the wrong +# repository (in which case a cache hit could mask the error). +cache-folder.group-by-repo = true + +# No proxy needed to reach build farm mirror (and as of this writing, +# Stevedore does not support NO_PROXY). +proxy = + +# The timeout limits time spent waiting for the HTTP response headers. +# For a cache miss, artifactory-slo won't respond until it has downloaded +# the entire artifact from the origin server. 100s should be enough for +# even the largest artifacts and a slow origin (e.g. 1 GB at 10 MB/s). +timeout = 100000 + +# For telemetry, we DO need proxy (and with a low 5s timeout) +telemetry = full +telemetry.proxy = http://proxy.bf.unity3d.com:3128 +telemetry.tag = unity-rec-slo +telemetry.timeout = 5000 + +# Use mirror for retrieving all artifacts +repo.public.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-public/ +repo.main.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-main/ +repo.unity-internal.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-unity-internal/ +repo.testing.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-testing/ +repo.ms-consoles.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-ms-consoles/ +repo.sony-consoles.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-sony-consoles/ +repo.nintendo-consoles.url = https://artifactory-slo.bf.unity3d.com/artifactory/public-generic-stevedore-nintendo-consoles/ diff --git a/.yamato/scripts/build_android.sh b/.yamato/scripts/build_android.sh new file mode 100755 index 000000000000..426fdb788f0e --- /dev/null +++ b/.yamato/scripts/build_android.sh @@ -0,0 +1,17 @@ +git submodule update --init --recursive +cp .yamato/config/Stevedore.conf ~/Stevedore.conf +cd external/buildscripts +./bee +cd ../.. + +perl external/buildscripts/build_runtime_android.pl --stevedorebuilddeps=1 +if [ $? -eq 0 ] +then + echo "mono build script ran successfully" +else + echo "mono build script failed" >&2 + exit 1 +fi + +mkdir -p incomingbuilds/android +cp -r builds/* incomingbuilds/android/ \ No newline at end of file diff --git a/.yamato/scripts/build_linux_x64.sh b/.yamato/scripts/build_linux_x64.sh new file mode 100755 index 000000000000..b4f34f72ada2 --- /dev/null +++ b/.yamato/scripts/build_linux_x64.sh @@ -0,0 +1,25 @@ +sudo apt-get install -y schroot +sudo apt-get install -y binutils debootstrap +git submodule update --init --recursive +# try again in case previous update failed +git submodule update --init --recursive +export UNITY_THISISABUILDMACHINE=1 +cd external/buildscripts +./bee +cd ../.. + +perl external/buildscripts/build_runtime_linux.pl -build64=1 --stevedorebuilddeps=1 +if [ $? -eq 0 ] +then + echo "mono build script ran successfully" +else + echo "mono build script failed" >&2 + exit 1 +fi + +echo "Making directory incomingbuilds/linux64" +mkdir -p incomingbuilds/linux64 +ls -al incomingbuilds/linux64 +echo "Copying builds to incomingbuilds" +cp -r builds/* incomingbuilds/linux64/ +ls -al incomingbuilds/linux64 \ No newline at end of file diff --git a/.yamato/scripts/build_linux_x86.sh b/.yamato/scripts/build_linux_x86.sh new file mode 100755 index 000000000000..d64965481989 --- /dev/null +++ b/.yamato/scripts/build_linux_x86.sh @@ -0,0 +1,23 @@ +git submodule update --init --recursive +export UNITY_THISISABUILDMACHINE=1 +# try again in case previous update failed +git submodule update --init --recursive +cd external/buildscripts +./bee +cd ../.. + +perl external/buildscripts/build_runtime_linux.pl -build64=0 --stevedorebuilddeps=1 +if [ $? -eq 0 ] +then + echo "mono build script ran successfully" +else + echo "mono build script failed" >&2 + exit 1 +fi + +echo "Making directory incomingbuilds/linux32" +mkdir -p incomingbuilds/linux32 +ls -al incomingbuilds/linux32 +echo "Copying builds to incomingbuilds" +cp -r -v builds/* incomingbuilds/linux32/ +ls -al incomingbuilds/linux32 \ No newline at end of file diff --git a/.yamato/scripts/build_osx_classlibs.sh b/.yamato/scripts/build_osx_classlibs.sh new file mode 100755 index 000000000000..8c9450dafc37 --- /dev/null +++ b/.yamato/scripts/build_osx_classlibs.sh @@ -0,0 +1,21 @@ +git submodule update --init --recursive +cp .yamato/config/Stevedore.conf ~/Stevedore.conf +cd external/buildscripts +./bee +cd ../.. + +perl external/buildscripts/build_classlibs_osx.pl --stevedorebuilddeps=1 +if [ $? -eq 0 ] +then + echo "mono build script ran successfully" +else + echo "mono build script failed" >&2 + exit 1 +fi + +mkdir -p incomingbuilds/classlibs +cp -r ZippedClasslibs.tar.gz incomingbuilds/classlibs/ +cd incomingbuilds/classlibs +tar -pzxf ZippedClasslibs.tar.gz +rm -f ZippedClasslibs.tar.gz +cd ../.. \ No newline at end of file diff --git a/.yamato/scripts/build_osx_runtime.sh b/.yamato/scripts/build_osx_runtime.sh new file mode 100755 index 000000000000..24a3f2786b17 --- /dev/null +++ b/.yamato/scripts/build_osx_runtime.sh @@ -0,0 +1,17 @@ +git submodule update --init --recursive +cp .yamato/config/Stevedore.conf ~/Stevedore.conf +cd external/buildscripts +./bee +cd ../.. + +perl external/buildscripts/build_runtime_osx.pl --stevedorebuilddeps=1 +if [ $? -eq 0 ] +then + echo "mono build script ran successfully" +else + echo "mono build script failed" >&2 + exit 1 +fi + +mkdir -p incomingbuilds/osx-i386 +cp -r builds/* incomingbuilds/osx-i386/ \ No newline at end of file diff --git a/.yamato/scripts/build_win.bat b/.yamato/scripts/build_win.bat new file mode 100644 index 000000000000..fcf2a4925061 --- /dev/null +++ b/.yamato/scripts/build_win.bat @@ -0,0 +1,12 @@ +@echo off +git submodule update --init --recursive + +perl external/buildscripts/build_runtime_win64.pl --stevedorebuilddeps=1 +if NOT %errorlevel% == 0 ( + echo "mono build script failed" + EXIT /B %errorlevel% +) +echo "mono build script ran successfully" + +md incomingbuilds\win64 +xcopy /s /e /h /y builds\* incomingbuilds\win64 diff --git a/.yamato/scripts/build_win_x86.bat b/.yamato/scripts/build_win_x86.bat new file mode 100644 index 000000000000..c8df8d6a83d5 --- /dev/null +++ b/.yamato/scripts/build_win_x86.bat @@ -0,0 +1,12 @@ +@echo off +git submodule update --init --recursive + +perl external/buildscripts/build_runtime_win.pl --stevedorebuilddeps=1 +if NOT %errorlevel% == 0 ( + echo "mono build script failed" + EXIT /B %errorlevel% +) +echo "mono build script ran successfully" + +md incomingbuilds\win32 +xcopy /s /e /h /y builds\* incomingbuilds\win32 \ No newline at end of file diff --git a/.yamato/scripts/collate_builds.sh b/.yamato/scripts/collate_builds.sh new file mode 100755 index 000000000000..f7f2000d1a9d --- /dev/null +++ b/.yamato/scripts/collate_builds.sh @@ -0,0 +1,5 @@ +sudo apt-get install -qy zip unzip +sudo apt-get install -qy p7zip-full p7zip-rar +perl external/buildscripts/collect_allbuilds.pl +pwd +ls -al \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index f9dd1784c836..024324e3fc7f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,9 +17,9 @@ else support_dir = endif -SUBDIRS = po $(libgc_dir) mono $(ikvm_native_dir) $(support_dir) data runtime scripts man samples $(tools_dir) msvc $(docs_dir) acceptance-tests llvm +SUBDIRS = po $(libgc_dir) $(bdwgc_dir) mono $(ikvm_native_dir) $(support_dir) data runtime scripts man samples $(tools_dir) msvc $(docs_dir) acceptance-tests llvm # Keep in sync with SUBDIRS -DIST_SUBDIRS = m4 po $(libgc_dir) mono ikvm-native $(support_dir) data runtime scripts man samples tools msvc docs acceptance-tests llvm +DIST_SUBDIRS = m4 po $(libgc_dir) $(bdwgc_dir) mono ikvm-native $(support_dir) data runtime scripts man samples tools msvc docs acceptance-tests llvm all: update_submodules diff --git a/README.md b/README.md index ce96d99aa059..744a997c10f9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,81 @@ +Unity +============================ +This is Unity Technologies fork of the open source mono project. + +### Versions +Each Unity release upto 2019.2 contains two versions of Mono. One older version (Mono) is embedded into the Editor and Players. Another newer version (MonoBleedingEdge) is used to run tools and tests. + +In newer Unity versions (2017.1+) this MonoBleedingEdge version can be used in the Editor and Players via enabling an Experimental player setting. + +Unity versions 2019.3+ contain only MonoBleedingEdge. + +### Branch Naming Convention +Branches for released Unity versions are of the form unity-\\[-mbe\][-staging\]. The '-staging' suffix is used as a branch for PRs to target with potential changes. The '-mbe' suffix indicates the branch is for the MonoBleedingEdge version of Mono mentioned above. + +### Branches + +#### Trunk +* [unity-master (PR to this branch for MonoBleedingEdge)](https://github.com/Unity-Technologies/mono/tree/unity-master) + +#### 2020.1 +* [unity-2020.1-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2020.1-mbe) + +#### 2019.4 +* [unity-2019.4-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2019.4-mbe) + +#### 2019.3 +* [unity-2019.3-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2019.3-mbe) + +#### 2019.2 +* [unity-2019.2](https://github.com/Unity-Technologies/mono/tree/unity-2019.2) +* [unity-2019.2-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2019.2-mbe) + +#### 2019.1 +* [unity-2019.1](https://github.com/Unity-Technologies/mono/tree/unity-2019.1) +* [unity-2019.1-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2019.1-mbe) + +#### 2018.4 +* [unity-2018.4](https://github.com/Unity-Technologies/mono/tree/unity-2018.4) +* [unity-2018.4-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2018.4-mbe) + +#### 2018.3 +* [unity-2018.3](https://github.com/Unity-Technologies/mono/tree/unity-2018.3) +* [unity-2018.3-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2018.3-mbe) + +#### 2018.2 +* [unity-2018.2](https://github.com/Unity-Technologies/mono/tree/unity-2018.2) +* [unity-2018.2-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2018.2-mbe) + +#### 2018.1 +* [unity-2018.1](https://github.com/Unity-Technologies/mono/tree/unity-2018.1) +* [unity-2018.1-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2018.1-mbe) + +#### 2017.4 +* [unity-2017.4](https://github.com/Unity-Technologies/mono/tree/unity-2017.4) +* [unity-2017.4-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2017.4-mbe) + +#### 2017.3 +* [unity-2017.3](https://github.com/Unity-Technologies/mono/tree/unity-2017.3) +* [unity-2017.3-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2017.3-mbe) + +#### 2017.2 +* [unity-2017.2](https://github.com/Unity-Technologies/mono/tree/unity-2017.2) +* [unity-2017.2-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2017.2-mbe) + +#### 2017.1 +* [unity-2017.1](https://github.com/Unity-Technologies/mono/tree/unity-2017.1) +* [unity-2017.1-mbe](https://github.com/Unity-Technologies/mono/tree/unity-2017.1-mbe) + +#### 5.6 +* [unity-5.6](https://github.com/Unity-Technologies/mono/tree/unity-5.6) +* [unity-5.6-mbe](https://github.com/Unity-Technologies/mono/tree/unity-5.6-mbe) + + + + +Mono +============================ + Mono is a software platform designed to allow developers to easily create cross platform applications. It is an open source implementation of Microsoft's .NET Framework based on the ECMA diff --git a/autogen.sh b/autogen.sh index 7c58e0921502..88c2eded6c6f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -148,6 +148,18 @@ automake --add-missing --gnu -Wno-portability -Wno-obsolete $am_opt || echo "Running autoconf ..." autoconf || { echo "**Error**: autoconf failed."; exit 1; } +# if test -d $srcdir/external/bdwgc/libatomic_ops; then +# echo Running external/bdwgc/libatomic_ops/autogen.sh ... +# (cd $srcdir/external/bdwgc/libatomic_ops ; ./autogen.sh) +# echo Done running external/bdwgc/libatomic_ops/autogen.sh ... +# fi + +if test -d $srcdir/external/bdwgc; then + echo Running external/bdwgc/autogen.sh ... + (cd $srcdir/external/bdwgc ; ./autogen.sh) + echo Done running external/bdwgc/autogen.sh ... +fi + if test -d $srcdir/libgc; then echo Running libgc/autogen.sh ... (cd $srcdir/libgc ; NOCONFIGURE=1 ./autogen.sh "$@") diff --git a/configure.ac b/configure.ac index ec5b8e7ac675..c934827fa4de 100644 --- a/configure.ac +++ b/configure.ac @@ -355,6 +355,9 @@ case "$host" in platform_ios=yes has_dtrace=no ;; + aarch64*-darwinmacos*) + support_boehm=no + ;; aarch64*-darwin*) platform_ios=yes ;; @@ -927,6 +930,8 @@ AC_ARG_WITH(xammac, [ --with-xammac=yes,no If you w AC_ARG_WITH(testing_aot_hybrid, [ --with-testing_aot_hybrid=yes,no If you want to build the testing_aot_hybrid assemblies (defaults to no)], [], [with_testing_aot_hybrid=default]) AC_ARG_WITH(testing_aot_full, [ --with-testing_aot_full=yes,no If you want to build the testing_aot_full assemblies (defaults to no)], [], [with_testing_aot_full=default]) AC_ARG_WITH(winaot, [ --with-winaot=yes,no If you want to build the Windows friendly AOT assemblies (defaults to no)], [], [with_winaot=default]) +AC_ARG_WITH(unityjit, [ --with-unityjit=yes,no If you want to build the Unity JIT friendly assemblies (defaults to no)], [], [with_unityjit=default]) +AC_ARG_WITH(unityaot, [ --with-unityaot=yes,no If you want to build the Unity AOT friendly assemblies (defaults to no)], [], [with_unityaot=default]) AC_ARG_WITH(orbis, [ --with-orbis=yes,no If you want to build the Orbis assemblies (defaults to no)], [], [with_orbis=default]) AC_ARG_WITH(unreal, [ --with-unreal=yes,no If you want to build the Unreal assemblies (defaults to no)], [], [with_unreal=default]) AC_ARG_WITH(wasm, [ --with-wasm=yes,no If you want to build the WebAssembly (defaults to no)], [], [with_wasm=default]) @@ -949,12 +954,15 @@ with_xammac_default=no with_testing_aot_hybrid_default=no with_testing_aot_full_default=no with_winaot_default=no +with_unityjit_default=no +with_unityaot_default=no with_orbis_default=no with_unreal_default=no with_wasm_default=no with_bitcode_default=no with_cooperative_gc_default=no +mono_feature_disable_com=yes INVARIANT_AOT_OPTIONS=nimt-trampolines=2000,ntrampolines=9000,nrgctx-fetch-trampolines=256,ngsharedvt-trampolines=4000 @@ -972,6 +980,8 @@ elif test x$with_runtime_preset = xall; then with_monotouch_tv_default=yes with_xammac_default=yes with_winaot_default=yes + with_unityjit_default=yes + with_unityaot_default=yes with_orbis_default=yes with_unreal_default=yes with_wasm_default=yes @@ -1034,6 +1044,25 @@ elif test x$with_runtime_preset = xwinaot; then AOT_BUILD_FLAGS="--runtime=mobile --aot=full,$INVARIANT_AOT_OPTIONS" AOT_RUN_FLAGS="--runtime=mobile --full-aot" AOT_MODE="llvmonly" +elif test x$with_runtime_preset = xunityjit; then + DISABLE_MCS_DOCS_default=yes + with_unityjit_default=yes + TEST_PROFILE=unityjit + + mono_feature_disable_remoting='yes' +elif test x$with_runtime_preset = xunityaot; then + DISABLE_MCS_DOCS_default=yes + with_unityaot_default=yes + TEST_PROFILE=unityaot + + #mono_feature_disable_com='yes' + mono_feature_disable_remoting='yes' + mono_feature_disable_reflection_emit_save='yes' + mono_feature_disable_reflection_emit='yes' + mono_feature_disable_appdomains='yes' + + AOT_BUILD_FLAGS="--aot=full,$INVARIANT_AOT_OPTIONS" + AOT_RUN_FLAGS="--full-aot" elif test x$with_runtime_preset = xorbis; then DISABLE_MCS_DOCS_default=yes with_orbis_default=yes @@ -1115,6 +1144,12 @@ fi if test "x$with_winaot" = "xdefault"; then with_winaot=$with_winaot_default fi +if test "x$with_unityjit = "xdefault"; then + with_unityjit=$with_unityjit_default +fi +if test "x$with_unityaot = "xdefault"; then + with_unityaot=$with_unityaot_default +fi if test "x$with_orbis" = "xdefault"; then with_orbis=$with_orbis_default fi @@ -1136,6 +1171,8 @@ AM_CONDITIONAL(INSTALL_XAMMAC, [test "x$with_xammac" != "xno"]) AM_CONDITIONAL(INSTALL_TESTING_AOT_HYBRID, [test "x$with_testing_aot_hybrid" != "xno"]) AM_CONDITIONAL(INSTALL_TESTING_AOT_FULL, [test "x$with_testing_aot_full" != "xno"]) AM_CONDITIONAL(INSTALL_WINAOT, [test "x$with_winaot" != "xno"]) +AM_CONDITIONAL(INSTALL_UNITYJIT, [test "x$with_unityjit" != "xno"]) +AM_CONDITIONAL(INSTALL_UNITYAOT, [test "x$with_unityaot" != "xno"]) AM_CONDITIONAL(INSTALL_ORBIS, [test "x$with_orbis" != "xno"]) AM_CONDITIONAL(INSTALL_UNREAL, [test "x$with_unreal" != "xno"]) AM_CONDITIONAL(INSTALL_WASM, [test "x$with_wasm" != "xno"]) @@ -1161,6 +1198,12 @@ fi if test -z "$INSTALL_WINAOT_TRUE"; then : default_profile=winaot fi +if test -z "$INSTALL_UNITYJIT_TRUE"; then : + default_profile=unityjit +fi +if test -z "$INSTALL_UNITYAOT_TRUE"; then : + default_profile=unityaot +fi if test -z "$INSTALL_ORBIS_TRUE"; then : default_profile=orbis fi @@ -1429,6 +1472,20 @@ AC_COMPILE_IFELSE([ AC_MSG_RESULT(no) ]) +dnl BDWGC GC configuration +dnl + +AC_ARG_ENABLE(bdwgc, [ --disable-bdwgc Disable the Bdwgc GC.], upport_bdwgc=$enableval,support_bdwgc=${support_bdwgc:-yes}) +AM_CONDITIONAL(SUPPORT_BDWGC, test x$support_bdwgc = xyes) +bdwgc_dir=external/bdwgc +BDWGC_DEFINES='-DHAVE_BOEHM_GC -DHAVE_BDWGC_GC' +LIBGCBDWGC_CPPFLAGS='-I$(top_srcdir)/external/bdwgc/include -I$(top_srcdir)/external/bdwgc/libatomic_ops/src' +AC_SUBST(bdwgc_dir) +AC_SUBST(BDWGC_DEFINES) +AC_SUBST(LIBGCBDWGC_CPPFLAGS) + +dnl + dnl dnl Boehm GC configuration dnl @@ -1505,7 +1562,10 @@ if test "x$support_boehm" = "xyes"; then AC_SUBST(LIBGC_STATIC_LIBS) AC_SUBST(libgc_dir) AC_SUBST(BOEHM_DEFINES) - +else + if test "x$support_bdwgc" = "xyes"; then + AC_DEFINE_UNQUOTED(DEFAULT_GC_NAME, "External BDWGC (with typed GC)", [GC description]) + fi fi AM_CONDITIONAL(SUPPORT_NULLGC, test "x$libgc" = "xnone") @@ -1560,22 +1620,27 @@ if test x$host_win32 = xno; then LIBS="$LIBS $DL_LIB" AC_DEFINE(HAVE_DL_LOADER,1,[dlopen-based dynamic loader available]) dnl from glib's configure.ac - AC_CACHE_CHECK([for preceeding underscore in symbols], - mono_cv_uscore,[ - AC_TRY_RUN([#include - int mono_underscore_test (void) { return 42; } - int main() { - void *f1 = (void*)0, *f2 = (void*)0, *handle; - handle = dlopen ((void*)0, 0); - if (handle) { - f1 = dlsym (handle, "mono_underscore_test"); - f2 = dlsym (handle, "_mono_underscore_test"); - } return (!f2 || f1); - }], + if test "x$cross_compiling" = "xyes"; then + AC_MSG_RESULT(cross compiling, assuming no) + mono_cv_uscore=no + else + AC_CACHE_CHECK([for preceeding underscore in symbols], + mono_cv_uscore,[ + AC_TRY_RUN([#include + int mono_underscore_test (void) { return 42; } + int main() { + void *f1 = (void*)0, *f2 = (void*)0, *handle; + handle = dlopen ((void*)0, 0); + if (handle) { + f1 = dlsym (handle, "mono_underscore_test"); + f2 = dlsym (handle, "_mono_underscore_test"); + } return (!f2 || f1); + }], [mono_cv_uscore=yes], [mono_cv_uscore=no], - []) - ]) + []) + ]) + fi if test "x$mono_cv_uscore" = "xyes"; then MONO_DL_NEED_USCORE=1 else @@ -3377,6 +3442,16 @@ case "$host" in ACCESS_UNALIGNED="no" CPPFLAGS="$CPPFLAGS -D__ARM_EABI__" ;; + aarch64-*-darwinmacos*) + TARGET_SYS=MACOS + TARGET=ARM64 + arch_target=arm64 + boehm_supported=false + AOT_SUPPORTED="yes" + BTLS_SUPPORTED=yes + BTLS_PLATFORM=aarch64 + AC_CHECK_HEADER(stdalign.h,[],[BTLS_SUPPORTED=no]) + ;; aarch64-*) # https://lkml.org/lkml/2012/7/15/133 TARGET=ARM64 @@ -3670,8 +3745,8 @@ if test "x$target_mach" = "xyes"; then CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_WATCHOS" CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DTARGET_WATCHOS" BTLS_SUPPORTED=no - elif test "x$TARGET" = "xARM" -o "x$TARGET" = "xARM64"; then - AC_DEFINE(TARGET_IOS,1,[The JIT/AOT targets iOS]) + elif test "x$TARGET" = "xARM" -o "x$TARGET" = "xARM64" -a "x$TARGET_SYS" != "xMACOS"; then + AC_DEFINE(TARGET_IOS,1,[The JIT/AOT targets iOS]) CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_IOS" CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DTARGET_IOS" BTLS_SUPPORTED=no @@ -4250,6 +4325,11 @@ case "x$libgc" in if test "x$support_boehm" = "xyes"; then AC_CONFIG_SUBDIRS(libgc) fi + if test "x$support_bdwgc" = "xyes"; then + AC_CONFIG_SUBDIRS([external/bdwgc/libatomic_ops]) + ac_configure_args="$ac_configure_args --enable-thread-local-alloc=no --enable-parallel-mark=no --enable-verify-defines=yes --enable-gc-threads=yes --with-libatomic-ops=no --enable-no-threads-discovery=yes --enable-single-obj-compilation=yes --enable-gcj-support=yes --enable-threads=$libgc_threads --enable-munmap=yes --enable-mmap=yes --enable-ignore-dynamic-loading=yes --enable-dont-register-main-static-data=yes" + AC_CONFIG_SUBDIRS([external/bdwgc]) + fi ;; esac @@ -4925,6 +5005,8 @@ echo " Xamarin.TVOS: $with_monotouch_tv Xamarin.Mac: $with_xammac Windows AOT: $with_winaot + Unity JIT: $with_unityjit + Unity AOT: $with_unityaot Orbis: $with_orbis Unreal: $with_unreal WebAssembly: $with_wasm diff --git a/data/config.in b/data/config.in index 282bc114950a..d03bfe3e8b81 100644 --- a/data/config.in +++ b/data/config.in @@ -10,8 +10,8 @@ - - + + diff --git a/data/net_2_0/machine.config b/data/net_2_0/machine.config index 2f8090f02884..9da7be987191 100644 --- a/data/net_2_0/machine.config +++ b/data/net_2_0/machine.config @@ -118,6 +118,9 @@ + + + diff --git a/data/net_4_0/machine.config b/data/net_4_0/machine.config index 30bc2105f3af..12839c1f6a3c 100644 --- a/data/net_4_0/machine.config +++ b/data/net_4_0/machine.config @@ -135,6 +135,9 @@ + + + diff --git a/data/net_4_5/machine.config b/data/net_4_5/machine.config index d17369394e31..61f7889871f8 100644 --- a/data/net_4_5/machine.config +++ b/data/net_4_5/machine.config @@ -138,6 +138,9 @@ + + + diff --git a/external/bdwgc b/external/bdwgc new file mode 160000 index 000000000000..4d253965d8f1 --- /dev/null +++ b/external/bdwgc @@ -0,0 +1 @@ +Subproject commit 4d253965d8f186c9506ed3b32e9ea9736ee60f14 diff --git a/external/binary-reference-assemblies b/external/binary-reference-assemblies index 9f07d0746d94..e048fe4a88d2 160000 --- a/external/binary-reference-assemblies +++ b/external/binary-reference-assemblies @@ -1 +1 @@ -Subproject commit 9f07d0746d94d2c2b055e3e689814ca07431e5fd +Subproject commit e048fe4a88d237d105ae02fe0363a68296099362 diff --git a/external/buildscripts/Build.bee.cs b/external/buildscripts/Build.bee.cs new file mode 100644 index 000000000000..980859c20be5 --- /dev/null +++ b/external/buildscripts/Build.bee.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using Bee.Core; +using Bee.Stevedore; +using Bee.Stevedore.Program; +using Unity.BuildSystem.NativeProgramSupport; + +namespace BuildProgram +{ + public class BuildProgram + { + private static readonly Dictionary> Artifacts = new Dictionary>(); + + internal static void Main() + { + RegisterCommonArtifacts(); + + if (Platform.HostPlatform is WindowsPlatform) + { + RegisterWindowsArtifacts(); + } + else + { + RegisterCommonNonWindowsArtifacts(); + + if (Platform.HostPlatform is MacOSXPlatform) + { + RegisterOSXArtifacts(); + } + else if (Platform.HostPlatform is LinuxPlatform) + { + RegisterLinuxArtifacts(); + } + } + + foreach (var artifact in Artifacts) + { + var name = artifact.Key; + var id = artifact.Value.Item1; + var repo = new RepoName(artifact.Value.Item2); + + Console.WriteLine($">>> Registering artifact {name}"); + var stevedoreArtifact = new StevedoreArtifact(repo, new ArtifactId(id)); + Backend.Current.Register(stevedoreArtifact); + } + } + + private static void RegisterCommonArtifacts() + { + Artifacts.Add("MonoBleedingEdge", + new Tuple( + "MonoBleedingEdge/f6eb9e15148_2a5566bb437c65d9af40d5f0f7fa4c2cc0f6e6efa243ae673bde2f5b29b03f08.7z", + "unity-internal")); + + Artifacts.Add("reference-assemblies", + new Tuple( + "reference-assemblies/1.0_fc1889ab066ec621a44e51c666d750590b0496d8284b4420e1119c26ce0c7462.7z", + "unity-internal")); + } + + private static void RegisterWindowsArtifacts() + { + Artifacts.Add("android-ndk-windows-x86_64", + new Tuple( + "android-ndk-windows-x86_64/r16b_4c6b39939b29dfd05e27c97caf588f26b611f89fe95aad1c987278bd1267b562.7z", + "unity-internal")); + } + + private static void RegisterOSXArtifacts() + { + Artifacts.Add("android-ndk-darwin-x86_64", + new Tuple( + "android-ndk-darwin-x86_64/r16b_9654a692ed97713e35154bfcacb0028fdc368128d636326f9644ed83eec5d88b.7z", + "unity-internal")); + + Artifacts.Add("MacBuildEnvironment", + new Tuple( + "mac-toolchain-11_0/12.0-12A8158a_2b346a6c93e9c82250a1d88c02f24a5dea9f0bd1aa2ef44109896a44800e8c68.zip", + "unity-internal")); + + Artifacts.Add("mono-build-tools-extra", + new Tuple( + "mono-build-tools-extra/9de3c42ef81ec4f79b53e7db32d390227d8c43c4_fa9931c37b7a4ca636eb9e0e48252c4cb591caaa9b77c41b75795037868c1256.7z", + "unity-internal")); + } + + private static void RegisterLinuxArtifacts() + { + Artifacts.Add("android-ndk-linux-x86_64", + new Tuple( + "android-ndk-linux-x86_64/r16b_bcdea4f5353773b2ffa85b5a9a2ae35544ce88ec5b507301d8cf6a76b765d901.7z", + "unity-internal")); + + Artifacts.Add("linux-sdk-20170609", + new Tuple( + "linux-sdk-20170609/9df1e3b3b120_9a3a0847d5b3767579e908b5a9ce050936617b1b9275a79a8b71bb3229998957.7z", + "unity-internal")); + } + + private static void RegisterCommonNonWindowsArtifacts() + { + Artifacts.Add("libtool-src", + new Tuple( + "libtool-src/2.4.6_49a0ed204b3b24572e044400cd05513f611bcca6ced0d0816a57ac3b17376257.7z", + "public")); + + Artifacts.Add("texinfo-src", + new Tuple( + "texinfo-src/4.8_975b9657ebef8a4fe3897047ca450b757a0a956b05399dc813f63e84829bac6a.7z", + "public")); + + Artifacts.Add("automake-src", + new Tuple( + "automake-src/1.16.1_d281b950e26265f55f0a63188a8c6388e638b354b7ed80d186690119cbc4f953.7z", + "public")); + + Artifacts.Add("autoconf-src", + new Tuple( + "autoconf-src/2.69_0e4ba7a0363c68ad08a7d138b228596aecdaea68e1d8b8eefc645e6ac8fc85c7.7z", + "public")); + + Artifacts.Add("libgdiplus-mac", + new Tuple( + "libgdiplus-mac/9df1e3b3b120_4cf7c08770db93922f54f38d2461b9122cddc898db58585864446e70c5ad3057.7z", + "unity-internal")); + } + } +} \ No newline at end of file diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/cli b/external/buildscripts/add_to_build_results/monodistribution/bin/cli new file mode 100755 index 000000000000..a8360f862951 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/cli @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/cli.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/cli.bat new file mode 100644 index 000000000000..685963d34fc1 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/cli.bat @@ -0,0 +1,7 @@ +@echo off +SETLOCAL +set MONO_PREFIX=%~dp0.. +set MONO=%MONO_PREFIX%\bin\mono +"%MONO%" %* +exit /b %ERRORLEVEL% +ENDLOCAL diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/cli_x86 b/external/buildscripts/add_to_build_results/monodistribution/bin/cli_x86 new file mode 100755 index 000000000000..de796470ff8b --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/cli_x86 @@ -0,0 +1,4 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +arch -i386 $MONO "$@" diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm b/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm new file mode 100755 index 000000000000..c7e0afc7f7c8 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm @@ -0,0 +1,3 @@ +#!/bin/bash +. $(dirname $0)/mono-env +exec $MONO $MONO_OPTIONS $MONO_PREFIX/lib/mono/4.5/ilasm.exe "$@" diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm.bat new file mode 100644 index 000000000000..3b3e4635ee56 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/ilasm.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% "%~dp0..\lib\mono\4.5\ilasm.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/mcs b/external/buildscripts/add_to_build_results/monodistribution/bin/mcs new file mode 100755 index 000000000000..7a48ace18cea --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/mcs @@ -0,0 +1,3 @@ +#!/bin/bash +. $(dirname $0)/mono-env +exec $MONO $MONO_OPTIONS $MONO_PREFIX/lib/mono/4.5/mcs.exe "$@" diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/mcs.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/mcs.bat new file mode 100644 index 000000000000..3ef693d443d3 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/mcs.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% "%~dp0..\lib\mono\4.5\mcs.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/mono-env b/external/buildscripts/add_to_build_results/monodistribution/bin/mono-env new file mode 100755 index 000000000000..eaf4af99c125 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/mono-env @@ -0,0 +1,10 @@ +. $(dirname $0)/monobin-env + +export MONO_PREFIX=$(dirname $0)/.. +MONO=$MONO_PREFIX/$MONOBINDIR/mono +# remove //.. pattern as it causes problem on OSX High Sierra +MONO=${MONO/\/\/\./\/\.} +LD_LIBRARY_PATH=$MONO_PREFIX/lib +[ "$MONOLIBDIR" ] && LD_LIBRARY_PATH="$MONO_PREFIX/$MONOLIBDIR:$LD_LIBRARY_PATH" +export LD_LIBRARY_PATH +[ "$MONOETCDIR" ] && export MONO_CONFIG=$MONO_PREFIX/$MONOETCDIR/mono/config diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/monobin-env b/external/buildscripts/add_to_build_results/monodistribution/bin/monobin-env new file mode 100644 index 000000000000..ab4f5b232f7c --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/monobin-env @@ -0,0 +1,20 @@ +MONOBINDIR=bin +MONOLIBDIR= +MONOETCDIR= + +os=$(uname -s) +variant=$(uname -m) + +case "$os" in + Linux) + if [ "$variant" = "x86_64" ]; then + MONOBINDIR=bin-linux64 + MONOLIBDIR=../embedruntimes/linux64 + MONOETCDIR=etc-linux64 + else + MONOBINDIR=bin-linux32 + MONOLIBDIR=../embedruntimes/linux32 + MONOETCDIR=etc-linux32 + fi + ;; +esac diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker b/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker new file mode 100755 index 000000000000..4da80a1bc55b --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO $MONO_PATH/monolinker.exe "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker.bat new file mode 100644 index 000000000000..013920349f82 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/monolinker.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% "%~dp0..\lib\mono\2.0\monolinker.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console new file mode 100755 index 000000000000..1a72b09096ed --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO --debug $MONO_PREFIX/lib/mono/4.0/nunit-console.exe "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console.bat new file mode 100644 index 000000000000..c21335b65c09 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% --debug "%~dp0..\lib\mono\4.0\nunit-console.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console2 b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console2 new file mode 100755 index 000000000000..17db31ffd0b8 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/nunit-console2 @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO --debug $MONO_PREFIX/lib/mono/2.0/nunit-console.exe "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2 b/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2 new file mode 100755 index 000000000000..20d79ca7d3bd --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2 @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO $MONO_PREFIX/lib/mono/4.5/resgen.exe "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2.bat new file mode 100644 index 000000000000..c3ec2fbb896a --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/resgen2.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% "%~dp0..\lib\mono\4.5\resgen.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild b/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild new file mode 100755 index 000000000000..60ba18fe8bec --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild @@ -0,0 +1,5 @@ +#!/bin/bash +. $(dirname $0)/mono-env + +$MONO $MONO_PREFIX/lib/mono/4.5/xbuild.exe "$@" + diff --git a/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild.bat b/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild.bat new file mode 100644 index 000000000000..50b3a8e44997 --- /dev/null +++ b/external/buildscripts/add_to_build_results/monodistribution/bin/xbuild.bat @@ -0,0 +1,2 @@ +@"%~dp0cli.bat" %MONO_OPTIONS% "%~dp0..\lib\mono\4.5\xbuild.exe" %* +exit /b %ERRORLEVEL% diff --git a/external/buildscripts/bee b/external/buildscripts/bee new file mode 100755 index 000000000000..d7914632c200 --- /dev/null +++ b/external/buildscripts/bee @@ -0,0 +1,9 @@ +#!/bin/sh +attempt=0 +until [ "$attempt" -ge 10 ] +do + mono bee.exe "$@" && break + attempt=$((attempt+1)) + sleep 5 + echo "\nRetry running bee... $attempt" +done \ No newline at end of file diff --git a/external/buildscripts/bee.exe b/external/buildscripts/bee.exe new file mode 100644 index 000000000000..51fe9e6fe252 Binary files /dev/null and b/external/buildscripts/bee.exe differ diff --git a/external/buildscripts/build.pl b/external/buildscripts/build.pl new file mode 100644 index 000000000000..e1dbd234d9f0 --- /dev/null +++ b/external/buildscripts/build.pl @@ -0,0 +1,1736 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; +use lib ('external/buildscripts', "../../Tools/perl_lib","perl_lib", 'external/buildscripts/perl_lib'); +use strict; +use warnings; +use Tools qw(InstallNameTool); + +print ">>> PATH in Build All = $ENV{PATH}\n\n"; + +my $currentdir = getcwd(); + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +$monoroot = abs_path($monoroot); + +my $buildscriptsdir = "$monoroot/external/buildscripts"; +my $addtoresultsdistdir = "$buildscriptsdir/add_to_build_results/monodistribution"; +my $buildsroot = "$monoroot/builds"; +my $includesroot = "$buildsroot/include"; +my $sourcesroot = "$buildsroot/source"; +my $distdir = "$buildsroot/monodistribution"; +my $buildMachine = $ENV{UNITY_THISISABUILDMACHINE}; + +# This script should not be ran on windows, if it is, kindly call the wrapper +# to switch over to cygwin +if ($^O eq "MSWin32") +{ + print(">>> build.pl called from Windows. Switching over to cygwin\n"); + system("perl", "$buildscriptsdir/build_win_wrapper.pl", @ARGV) eq 0 or die("\n"); + exit 0; +} + +system("source","~/.profile"); + +my $build=0; +my $clean=0; +my $jobs=8; +my $test=0; +my $artifact=0; +my $debug=0; +my $disableMcs=0; +my $mcsOnly=0; +my $buildUsAndBoo=0; +my $artifactsCommon=0; +my $artifactsRuntime=1; +my $runRuntimeTests=1; +my $runClasslibTests=1; +my $checkoutOnTheFly=0; +my $forceDefaultBuildDeps=0; +my $existingMonoRootPath = ''; +my $sdk = ''; +my $arch32 = 0; +my $targetArch = ""; +my $winPerl = ""; +my $winMonoRoot = ""; +my $msBuildVersion = "14.0"; +my $buildDeps = ""; +my $android=0; +my $androidArch = ""; +my $iphone=0; +my $iphoneArch = ""; +my $iphoneCross=0; +my $iphoneSimulator=0; +my $iphoneSimulatorArch=""; +my $tizen=0; +my $tizenEmulator=0; +my $windowsSubsystemForLinux=0; +my $stevedoreBuildDeps=1; + +# Handy troubleshooting/niche options +my $skipMonoMake=0; + +# The prefix hack probably isn't needed anymore. Let's disable it by default and see how things go +my $shortPrefix=1; + +# Disabled by default for now. causes more problems than it's worth when actively making changes to the build scripts. +# Would be okay to turn on once the build scripts stabilize and you just want to rebuild code changes +my $enableCacheFile=0; + +# Linux toolchain setup needs this +my @commandPrefix = (); + +print(">>> Build All Args = @ARGV\n"); + +GetOptions( + 'build=i'=>\$build, + 'clean=i'=>\$clean, + 'test=i'=>\$test, + 'artifact=i'=>\$artifact, + 'artifactscommon=i'=>\$artifactsCommon, + 'artifactsruntime=i'=>\$artifactsRuntime, + 'debug=i'=>\$debug, + 'disablemcs=i'=>\$disableMcs, + 'mcsonly=i'=>\$mcsOnly, + 'buildusandboo=i'=>\$buildUsAndBoo, + 'runtimetests=i'=>\$runRuntimeTests, + 'classlibtests=i'=>\$runClasslibTests, + 'arch32=i'=>\$arch32, + 'targetarch=s'=>\$targetArch, + 'jobs=i'=>\$jobs, + 'sdk=s'=>\$sdk, + 'existingmono=s'=>\$existingMonoRootPath, + 'skipmonomake=i'=>\$skipMonoMake, + 'shortprefix=i'=>\$shortPrefix, + 'winperl=s'=>\$winPerl, + 'winmonoroot=s'=>\$winMonoRoot, + 'msbuildversion=s'=>\$msBuildVersion, + 'checkoutonthefly=i'=>\$checkoutOnTheFly, + 'builddeps=s'=>\$buildDeps, + 'forcedefaultbuilddeps=i'=>\$forceDefaultBuildDeps, + 'android=i'=>\$android, + 'androidarch=s'=>\$androidArch, + 'iphone=i'=>\$iphone, + 'iphonearch=s'=>\$iphoneArch, + 'iphonecross=i'=>\$iphoneCross, + 'iphonesimulator=i'=>\$iphoneSimulator, + 'tizen=i'=>\$tizen, + 'tizenemulator=i'=>\$tizenEmulator, + 'windowssubsystemforlinux=i'=>\$windowsSubsystemForLinux, + 'enablecachefile=i'=>\$enableCacheFile, + 'stevedorebuilddeps=i'=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +print ">>> Mono checkout = $monoroot\n"; + +print(">> System Info : \n"); +system("uname", "-a"); + +my $monoRevision = `git rev-parse HEAD`; +chdir("$buildscriptsdir") eq 1 or die ("failed to chdir : $buildscriptsdir\n"); +my $buildScriptsRevision = `git rev-parse HEAD`; +chdir("$monoroot") eq 1 or die ("failed to chdir : $monoroot\n"); + +print(">>> Mono Revision = $monoRevision\n"); +print(">>> Build Scripts Revision = $buildScriptsRevision\n"); + +if ($androidArch ne "") +{ + $android = 1; +} + +if ($iphoneArch ne "") +{ + $iphone = 1; +} + +if($iphoneSimulator) +{ + if ($arch32) + { + $iphoneSimulatorArch = "i386"; + } + else + { + $iphoneSimulatorArch = "x86_64"; + } +} + +my $isDesktopBuild = 1; +if ($android || $iphone || $iphoneCross || $iphoneSimulator || $tizen || $tizenEmulator) +{ + $isDesktopBuild = 0; + + # Disable building of the class libraries by default when building the android runtime + # since we don't care about a class library build in this situation (as of writing this at least) + # but only if the test flag is not set. If the test flag was set, we'd need to build the classlibs + # in order to run the tests + $disableMcs = 1 if(!($test)); +} + +# Do any settings agnostic per-platform stuff +my $externalBuildDeps = ""; + +if ($buildDeps ne "" && not $forceDefaultBuildDeps) +{ + $externalBuildDeps = $buildDeps; +} +else +{ + if($stevedoreBuildDeps) + { + $externalBuildDeps = "$monoroot/external/buildscripts/artifacts/Stevedore"; + } + else + { + $externalBuildDeps = "$monoroot/../../mono-build-deps/build"; + } +} +print(">>> External build deps = $externalBuildDeps\n"); + +# Only clean up the path if the directory exists, if it doesn't exist, +# abs_path ends up returning an empty string +$externalBuildDeps = abs_path($externalBuildDeps) if (-d $externalBuildDeps); + +my $existingExternalMonoRoot = "$externalBuildDeps/MonoBleedingEdge"; +my $existingExternalMono = ""; +my $existingExternalMonoBinDir = ""; +my $monoHostArch = ""; +my $monoprefix = "$monoroot/tmp"; +my $runningOnWindows=0; +if($^O eq "linux") +{ + $monoHostArch = $arch32 ? "i686" : "x86_64"; + $existingExternalMono = "$existingExternalMonoRoot"; + $existingExternalMonoBinDir = "bin-linux64"; +} +elsif($^O eq 'darwin') +{ + $monoHostArch = "x86_64"; + $existingExternalMono = "$existingExternalMonoRoot"; + $existingExternalMonoBinDir = "bin"; + + if ($targetArch eq "arm64") + { + $disableMcs = 1; + $test = 0; + } + + # From Massi: I was getting failures in install_name_tool about space + # for the commands being too small, and adding here things like + # $ENV{LDFLAGS} = '-headerpad_max_install_names' and + # $ENV{LDFLAGS} = '-headerpad=0x40000' did not help at all (and also + # adding them to our final gcc invocation to make the bundle). + # Lucas noticed that I was lacking a Mono prefix, and having a long + # one would give us space, so here is this silly looong prefix. + if (not $shortPrefix) + { + $monoprefix = "$monoroot/tmp/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting/scripting"; + } +} +else +{ + $monoHostArch = "i686"; + $existingExternalMono = "$existingExternalMonoRoot"; + $existingExternalMonoBinDir = "bin-x64"; + $runningOnWindows = 1; + + # We only care about an existing mono if we need to build. + # So only do this path clean up if we are building. + if ($build) + { + if ($existingMonoRootPath ne "" && not $existingMonoRootPath =~ /^\/cygdrive/) + { + $existingMonoRootPath = `cygpath -u $existingMonoRootPath`; + chomp($existingMonoRootPath); + } + + $existingMonoRootPath =~ tr/\\//d; + } +} + +if ($runningOnWindows) +{ + # Fixes a line ending issue that happens on windows when we try to run autogen.sh + $ENV{'SHELLOPTS'} = "igncr"; +} + +print(">>> Existing Mono = $existingMonoRootPath\n"); +print(">>> Mono Arch = $monoHostArch\n"); + +if ($build) +{ + my $platformflags = ''; + my $host = ''; + my $mcs = ''; + + my $iphoneCrossAbi = "arm-apple-darwin10"; + my $iphoneCrossMonoBinToUse = "$monoroot/builds/monodistribution/bin"; + + my @configureparams = (); + + push @configureparams, "--disable-mcs-build" if($disableMcs); + push @configureparams, "--with-glib=embedded"; + push @configureparams, "--disable-nls"; #this removes the dependency on gettext package + push @configureparams, "--disable-btls"; #this removes the dependency on cmake to build btls for now + push @configureparams, "--with-mcs-docs=no"; + push @configureparams, "--prefix=$monoprefix"; + + if(!($disableMcs)) + { + push @configureparams, "--with-unityjit=yes"; + push @configureparams, "--with-unityaot=yes"; + } + + if ($isDesktopBuild) + { + push @configureparams, "--with-monotouch=no"; + } + + if ($existingMonoRootPath eq "") + { + print(">>> No existing mono supplied. Checking for external...\n"); + + if (!(-d "$externalBuildDeps")) + { + if($stevedoreBuildDeps) + { + print(">>> Running bee to download build-deps...\n"); + chdir($buildscriptsdir) eq 1 or die ("failed to chdir to $buildscriptsdir directory\n"); + system("./bee") eq 0 or die ("failed to run bee\n"); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + else + { + if (not $checkoutOnTheFly) + { + print(">>> No external build deps found. Might as well try to check them out. If it fails, we'll continue and trust mono is in your PATH\n"); + } + + # Check out on the fly + print(">>> Checking out mono build dependencies to : $externalBuildDeps\n"); + my $repo = "https://ono.unity3d.com/unity-extra/mono-build-deps"; + print(">>> Cloning $repo at $externalBuildDeps\n"); + my $checkoutResult = system("hg", "clone", $repo, "$externalBuildDeps"); + + if ($checkoutOnTheFly && $checkoutResult ne 0) + { + die("failed to checkout mono build dependencies\n"); + } + } + + # Only clean up if the dir exists. Otherwise abs_path will return empty string + $externalBuildDeps = abs_path($externalBuildDeps) if (-d $externalBuildDeps); + } + + if (-d "$existingExternalMono") + { + print(">>> External mono found at : $existingExternalMono\n"); + + $existingMonoRootPath = "$existingExternalMono/monodistribution"; + } + else + { + print(">>> No external mono found. Trusting a new enough mono is in your PATH.\n"); + } + } + + if ($existingMonoRootPath ne "" && !(-d $existingMonoRootPath)) + { + die("Existing mono not found at : $existingMonoRootPath\n"); + } + + if ($externalBuildDeps ne "") + { + print "\n"; + print ">>> Building autoconf, texinfo, automake, and libtool if needed...\n"; + my $autoconfVersion = "2.69"; + my $texinfoVersion = "4.8"; + my $automakeVersion = "1.16.1"; + my $libtoolVersion = "2.4.6"; + my $autoconfDir = "$externalBuildDeps/autoconf-2-69/autoconf-$autoconfVersion"; + my $texinfoDir = "$externalBuildDeps/texinfo-4-8/texinfo-$texinfoVersion"; + my $automakeDir = "$externalBuildDeps/automake-1-16-1/automake-$automakeVersion"; + my $libtoolDir = "$externalBuildDeps/libtool-2-4-6/libtool-$libtoolVersion"; + my $builtToolsDir = "$externalBuildDeps/built-tools"; + + $ENV{PATH} = "$builtToolsDir/bin:$ENV{PATH}"; + + if ($stevedoreBuildDeps) + { + $autoconfDir = "$externalBuildDeps/autoconf-src/autoconf-$autoconfVersion"; + } + elsif (!(-d "$autoconfDir")) + { + print(">>> Extracting autoconf\n"); + chdir("$externalBuildDeps/autoconf-2-69") eq 1 or die ("failed to chdir to external directory\n"); + system("tar xzf autoconf-$autoconfVersion.tar.gz") eq 0 or die ("failed to extract autoconf\n"); + } + if (-d "$autoconfDir") + { + print(">>> Installing autoconf from $autoconfDir\n"); + chdir("$autoconfDir") eq 1 or die ("failed to chdir to autoconf directory\n"); + system("./configure --prefix=$builtToolsDir") eq 0 or die ("failed to configure autoconf\n"); + system("make") eq 0 or die ("failed to make autoconf\n"); + system("make install") eq 0 or die ("failed to make install autoconf\n"); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + + if ($stevedoreBuildDeps and $windowsSubsystemForLinux) + { + $texinfoDir = "$externalBuildDeps/texinfo-src/texinfo-$texinfoVersion"; + } + elsif (!(-d "$texinfoDir") and $windowsSubsystemForLinux) + { + print(">>> Extracting texinfo\n"); + chdir("$externalBuildDeps/texinfo-4-8") eq 1 or die ("failed to chdir to external directory\n"); + system("tar xzf texinfo-$texinfoVersion.tar.gz") eq 0 or die ("failed to extract texinfo\n"); + } + if (-d "$texinfoDir") + { + print(">>> Installing texinfo from $texinfoDir\n"); + chdir($texinfoDir) eq 1 or die ("failed to chdir to texinfo directory\n"); + system("./configure --prefix=$builtToolsDir") eq 0 or die ("failed to configure texinfo\n"); + system("make") eq 0 or die ("failed to make texinfo\n"); + system("make install") eq 0 or die ("failed to make install texinfo\n"); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + + if ($stevedoreBuildDeps) + { + $automakeDir = "$externalBuildDeps/automake-src/automake-$automakeVersion"; + } + elsif (!(-d "$automakeDir")) + { + print(">>> Extracting automake\n"); + chdir("$externalBuildDeps/automake-1-16-1") eq 1 or die ("failed to chdir to external directory\n"); + system("tar xzf automake-$automakeVersion.tar.gz") eq 0 or die ("failed to extract automake\n"); + } + if (-d "$automakeDir") + { + my $automakeMakeFlags = ""; + print(">>> Installing automake from $automakeDir\n"); + chdir("$automakeDir") eq 1 or die ("failed to chdir to automake directory\n"); + if($windowsSubsystemForLinux) + { + #Windows subsystem needs to run bootstrap, and make needs to be run with -i due to one doc failing to build + system("./bootstrap") eq 0 or die ("failed to bootstrap automake\n"); + $automakeMakeFlags = "-i"; + } + system("./configure --prefix=$builtToolsDir") eq 0 or die ("failed to configure automake\n"); + system("make $automakeMakeFlags") eq 0 or die ("failed to make automake\n"); + system("make install"); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + + if ($stevedoreBuildDeps) + { + $libtoolDir = "$externalBuildDeps/libtool-src/libtool-$libtoolVersion"; + } + elsif (!(-d "$libtoolDir")) + { + print(">>> Extracting libtool\n"); + chdir("$externalBuildDeps/libtool-2-4-6") eq 1 or die ("failed to chdir to external directory\n"); + system("tar xzf libtool-$libtoolVersion.tar.gz") eq 0 or die ("failed to extract libtool\n"); + } + if (-d "$libtoolDir") + { + print(">>> Installing libtool from $libtoolDir\n"); + chdir("$libtoolDir") eq 1 or die ("failed to chdir to libtool directory\n"); + system("./configure --prefix=$builtToolsDir") eq 0 or die ("failed to configure libtool\n"); + system("make") eq 0 or die ("failed to make libtool\n"); + system("make install") eq 0 or die ("failed to make install libtool\n"); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + + $ENV{'LIBTOOLIZE'} = "$builtToolsDir/bin/libtoolize"; + $ENV{'LIBTOOL'} = "$builtToolsDir/bin/libtool"; + } + + my $macSdkPath = ""; + my $macversion = '10.12'; + my $darwinVersion = "10"; + if ($^O eq 'darwin') + { + $sdk='11.0'; + $macSdkPath = "$externalBuildDeps/mac-toolchain-11_0/MacOSX$sdk.sdk"; + } + + if ($iphone || $iphoneSimulator) + { + if ($runningOnWindows) + { + die("This build is not supported on Windows\n"); + } + + my $iosBuildEnvDir = "$externalBuildDeps/iOSBuildEnvironment"; + my $iosXcodeDefaultToolchainRoot = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain"; + + if (! -d "$iosBuildEnvDir/builds") + { + print(">>> Unzipping ios build toolchain\n"); + system("unzip", '-qd', "$iosBuildEnvDir/builds", "$iosBuildEnvDir/builds.zip") eq 0 or die ("failed unzipping ios build toolchain\n"); + } + + $ENV{PATH} = "$iosXcodeDefaultToolchainRoot/usr/bin:$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/usr/bin:$ENV{PATH}"; + # Need to keep our libtool in front + $ENV{PATH} = "$externalBuildDeps/built-tools/bin:$ENV{PATH}"; + + push @configureparams, "--cache-file=iphone-$iphoneArch.cache" if ($enableCacheFile); + + my $iosMinimalCommon = "com,remoting,shared_perfcounters,appdomains"; + my $iosCFlagsCommon = "-DMONOTOUCH -DHOST_IOS -DDISABLE_POLICY_EVIDENCE=1 -DDISABLE_PROCESS_HANDLING=1"; + + push @configureparams, "--with-tls=pthread"; + push @configureparams, "--without-ikvm-native"; + push @configureparams, "--disable-executables"; + push @configureparams, "--disable-visibility-hidden"; + + if ($iphone) + { + my $iosSdkVersion = "9.3"; + my $iphoneOsMinVersion = "3.0"; + my $iosSdkRoot = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$iosSdkVersion.sdk"; + + print(">>> iOS Build Environment = $iosBuildEnvDir\n"); + print(">>> iOS SDK Version = $iosSdkVersion\n"); + print(">>> iOS SDK Root = $iosSdkRoot\n"); + print(">>> iPhone Arch = $iphoneArch\n"); + + $ENV{PATH} = "$iosSdkRoot/usr/bin:$ENV{PATH}"; + + $ENV{C_INCLUDE_PATH} = "$iosSdkRoot/usr/include"; + $ENV{CPLUS_INCLUDE_PATH} = "$iosSdkRoot/usr/include"; + + $ENV{CC} = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch $iphoneArch"; + $ENV{CXX} = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch $iphoneArch"; + $ENV{LD} = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld"; + + $ENV{CFLAGS} = "$iosCFlagsCommon -gdwarf-2 -DSMALL_CONFIG -DHAVE_LARGE_FILE_SUPPORT=1 -DHAVE_ARMV6=1 -DARM_FPU_VFP=1 -Wl,-application_extension -miphoneos-version-min=$iphoneOsMinVersion -mno-thumb -Os -isysroot $iosSdkRoot"; + + # Unity defines + $ENV{CFLAGS} = "-DPLATFORM_IPHONE $ENV{CFLAGS}"; + + $ENV{CXXFLAGS} = "$ENV{CFLAGS} -U__powerpc__ -U__i386__ -D__arm__"; + $ENV{CPPFLAGS} = $ENV{CXXFLAGS}; + + $ENV{LDFLAGS} = "-arch $iphoneArch -liconv -lobjc -lc++ -Wl,-syslibroot,$iosSdkRoot"; + + print "\n"; + print ">>> Environment:\n"; + print ">>> \tCC = $ENV{CC}\n"; + print ">>> \tCXX = $ENV{CXX}\n"; + print ">>> \tLD = $ENV{LD}\n"; + print ">>> \tCFLAGS = $ENV{CFLAGS}\n"; + print ">>> \tCXXFLAGS = $ENV{CXXFLAGS}\n"; + print ">>> \tCPPFLAGS = $ENV{CPPFLAGS}\n"; + print ">>> \tLDFLAGS = $ENV{LDFLAGS}\n"; + print ">>> \tCPLUS_INCLUDE_PATH = $ENV{CPLUS_INCLUDE_PATH}\n"; + print ">>> \tC_INCLUDE_PATH = $ENV{C_INCLUDE_PATH}\n"; + + push @configureparams, "--host=arm-apple-darwin$darwinVersion"; + + push @configureparams, "--with-sigaltstack=no"; + push @configureparams, "--disable-shared-handles"; + push @configureparams, "--with-monotouch"; + + push @configureparams, "--enable-llvm-runtime"; + push @configureparams, "--with-bitcode=yes"; + + push @configureparams, "--with-lazy-gc-thread-creation=yes"; + push @configureparams, "--enable-icall-export"; + push @configureparams, "--enable-dtrace=no"; + + push @configureparams, "--enable-minimal=$iosMinimalCommon,ssa,jit,reflection_emit_save,reflection_emit,portability,assembly_remapping,attach,verifier,full_messages,security,sgen_remset,sgen_marksweep_par,sgen_marksweep_fixed,sgen_marksweep_fixed_par,sgen_copying,logging"; + + push @configureparams, "mono_cv_uscore=yes"; + push @configureparams, "cv_mono_sizeof_sunpath=104"; + push @configureparams, "ac_cv_func_posix_getpwuid_r=yes"; + push @configureparams, "ac_cv_func_backtrace_symbols=no"; + push @configureparams, "ac_cv_func_finite=no"; + push @configureparams, "ac_cv_header_curses_h=no"; + } + elsif ($iphoneSimulator) + { + my $iosSdkVersion = "9.3"; + my $iosSimMinVersion = "4.3"; + my $iosSdkRoot = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator$iosSdkVersion.sdk"; + + print(">>> iOS Sim Build Environment = $iosBuildEnvDir\n"); + print(">>> iOS Sim SDK Version = $iosSdkVersion\n"); + print(">>> iOS Sim SDK Root = $iosSdkRoot\n"); + print(">>> iOS Sim Arch = $iphoneSimulatorArch\n"); + + $ENV{PATH} = "$iosSdkRoot/usr/bin:$ENV{PATH}"; + + $ENV{MACSDKOPTIONS} = "$iosCFlagsCommon -D_XOPEN_SOURCE=1 -g -O0 -DTARGET_IPHONE_SIMULATOR -mios-simulator-version-min=$iosSimMinVersion -isysroot $iosSdkRoot"; + $ENV{CFLAGS} = "-arch $iphoneSimulatorArch $ENV{MACSDKOPTIONS}"; + $ENV{CXXFLAGS} = "$ENV{CFLAGS}"; + $ENV{CPPFLAGS} = "$ENV{CFLAGS}"; + $ENV{CC} = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/usr/bin/gcc"; + $ENV{CXX} = "$iosBuildEnvDir/builds/Xcode.app/Contents/Developer/usr/bin/g++"; + + print "\n"; + print ">>> Environment:\n"; + print ">>> \tCC = $ENV{CC}\n"; + print ">>> \tCXX = $ENV{CXX}\n"; + print ">>> \tLD = $ENV{LD}\n"; + print ">>> \tCFLAGS = $ENV{CFLAGS}\n"; + print ">>> \tCXXFLAGS = $ENV{CXXFLAGS}\n"; + print ">>> \tCPPFLAGS = $ENV{CPPFLAGS}\n"; + print ">>> \tMACSDKOPTIONS = $ENV{MACSDKOPTIONS}\n"; + + push @configureparams, "--host=$iphoneSimulatorArch-apple-darwin$darwinVersion"; + push @configureparams, "--enable-minimal=$iosMinimalCommon"; + + push @configureparams, "mono_cv_uscore=yes"; + push @configureparams, "ac_cv_func_clock_nanosleep=no"; + } + else + { + die("This should not be hit\n"); + } + } + elsif ($iphoneCross) + { + if ($runningOnWindows) + { + die("Not implemented\n"); + } + else + { + $ENV{CFLAGS} = "-DMONOTOUCH -DARM_FPU_VFP=1 -DUSE_MUNMAP -DPLATFORM_IPHONE_XCOMP -mmacosx-version-min=$macversion"; + $ENV{CXXFLAGS} = "-mmacosx-version-min=$macversion -stdlib=libc++"; + $ENV{CPPFLAGS} = "$ENV{CFLAGS} -mmacosx-version-min=$macversion"; + + $ENV{CC} = "$macSdkPath/../usr/bin/clang -arch i386"; + $ENV{CXX} = "$macSdkPath/../usr/bin/clang++ -arch i386"; + $ENV{CPP} = "$ENV{CC} -E"; + $ENV{LD} = $ENV{CC}; + $ENV{LDFLAGS} = "-stdlib=libc++"; + $ENV{MACSDKOPTIONS} = "-mmacosx-version-min=$macversion -isysroot $macSdkPath"; + + print "\n"; + print ">>> Environment:\n"; + print ">>> \tCC = $ENV{CC}\n"; + print ">>> \tCXX = $ENV{CXX}\n"; + print ">>> \tLD = $ENV{LD}\n"; + print ">>> \tCFLAGS = $ENV{CFLAGS}\n"; + print ">>> \tCXXFLAGS = $ENV{CXXFLAGS}\n"; + print ">>> \tCPPFLAGS = $ENV{CPPFLAGS}\n"; + print ">>> \tLDFLAGS = $ENV{LDFLAGS}\n"; + print ">>> \tMACSDKOPTIONS = $ENV{MACSDKOPTIONS}\n"; + + push @configureparams, "--cache-file=iphone-cross.cache" if ($enableCacheFile); + + push @configureparams, "--with-sigaltstack=no"; + push @configureparams, "--disable-shared-handles"; + push @configureparams, "--with-tls=pthread"; + + push @configureparams, "--target=arm-darwin"; + push @configureparams, "--with-macversion=$macversion"; + push @configureparams, "--with-cross-offsets=$iphoneCrossAbi.h"; + + push @configureparams, "--build=i386-apple-darwin10"; + push @configureparams, "--disable-libraries"; + push @configureparams, "--enable-icall-symbol-map"; + push @configureparams, "--enable-minimal=com,remoting"; + + #push @configureparams, "--enable-llvm"; + #push @configureparams, "--with-llvm=llvm/usr"; + + my @mcsArgs = (); + push @mcsArgs, "$monoroot/tools/offsets-tool/MonoAotOffsetsDumper.cs"; + push @mcsArgs, "$monoroot/mcs/class/Mono.Options/Mono.Options/Options.cs"; + push @mcsArgs, "/r:$externalBuildDeps/CppSharpBinaries/CppSharp.AST.dll"; + push @mcsArgs, "/r:$externalBuildDeps/CppSharpBinaries/CppSharp.Generator.dll"; + push @mcsArgs, "/r:$externalBuildDeps/CppSharpBinaries/CppSharp.Parser.CSharp.dll"; + push @mcsArgs, "/r:$externalBuildDeps/CppSharpBinaries/CppSharp.Parser.dll"; + push @mcsArgs, "/r:$externalBuildDeps/CppSharpBinaries/CppSharp.dll"; + push @mcsArgs, "/debug"; + push @mcsArgs, "/nowarn:0436"; + push @mcsArgs, "/out:$monoroot/tools/offsets-tool/MonoAotOffsetsDumper.exe"; + + print ">>> Compiling MonoAotOffsetDumper : $iphoneCrossMonoBinToUse/mcs @mcsArgs\n"; + system("$iphoneCrossMonoBinToUse/mcs", @mcsArgs) eq 0 or die("failed to compile MonoAotOffsetsDumper\n"); + + # clean up any pre-existing offset header just in case + if (-f "$monoroot/$iphoneCrossAbi.h") + { + system("rm", "-rf", "$iphoneCrossAbi.h"); + } + } + } + elsif ($android) + { + if (!(-d $externalBuildDeps)) + { + die("mono build deps are required and the directory was not found : $externalBuildDeps\n"); + } + + my $ndkVersion = "r16b"; + my $apiLevel = 16; + my $hostTriple = ""; + my $platformRootPostfix = ""; + my $useKraitPatch = 1; + my $kraitPatchPath = "$monoroot/../../android_krait_signal_handler/build"; + + $ENV{ANDROID_PLATFORM} = "android-$apiLevel"; + + if ($androidArch eq "armv7a") + { + $hostTriple = "arm-linux-androideabi"; + $platformRootPostfix = "arm"; + } + elsif ($androidArch eq "x86") + { + $hostTriple = "i686-linux-android"; + $platformRootPostfix = "x86"; + $useKraitPatch = 0; + } + else + { + die("Unsupported android architecture: $androidArch\n"); + } + + if ($^O eq "linux") + { + $ENV{HOST_ENV} = "linux"; + } + elsif ($^O eq 'darwin') + { + $ENV{HOST_ENV} = "darwin"; + } + else + { + $ENV{HOST_ENV} = "windows"; + } + + print "\n"; + print(">>> Android Platform = $ENV{ANDROID_PLATFORM}\n"); + print(">>> Android NDK Version = $ndkVersion\n"); + + my $ndkName = ""; + if($^O eq "linux") + { + $ndkName = "android-ndk-linux-x86_64/android-ndk-$ndkVersion"; + } + elsif($^O eq "darwin") + { + $ndkName = "android-ndk-darwin-x86_64/android-ndk-$ndkVersion"; + } + else + { + $ndkName = "android-ndk-windows-x86_64/android-ndk-$ndkVersion"; + } + + my $depsNdkFinal = "$externalBuildDeps/$ndkName"; + + print(">>> Android NDK Destination = $depsNdkFinal\n"); + print("\n"); + + $ENV{ANDROID_NDK_ROOT} = "$depsNdkFinal"; + + if(!(-d $depsNdkFinal)) + { + die("Android NDK not found\n"); + } + + if (!(-f "$ENV{ANDROID_NDK_ROOT}/ndk-build")) + { + die("Something went wrong. $ENV{ANDROID_NDK_ROOT} does not contain ndk-build\n"); + } + + my $androidNdkRoot = $ENV{ANDROID_NDK_ROOT}; + my $androidPlatformRoot = "$androidNdkRoot/platforms/$ENV{ANDROID_PLATFORM}/arch-$platformRootPostfix"; + + my $androidToolchain = "$androidNdkRoot/toolchains/$hostTriple-clang"; + + print(">>> Generating android toolchain\n"); + system("$androidNdkRoot/build/tools/make_standalone_toolchain.py --arch $platformRootPostfix --api $apiLevel --install-dir $androidToolchain"); + + if ($runningOnWindows) + { + $androidPlatformRoot = `cygpath -w $androidPlatformRoot`; + # clean up trailing new lines that end up in the output from cygpath. + $androidPlatformRoot =~ s/\n+$//; + # Switch over to forward slashes. They propagate down the toolchain correctly + $androidPlatformRoot =~ s/\\/\//g; + + # this will get passed as a path to the linker, so we need to windows-ify the path + $kraitPatchPath = `cygpath -w $kraitPatchPath`; + $kraitPatchPath =~ s/\n+$//; + $kraitPatchPath =~ s/\\/\//g; + } + + print(">>> Android Arch = $androidArch\n"); + print(">>> Android NDK Root = $androidNdkRoot\n"); + print(">>> Android Platform Root = $androidPlatformRoot\n"); + print(">>> Android Toolchain = $androidToolchain\n"); + + if (!(-d "$androidToolchain")) + { + die("Failed to locate android toolchain\n"); + } + + if (!(-d "$androidPlatformRoot")) + { + die("Failed to locate android platform root\n"); + } + + if ($androidArch eq "armv7a") + { + $ENV{CFLAGS} = "-DARM_FPU_VFP=1 -march=armv7-a -target armv7-none-linux-androideabi -DHAVE_ARMV6=1 -funwind-tables $ENV{CFLAGS}"; + $ENV{LDFLAGS} = "-Wl,--fix-cortex-a8 -Wl,-rpath-link=$androidPlatformRoot/usr/lib $ENV{LDFLAGS}"; + } + + my $compilerSysroot = "$androidNdkRoot/sysroot"; + my $archISystem = "$compilerSysroot/usr/include/$hostTriple"; + my $unifiedISystem = "$compilerSysroot/usr/include"; + + $ENV{CC} = "$androidToolchain/bin/clang -v -isystem $archISystem -isystem $unifiedISystem"; + $ENV{CXX} = "$androidToolchain/bin/clang++ -isystem $archISystem -isystem $unifiedISystem"; + $ENV{CPP} = "$androidToolchain/bin/clang -E -isystem $archISystem -isystem $unifiedISystem"; + $ENV{CXXCPP} = "$androidToolchain/bin/clang++ -E -isystem $archISystem -isystem $unifiedISystem"; + + $ENV{CPATH} = "$androidPlatformRoot/usr/include"; + + $ENV{LD} = "$androidToolchain/bin/$hostTriple-ld"; + $ENV{AS} = "$androidToolchain/bin/$hostTriple-as"; + $ENV{AR} = "$androidToolchain/bin/$hostTriple-ar"; + $ENV{RANLIB} = "$androidToolchain/bin/$hostTriple-ranlib"; + $ENV{STRIP} = "$androidToolchain/bin/$hostTriple-strip"; + + $ENV{CFLAGS} = "-DANDROID -D__ANDROID_API__=16 -DPLATFORM_ANDROID -DLINUX -D__linux__ -DHAVE_USR_INCLUDE_MALLOC_H -D_POSIX_PATH_MAX=256 -DS_IWRITE=S_IWUSR -DHAVE_PTHREAD_MUTEX_TIMEDLOCK -fpic -g -ffunction-sections -fdata-sections $ENV{CFLAGS}"; + $ENV{CXXFLAGS} = $ENV{CFLAGS}; + $ENV{CPPFLAGS} = $ENV{CFLAGS}; + + if ($useKraitPatch) + { + $ENV{LDFLAGS} = "-Wl,--wrap,sigaction -L$kraitPatchPath/obj/local/armeabi-v7a -lkrait-signal-handler $ENV{LDFLAGS}"; + } + + $ENV{LDFLAGS} = "--sysroot=$androidPlatformRoot -Wl,--no-undefined -Wl,--gc-sections -ldl -lm -llog -lc $ENV{LDFLAGS}"; + + print "\n"; + print ">>> Environment:\n"; + print ">>> \tCC = $ENV{CC}\n"; + print ">>> \tCXX = $ENV{CXX}\n"; + print ">>> \tCPP = $ENV{CPP}\n"; + print ">>> \tCXXCPP = $ENV{CXXCPP}\n"; + print ">>> \tCPATH = $ENV{CPATH}\n"; + print ">>> \tLD = $ENV{LD}\n"; + print ">>> \tAS = $ENV{AS}\n"; + print ">>> \tAR = $ENV{AR}\n"; + print ">>> \tRANLIB = $ENV{RANLIB}\n"; + print ">>> \tSTRIP = $ENV{STRIP}\n"; + print ">>> \tCFLAGS = $ENV{CFLAGS}\n"; + print ">>> \tCXXFLAGS = $ENV{CXXFLAGS}\n"; + print ">>> \tCPPFLAGS = $ENV{CPPFLAGS}\n"; + print ">>> \tLDFLAGS = $ENV{LDFLAGS}\n"; + + if ($useKraitPatch) + { + my $kraitPatchRepo = "git://github.com/Unity-Technologies/krait-signal-handler.git"; + if (-d "$kraitPatchPath") + { + print ">>> Krait patch repository already cloned\n"; + } + else + { + system("git", "clone", "--branch", "master", "$kraitPatchRepo", "$kraitPatchPath") eq 0 or die ('failing cloning Krait patch'); + } + + chdir("$kraitPatchPath") eq 1 or die ("failed to chdir to krait patch directory\n"); + system('$ANDROID_NDK_ROOT/ndk-build clean') eq 0 or die ('failing to clean Krait patch'); + system('$ANDROID_NDK_ROOT/ndk-build') eq 0 or die ('failing to build Krait patch'); + chdir("$monoroot") eq 1 or die ("failed to chdir to $monoroot\n"); + } + + push @configureparams, "--host=$hostTriple"; + + push @configureparams, "--cache-file=android-$androidArch.cache" if ($enableCacheFile); + + push @configureparams, "--disable-parallel-mark"; + push @configureparams, "--disable-shared-handles"; + push @configureparams, "--with-sigaltstack=no"; + push @configureparams, "--with-tls=pthread"; + push @configureparams, "--disable-visibility-hidden"; + push @configureparams, "mono_cv_uscore=yes"; + push @configureparams, "ac_cv_header_zlib_h=no" if($runningOnWindows); + } + elsif ($tizen) + { + if (!(-d $externalBuildDeps)) + { + die("mono build deps are required and the directory was not found : $externalBuildDeps\n"); + } + + my $sdkVersion = "2.4.0r1"; + my $isArmArch = 1; + + $isArmArch = 0 if ($tizenEmulator); + + $ENV{TIZEN_PLATFORM} = "tizen-2.4"; + + if ($tizenEmulator) + { + $ENV{TIZEN_ROOTSTRAP} = "mobile-2.4-emulator.core"; + } + else + { + $ENV{TIZEN_ROOTSTRAP} = "mobile-2.4-device.core"; + } + + if ($^O eq "linux") + { + $ENV{HOST_ENV} = "linux"; + } + elsif ($^O eq 'darwin') + { + $ENV{HOST_ENV} = "darwin"; + } + else + { + $ENV{HOST_ENV} = "windows"; + } + + print "\n"; + print(">>> Tizen Platform = $ENV{TIZEN_PLATFORM}\n"); + print(">>> Tizen SDK Version = $sdkVersion\n"); + + my $sdkName = "tizen-$sdkVersion-$ENV{HOST_ENV}.tar.bz2"; + my $depsSdkArchive = "$externalBuildDeps/$sdkName"; + my $depsSdkFinal = "$externalBuildDeps/tizen-$sdkVersion-$ENV{HOST_ENV}"; + + print(">>> Tizen SDK Archive = $depsSdkArchive\n"); + print(">>> Tizen SDK Extraction Destination = $depsSdkFinal\n"); + print("\n"); + + $ENV{TIZEN_SDK_ROOT} = "$depsSdkFinal"; + + if (-d $depsSdkFinal) + { + print(">>> Tizen SDK already extracted\n"); + } + else + { + print(">>> Tizen SDK needs to be extracted\n"); + + if ($runningOnWindows) + { + my $sevenZip = "$externalBuildDeps/7za-win-x64/7za.exe"; + my $winDepsSdkArchive = `cygpath -w $depsSdkArchive`; + my $winDepsSdkExtract = `cygpath -w $externalBuildDeps`; + + # clean up trailing new lines that end up in the output from cygpath. If left, they cause problems down the line + # for 7zip + $winDepsSdkArchive =~ s/\n+$//; + $winDepsSdkExtract =~ s/\n+$//; + + system($sevenZip, "x", "$winDepsSdkArchive", "-o$winDepsSdkExtract"); + } + else + { + my ($name,$path,$suffix) = fileparse($depsSdkArchive, qr/\.[^.]*/); + + print(">>> Tizen SDK Extension = $suffix\n"); + + if (lc $suffix eq '.bz2') + { chmod(0755, $depsSdkArchive); + system("tar xjf $depsSdkArchive -C $externalBuildDeps") eq 0 or die ("failed to extract Tizen SDK\n"); + } + else + { + die "Unknown file extension '" . $suffix . "'\n"; + } + } + } + + if (!(-f "$ENV{TIZEN_SDK_ROOT}/tools/sdb")) + { + die("Something went wrong with the SDK extraction\n"); + } + + my $tizenSdkRoot = $ENV{TIZEN_SDK_ROOT}; + my $tizenPlatformRoot = "$tizenSdkRoot/platforms/$ENV{TIZEN_PLATFORM}/mobile/rootstraps/$ENV{TIZEN_ROOTSTRAP}"; + my $tizenToolchain = "$tizenSdkRoot/tools/llvm-3.6/bin"; + + if ($runningOnWindows) + { + $tizenPlatformRoot = `cygpath -w $tizenPlatformRoot`; + # clean up trailing new lines that end up in the output from cygpath. + $tizenPlatformRoot =~ s/\n+$//; + # Switch over to forward slashes. They propagate down the toolchain correctly + $tizenPlatformRoot =~ s/\\/\//g; + } + + if ($tizenEmulator) + { + $tizenToolchain = "$tizenSdkRoot/tools/i386-linux-gnueabi-gcc-4.9/bin/i386"; + $ENV{CFLAGS} = "-Os -g -march=i686 -msse2 -mfpmath=sse"; + } + else + { + $tizenToolchain = "$tizenSdkRoot/tools/arm-linux-gnueabi-gcc-4.9/bin/arm"; + $ENV{CFLAGS} = "-Os -g -march=armv7-a -mfpu=vfp -mfloat-abi=softfp -DARM_FPU_VFP=1 -DHAVE_ARMV6=1"; + $ENV{LDFLAGS} = "-Wl,-rpath-link=$tizenPlatformRoot/usr/lib -L$tizenPlatformRoot/usr/lib $ENV{LDFLAGS}"; + } + + print(">>> Tizen SDK Root = $tizenSdkRoot\n"); + print(">>> Tizen Platform Root = $tizenPlatformRoot\n"); + print(">>> Tizen Toolchain Prefix = $tizenToolchain\n"); + + if (!(-d "$tizenPlatformRoot")) + { + die("Failed to locate Tizen platform root\n"); + } + + $ENV{PATH} = "$tizenToolchain/bin:$ENV{PATH}"; + $ENV{CC} = "$tizenToolchain-linux-gnueabi-gcc --sysroot=$tizenPlatformRoot"; + $ENV{CXX} = "$tizenToolchain-linux-gnueabi-g++ --sysroot=$tizenPlatformRoot"; + $ENV{CPP} = "$tizenToolchain-linux-gnueabi-cpp"; + $ENV{CXXCPP} = "$tizenToolchain-linux-gnueabi-cpp"; + $ENV{CPATH} = "$tizenPlatformRoot/usr/include"; + $ENV{LD} = "$tizenToolchain-linux-gnueabi-ld --sysroot=$tizenPlatformRoot"; + $ENV{AS} = "$tizenToolchain-linux-gnueabi-as"; + $ENV{STRIP} = "$tizenToolchain-linux-gnueabi-strip"; + + if ($tizenEmulator) + { + $ENV{AR} = "$ENV{TIZEN_SDK_ROOT}/tools/i386-linux-gnueabi-gcc-4.9/bin/i386-linux-gnueabi-ar"; + $ENV{RANLIB} = "$ENV{TIZEN_SDK_ROOT}/tools/i386-linux-gnueabi-gcc-4.9/bin/i386-linux-gnueabi-ranlib"; + } + else + { + $ENV{AR} = "$ENV{TIZEN_SDK_ROOT}/tools/arm-linux-gnueabi-gcc-4.9/bin/arm-linux-gnueabi-ar"; + $ENV{RANLIB} = "$ENV{TIZEN_SDK_ROOT}/tools/arm-linux-gnueabi-gcc-4.9/bin/arm-linux-gnueabi-ranlib"; + } + + $ENV{CFLAGS} = "-DTIZEN -DLINUX -D__linux__ -DHAVE_USR_INCLUDE_MALLOC_H -DPAGE_SIZE=0x1000 -D_POSIX_PATH_MAX=256 -DS_IWRITE=S_IWUSR -DHAVE_PTHREAD_MUTEX_TIMEDLOCK -fpic -g -ffunction-sections -fdata-sections $ENV{CFLAGS}"; + $ENV{CXXFLAGS} = $ENV{CFLAGS}; + $ENV{CPPFLAGS} = $ENV{CFLAGS}; + $ENV{LDFLAGS} = "-Wl,--no-undefined -ldlog -shared -Xlinker --as-needed $ENV{LDFLAGS}"; + + print "\n"; + print ">>> Environment:\n"; + print ">>> \tCC = $ENV{CC}\n"; + print ">>> \tCXX = $ENV{CXX}\n"; + print ">>> \tCPP = $ENV{CPP}\n"; + print ">>> \tCXXCPP = $ENV{CXXCPP}\n"; + print ">>> \tCPATH = $ENV{CPATH}\n"; + print ">>> \tLD = $ENV{LD}\n"; + print ">>> \tAS = $ENV{AS}\n"; + print ">>> \tAR = $ENV{AR}\n"; + print ">>> \tRANLIB = $ENV{RANLIB}\n"; + print ">>> \tSTRIP = $ENV{STRIP}\n"; + print ">>> \tCFLAGS = $ENV{CFLAGS}\n"; + print ">>> \tCXXFLAGS = $ENV{CXXFLAGS}\n"; + print ">>> \tCPPFLAGS = $ENV{CPPFLAGS}\n"; + print ">>> \tLDFLAGS = $ENV{LDFLAGS}\n"; + + if ($tizenEmulator) + { + push @configureparams, "--host=i386-tizen-linux-gnueabi"; + } + else + { + push @configureparams, "--host=arm-tizen-linux-gnueabi"; + } + + push @configureparams, "--cache-file=tizen-cross.cache" if ($enableCacheFile); + push @configureparams, "--disable-parallel-mark"; + push @configureparams, "--disable-shared-handles"; + push @configureparams, "--with-sigaltstack=no"; + push @configureparams, "--with-tls=pthread"; + push @configureparams, "--disable-visibility-hidden"; + push @configureparams, "--disable-executables"; + push @configureparams, "--with-gnu-ld=yes"; + push @configureparams, "mono_cv_uscore=yes"; + push @configureparams, "ac_cv_header_zlib_h=no" if($runningOnWindows); + } + elsif($^O eq "linux") + { + if (!(-d $externalBuildDeps)) + { + die("mono build deps are required and the directory was not found : $externalBuildDeps\n"); + } + + if($ENV{UNITY_THISISABUILDMACHINE} || $ENV{UNITY_USE_LINUX_SDK}) + { + my $sdkVersion = '20170609'; + my $schroot = "LinuxBuildEnvironment-$sdkVersion"; + my @linuxToolchain = ('schroot', '-c', $schroot, '--'); + + print "\n"; + print(">>> Linux SDK Version = $sdkVersion\n"); + + my $sdkName = "linux-sdk-$sdkVersion.tar.bz2"; + my $depsSdkArchive = "$externalBuildDeps/linux-sdk-$sdkVersion/$sdkName"; + my $depsSdkFinal = "$externalBuildDeps/linux-sdk-$sdkVersion/linux-sdk-$sdkVersion"; + + print(">>> Linux SDK Archive = $depsSdkArchive\n"); + print(">>> Linux SDK Extraction Destination = $depsSdkFinal\n"); + print("\n"); + + my $linuxSdkRoot = $depsSdkFinal; + + if (-d $depsSdkFinal) + { + print(">>> Linux SDK already extracted\n"); + } + else + { + print(">>> Linux SDK needs to be extracted\n"); + system('mkdir', '-p', $depsSdkFinal) eq 0 or die ("failed to create directory $depsSdkFinal\n"); + system('tar', 'xaf', $depsSdkArchive, '-C', $depsSdkFinal) eq 0 or die ("failed to extract Linux SDK\n"); + system('sudo', 'cp', '-R', "$depsSdkFinal/linux-sdk-$sdkVersion", '/etc/schroot') eq 0 or die ("failed to copy SDK\n"); + + if($ENV{YAMATO_PROJECT_ID}) + { + system('sudo', 'cp', "$monoroot/.yamato/config/LinuxBuildEnvironment-20170609.conf", '/etc/schroot/chroot.d/') eq 0 or die ("failed to copy conf file\n"); + system('sudo', 'cat', '/etc/schroot/chroot.d/LinuxBuildEnvironment-20170609.conf') eq 0 or die ("failed to list contents on /etc/schroot/chroot.d\n"); + } + else + { + system("sed 's,^directory=.*,directory=$depsSdkFinal/$schroot,' \"$depsSdkFinal/$schroot.conf\" | sudo tee /etc/schroot/chroot.d/$schroot.conf") eq 0 or die ("failed to deploy Linux SDK\n"); + } + } + + @commandPrefix = @linuxToolchain; + print(">>> Linux SDK Root = $linuxSdkRoot\n"); + print(">>> Linux Toolchain Command Prefix = " . join(' ', @commandPrefix) . "\n"); + } + + push @configureparams, "--host=$monoHostArch-pc-linux-gnu"; + + push @configureparams, "--disable-parallel-mark"; #this causes crashes + + my $archflags = ''; + if ($arch32) + { + $archflags = '-m32'; + } + else + { + $archflags = '-fPIC'; + } + + if ($debug) + { + $ENV{CFLAGS} = "$archflags -g -O0"; + } + else + { + $ENV{CFLAGS} = "$archflags -Os"; #optimize for size + } + } + elsif($^O eq 'darwin') + { + # Set up mono for bootstrapping + if ($existingMonoRootPath eq "") + { + # Find the latest mono version and use that for boostrapping + my $monoInstalls = '/Library/Frameworks/Mono.framework/Versions'; + my @monoVersions = (); + + opendir( my $DIR, $monoInstalls ); + while ( my $entry = readdir $DIR ) + { + next unless -d $monoInstalls . '/' . $entry; + next if $entry eq '.' or $entry eq '..' or $entry eq 'Current'; + push @monoVersions, $entry; + } + closedir $DIR; + @monoVersions = sort @monoVersions; + my $monoVersionToUse = pop @monoVersions; + $existingMonoRootPath = "$monoInstalls/$monoVersionToUse"; + } + + if ($targetArch eq "arm64") + { + $macversion = "11.0"; # To build on ARM64, we need to specify min OS version as 11.0 as we need to use new APIs from 11.0 + } + + $mcs = "EXTERNAL_MCS=$existingMonoRootPath/bin/mcs"; + + $ENV{'CC'} = "$macSdkPath/../usr/bin/clang"; + $ENV{'CXX'} = "$macSdkPath/../usr/bin/clang++"; + $ENV{'CFLAGS'} = $ENV{MACSDKOPTIONS} = "-mmacosx-version-min=$macversion -isysroot $macSdkPath -g"; + + $ENV{CFLAGS} = "$ENV{CFLAGS} -O0" if $debug; + $ENV{CFLAGS} = "$ENV{CFLAGS} -Os" if not $debug; #optimize for size + + $ENV{CC} = "$ENV{CC} -arch $targetArch"; + $ENV{CXX} = "$ENV{CXX} -arch $targetArch"; + + # Add OSX specific autogen args + + if ($targetArch eq "x86_64") + { + push @configureparams, "--host=x86_64-apple-darwin12.2.0"; + } + elsif ($targetArch eq "arm64") + { + push @configureparams, "--host=aarch64-apple-darwinmacos12.2.0"; + } + else + { + die("Unsupported macOS architecture: $targetArch"); + } + + # Need to define because Apple's SIP gets in the way of us telling mono where to find this + push @configureparams, "--with-libgdiplus=$addtoresultsdistdir/lib/libgdiplus.dylib"; + push @configureparams, "--enable-minimal=shared_perfcounters"; + + print "\n"; + print ">>> Setting environment:\n"; + print ">>> PATH = ".$ENV{PATH}."\n"; + print ">>> C_INCLUDE_PATH = ".$ENV{C_INCLUDE_PATH}."\n"; + print ">>> CPLUS_INCLUDE_PATH = ".$ENV{CPLUS_INCLUDE_PATH}."\n"; + print ">>> CFLAGS = ".$ENV{CFLAGS}."\n"; + print ">>> CXXFLAGS = ".$ENV{CXXFLAGS}."\n"; + print ">>> CC = ".$ENV{CC}."\n"; + print ">>> CXX = ".$ENV{CXX}."\n"; + print ">>> CPP = ".$ENV{CPP}."\n"; + print ">>> CXXPP = ".$ENV{CXXPP}."\n"; + print ">>> LD = ".$ENV{LD}."\n"; + print ">>> LDFLAGS = ".$ENV{LDFLAGS}."\n"; + print "\n"; + } + else + { + push @configureparams, "--host=$monoHostArch-pc-mingw32"; + } + + if ($isDesktopBuild) + { + my $cacheArch = $arch32 ? "i386" : "x86_64"; + push @configureparams, "--cache-file=desktop-$cacheArch.cache" if ($enableCacheFile); + } + + print ">>> Existing Mono : $existingMonoRootPath\n\n"; + $ENV{'PATH'} = "$existingMonoRootPath/$existingExternalMonoBinDir:$ENV{'PATH'}"; + + print ">>> PATH before Build = $ENV{PATH}\n\n"; + + print(">>> mcs Information : \n"); + system(@commandPrefix, ("which", "mcs")); + system(@commandPrefix, ("mcs", "--version")); + print("\n"); + + print ">>> Checking on some tools...\n"; + system(@commandPrefix, ("which", "autoconf")); + system(@commandPrefix, ("autoconf", "--version")); + + system(@commandPrefix, ("which", "texi2dvi")); + system(@commandPrefix, ("texi2dvi", "--version")); + + system(@commandPrefix, ("which", "automake")); + system(@commandPrefix, ("automake", "--version")); + + system(@commandPrefix, ("which", "libtool")); + system(@commandPrefix, ("libtool", "--version")); + + system(@commandPrefix, ("which", "libtoolize")); + system(@commandPrefix, ("libtoolize", "--version")); + print("\n"); + + print ">>> LIBTOOLIZE before Build = $ENV{LIBTOOLIZE}\n"; + print ">>> LIBTOOL before Build = $ENV{LIBTOOL}\n"; + + chdir("$monoroot") eq 1 or die ("failed to chdir 2\n"); + + if (not $skipMonoMake) + { + if ($clean) + { + if (!($mcsOnly)) + { + print(">>> Cleaning $monoprefix\n"); + rmtree($monoprefix); + } + + # Avoid "source directory already configured" ... + system(@commandPrefix, ('rm', '-f', 'config.status', 'eglib/config.status', 'libgc/config.status')); + + print("\n>>> Calling autogen in mono\n"); + print("\n"); + print("\n>>> Configure parameters are : @configureparams\n"); + print("\n"); + + system(@commandPrefix, ('./autogen.sh', @configureparams)) eq 0 or die ('failing autogenning mono'); + + if ($mcsOnly) + { + print("\n>>> Calling make clean in mcs\n"); + chdir("$monoroot/mcs"); + system(@commandPrefix, ("make","clean")) eq 0 or die ("failed to make clean\n"); + chdir("$monoroot"); + } + else + { + print("\n>>> Calling make clean in mono\n"); + system(@commandPrefix, ("make","clean")) eq 0 or die ("failed to make clean\n"); + } + } + + # this step needs to run after configure + if ($iphoneCross || $iphone || $android || $tizen) + { + # This step generates the arm_dpimacros.h file, which is needed by the offset dumper + chdir("$monoroot/mono/arch/arm"); + system("make") eq 0 or die("failed to make in $monoroot/mono/arch/arm\n"); + chdir("$monoroot"); + } + + if ($iphoneCross) + { + my @monoArgs = (); + push @monoArgs, "$monoroot/tools/offsets-tool/MonoAotOffsetsDumper.exe"; + push @monoArgs, "--abi"; + push @monoArgs, "$iphoneCrossAbi"; + push @monoArgs, "--out"; + push @monoArgs, "$monoroot"; + push @monoArgs, "--mono"; + push @monoArgs, "$monoroot"; + push @monoArgs, "--maccore"; + push @monoArgs, "$monoroot"; + + $ENV{MONO_PATH} = "$externalBuildDeps/CppSharpBinaries"; + # Need to use 32bit mono because there is a native CppSharp dylib that will be used and there's only a 32bit version of it + print ">>> Running MonoAotOffsetDumper : arch -i386 $iphoneCrossMonoBinToUse/mono @monoArgs\n"; + system("arch", "-i386", "$iphoneCrossMonoBinToUse/mono", @monoArgs) eq 0 or die("failed to run MonoAotOffsetsDumper\n"); + } + + if ($mcsOnly) + { + print("\n>>> Calling make in mcs\n"); + chdir("$monoroot/mcs"); + my @makeCommand = (@commandPrefix, ('make', "-j$jobs")); + if($mcs ne '') + { + push(@makeCommand, $mcs); + } + system(@makeCommand) eq 0 or die ("Failed to make\n"); + chdir("$monoroot"); + } + else + { + print("\n>>> Calling make\n"); + my @makeCommand = (@commandPrefix, ('make', "-j$jobs")); + if($mcs ne '') + { + push(@makeCommand, $mcs); + } + system(@makeCommand) eq 0 or die ("Failed to make\n"); + } + + if ($isDesktopBuild) + { + print("\n>>> Calling make install\n"); + system(@commandPrefix, ('make', 'install')) eq 0 or die ("Failed to make install\n"); + } + else + { + if ($disableMcs) + { + print(">>> Skipping make install. We don't need to run this step when building the runtime on non-desktop platforms.\n"); + } + else + { + # Note by Mike : make install on Windows for android runtime runs into more cygwin path issues. The one I hit was related to ranlib.exe being passed cygwin linux paths + # and as a result not being able to find stuff. The previous build scripts didn't run make install for android or iOS, so I think we are fine to skip this step. + # However, if we were to build the class libs for these cases, then we probably would need to run make install. If that day comes, we'll have to figure out what to do here. + print(">>> Attempting to build class libs for a non-desktop platform. The `make install` step is probably needed, but it has cygwin path related problems on Windows for android\n"); + die("Blocking this code path until we need it. It probably should be looked at more closely before letting it proceed\n"); + } + } + } + + if ($isDesktopBuild) + { + if ($^O eq "cygwin") + { + system("$winPerl", "$winMonoRoot/external/buildscripts/build_runtime_vs.pl", "--build=$build", "--arch32=$arch32", "--msbuildversion=$msBuildVersion", "--clean=$clean", "--debug=$debug") eq 0 or die ('failed building mono with VS\n'); + + # Copy over the VS built stuff that we want to use instead into the prefix directory + my $archNameForBuild = $arch32 ? 'Win32' : 'x64'; + my $config = $debug ? "Debug" : "Release"; + system("cp $monoroot/msvc/$archNameForBuild/bin/$config/mono.exe $monoprefix/bin/.") eq 0 or die ("failed copying mono.exe\n"); + system("cp $monoroot/msvc/$archNameForBuild/bin/$config/mono-2.0.dll $monoprefix/bin/.") eq 0 or die ("failed copying mono-2.0.dll\n"); + system("cp $monoroot/msvc/$archNameForBuild/bin/$config/mono-2.0.pdb $monoprefix/bin/.") eq 0 or die ("failed copying mono-2.0.pdb\n"); + } + + system("cp -R $addtoresultsdistdir/bin/. $monoprefix/bin/") eq 0 or die ("Failed copying $addtoresultsdistdir/bin to $monoprefix/bin\n"); + } + + if(!($disableMcs)) + { + my @additionalProfiles = (); + push @additionalProfiles, "unityjit"; + push @additionalProfiles, "unityaot"; + + chdir("$monoroot/mcs"); + foreach my $profileName(@additionalProfiles) + { + print(">>> Making profile : $profileName\n"); + system("make", "PROFILE=$profileName") eq 0 or die ("Failed to make $profileName profile in mcs\n"); + + my $profileDestDir = "$monoprefix/lib/mono/$profileName"; + print(">>> Copying $profileName to $profileDestDir directory\n"); + + print(">>> Cleaning $profileDestDir\n"); + system("rm -rf $profileDestDir"); + + system("mkdir -p $profileDestDir") eq 0 or die("failed to make directory $profileDestDir\n"); + system("mkdir -p $profileDestDir/Facades") eq 0 or die("failed to make directory $profileDestDir/Facades\n"); + + system("cp $monoroot/mcs/class/lib/$profileName/*.dll $profileDestDir") eq 0 or die("Failed copying dlls from $monoroot/mcs/class/lib/$profileName to $profileDestDir\n"); + system("cp $monoroot/mcs/class/lib/$profileName/Facades/*.dll $profileDestDir/Facades") eq 0 or die("Failed copying dlls from $monoroot/mcs/class/lib/$profileName/Facades to $profileDestDir/Facades\n"); + } + + chdir("$monoroot"); + + my $stubResult = system("perl", "$buildscriptsdir/stub_classlibs.pl"); + + if ($stubResult ne 0) + { + die("Failed to run the profile stubber\n"); + } + } +} +else +{ + print(">>> Skipping build\n"); +} + +if ($artifact) +{ + print(">>> Creating artifact...\n"); + + if ($artifactsCommon) + { + print(">>> Creating common artifacts...\n"); + print(">>> distribution directory = $distdir\n"); + + if (!(-d "$distdir")) + { + system("mkdir -p $distdir") eq 0 or die("failed to make directory $distdir\n"); + } + + $File::Copy::Recursive::CopyLink = 0; #make sure we copy files as files and not as symlinks, as TC unfortunately doesn't pick up symlinks. + + my $distdirlibmono = "$distdir/lib/mono"; + + print(">>> Cleaning $distdir/lib\n"); + system("rm -rf $distdir/lib"); + system("mkdir -p $distdir/lib"); + + print(">>> Creating normal profile artifacts...\n"); + system("cp -R $addtoresultsdistdir/. $distdir/") eq 0 or die ("Failed copying $addtoresultsdistdir to $distdir\n"); + + system("cp -r $monoprefix/lib/mono $distdir/lib"); + + if($^O ne 'darwin') + { + # On OSX we build a universal binary for 32-bit and 64-bit in the mono executable. The class library build + # only creates the 64-bit slice, so we don't want to end up with a single slice binary in the output. + # If we do, it will step on the universal binary produced but the OSX runtime build. + system("cp -r $monoprefix/bin $distdir/") eq 0 or die ("failed copying bin folder\n"); + } + system("cp -r $monoprefix/etc $distdir/") eq 0 or die("failed copying etc folder\n"); + + system("cp -R $externalBuildDeps/reference-assemblies/unity $distdirlibmono/unity"); + system("cp -R $externalBuildDeps/reference-assemblies/unity_web $distdirlibmono/unity_web"); + + # now remove nunit from a couple places (but not all, we need some of them) + # linux tar is not happy these are removed(at least on wsl), so don't remove them for now + if(not $windowsSubsystemForLinux) + { + system("rm -rf $distdirlibmono/2.0/nunit*"); + system("rm -rf $distdirlibmono/gac/nunit*"); + } + + # Remove a self referencing sym link that causes problems + system("rm -rf $monoprefix/bin/bin"); + + if (-f "$monoroot/ZippedClasslibs.tar.gz") + { + system("rm -f $monoroot/ZippedClasslibs.tar.gz") eq 0 or die("Failed to clean existing ZippedClasslibs.tar.gz\n"); + } + + print(">>> Creating ZippedClasslibs.tar.gz\n"); + print(">>> Changing directory to : $buildsroot\n"); + chdir("$buildsroot"); + system("tar -hpczf ../ZippedClasslibs.tar.gz *") eq 0 or die("Failed to zip up classlibs\n"); + print(">>> Changing directory back to : $currentdir\n"); + chdir("$currentdir"); + } + + # Do the platform specific logic to create the builds output structure that we want + + my $embedDirRoot = "$buildsroot/embedruntimes"; + my $embedDirArchDestination = ""; + my $distDirArchBin = ""; + my $versionsOutputFile = ""; + my $crossCompilerRoot = "$buildsroot/crosscompiler"; + my $crossCompilerDestination = ""; + + if ($iphone) + { + $embedDirArchDestination = "$embedDirRoot/iphone/$iphoneArch"; + $versionsOutputFile = "$buildsroot/versions-iphone-$iphoneArch.txt"; + } + elsif ($iphoneCross) + { + $crossCompilerDestination = "$buildsroot/crosscompiler/iphone"; + $versionsOutputFile = "$buildsroot/versions-iphone-xcompiler.txt"; + } + elsif ($iphoneSimulator) + { + $embedDirArchDestination = "$embedDirRoot/iphone/$iphoneSimulatorArch"; + $versionsOutputFile = "$buildsroot/versions-iphone-$iphoneSimulatorArch.txt"; + } + elsif ($android) + { + $embedDirArchDestination = "$embedDirRoot/android/$androidArch"; + $versionsOutputFile = "$buildsroot/versions-android-$androidArch.txt"; + } + elsif ($tizenEmulator) + { + $embedDirArchDestination = "$embedDirRoot/tizenemulator/"; + $versionsOutputFile = "$buildsroot/versions-tizenemulator.txt"; + } + elsif ($tizen) + { + $embedDirArchDestination = "$embedDirRoot/tizen/"; + $versionsOutputFile = "$buildsroot/versions-tizen.txt"; + } + elsif($^O eq "linux") + { + $embedDirArchDestination = $arch32 ? "$embedDirRoot/linux32" : "$embedDirRoot/linux64"; + $distDirArchBin = $arch32 ? "$distdir/bin-linux32" : "$distdir/bin-linux64"; + $versionsOutputFile = $arch32 ? "$buildsroot/versions-linux32.txt" : "$buildsroot/versions-linux64.txt"; + } + elsif($^O eq 'darwin') + { + $embedDirArchDestination = "$embedDirRoot/osx-tmp-$targetArch"; + $distDirArchBin = "$distdir/bin-osx-tmp-$targetArch"; + $versionsOutputFile = "$buildsroot/versions-macos-$targetArch.txt"; + } + else + { + $embedDirArchDestination = $arch32 ? "$embedDirRoot/win32" : "$embedDirRoot/win64"; + $distDirArchBin = $arch32 ? "$distdir/bin" : "$distdir/bin-x64"; + $versionsOutputFile = $arch32 ? "$buildsroot/versions-win32.txt" : "$buildsroot/versions-win64.txt"; + } + + # Make sure the directory for our architecture is clean before we copy stuff into it + if (-d "$embedDirArchDestination") + { + print(">>> Cleaning $embedDirArchDestination\n"); + rmtree($embedDirArchDestination); + } + + if (-d "$distDirArchBin") + { + print(">>> Cleaning $distDirArchBin\n"); + rmtree($distDirArchBin); + } + + if ($artifactsRuntime) + { + system("mkdir -p $embedDirArchDestination") if ($embedDirArchDestination ne ""); + system("mkdir -p $distDirArchBin") if ($distDirArchBin ne ""); + system("mkdir -p $crossCompilerDestination") if ($crossCompilerDestination ne ""); + + # embedruntimes directory setup + print(">>> Creating embedruntimes directory : $embedDirArchDestination\n"); + if ($iphone || $iphoneSimulator) + { + for my $file ('libmonosgen-2.0.a','libmonobdwgc-2.0.a') + { + print ">>> Copying $file\n"; + system("cp", "$monoroot/mono/mini/.libs/$file","$embedDirArchDestination/$file") eq 0 or die ("failed copying $file\n"); + } + } + elsif ($iphoneCross) + { + # Nothing to do + } + elsif ($android) + { + for my $file ('libmonosgen-2.0.so','libmonobdwgc-2.0.so') + { + print ">>> Copying $file\n"; + system("cp", "$monoroot/mono/mini/.libs/$file","$embedDirArchDestination/$file") eq 0 or die ("failed copying $file\n"); + } + print ">>> Copying libMonoPosixHelper.so\n"; + system("cp", "$monoroot/support/.libs/libMonoPosixHelper.so","$embedDirArchDestination/libMonoPosixHelper.so") eq 0 or die ("failed copying libMonoPosixHelper.so\n"); + } + elsif ($tizen || $tizenEmulator) + { + for my $file ('libmonosgen-2.0.so','libmonobdwgc-2.0.so') + { + print ">>> Copying $file\n"; + system("cp", "$monoroot/mono/mini/.libs/$file","$embedDirArchDestination/$file") eq 0 or die ("failed copying $file\n"); + } + } + elsif($^O eq "linux") + { + print ">>> Copying libmonosgen-2.0\n"; + system("cp", "$monoroot/mono/mini/.libs/libmonobdwgc-2.0.so","$embedDirArchDestination/libmonobdwgc-2.0.so") eq 0 or die ("failed copying libmonobdwgc-2.0.so\n"); + system("cp", "$monoroot/mono/mini/.libs/libmonosgen-2.0.so","$embedDirArchDestination/libmonosgen-2.0.so") eq 0 or die ("failed copying libmonosgen-2.0.so\n"); + + print ">>> Copying libMonoPosixHelper.so\n"; + system("cp", "$monoroot/support/.libs/libMonoPosixHelper.so","$embedDirArchDestination/libMonoPosixHelper.so") eq 0 or die ("failed copying libMonoPosixHelper.so\n"); + + if ($buildMachine) + { + system("strip $embedDirArchDestination/libmonobdwgc-2.0.so") eq 0 or die("failed to strip libmonobdwgc-2.0.so (shared)\n"); + system("strip $embedDirArchDestination/libmonosgen-2.0.so") eq 0 or die("failed to strip libmonosgen-2.0.so (shared)\n"); + system("strip $embedDirArchDestination/libMonoPosixHelper.so") eq 0 or die("failed to strip libMonoPosixHelper (shared)\n"); + } + } + elsif($^O eq 'darwin') + { + # embedruntimes directory setup + print ">>> Hardlinking libmonosgen-2.0\n"; + + system("ln","-f", "$monoroot/mono/mini/.libs/libmonobdwgc-2.0.dylib","$embedDirArchDestination/libmonobdwgc-2.0.dylib") eq 0 or die ("failed symlinking libmonobdwgc-2.0.dylib\n"); + system("ln","-f", "$monoroot/mono/mini/.libs/libmonosgen-2.0.dylib","$embedDirArchDestination/libmonosgen-2.0.dylib") eq 0 or die ("failed symlinking libmonosgen-2.0.dylib\n"); + + print "Hardlinking libMonoPosixHelper.dylib\n"; + system("ln","-f", "$monoroot/support/.libs/libMonoPosixHelper.dylib","$embedDirArchDestination/libMonoPosixHelper.dylib") eq 0 or die ("failed symlinking $embedDirArchDestination/libMonoPosixHelper.dylib\n"); + + InstallNameTool("$embedDirArchDestination/libmonobdwgc-2.0.dylib", "\@executable_path/../Frameworks/MonoEmbedRuntime/osx/libmonobdwgc-2.0.dylib"); + InstallNameTool("$embedDirArchDestination/libmonosgen-2.0.dylib", "\@executable_path/../Frameworks/MonoEmbedRuntime/osx/libmonosgen-2.0.dylib"); + InstallNameTool("$embedDirArchDestination/libMonoPosixHelper.dylib", "\@executable_path/../Frameworks/MonoEmbedRuntime/osx/libMonoPosixHelper.dylib"); + + print ">>> Copying mono public headers\n"; + system("mkdir -p $includesroot/mono"); + system("cp -R $monoprefix/include/mono-2.0/mono $includesroot/mono"); + } + else + { + # embedruntimes directory setup + system("cp", "$monoprefix/bin/mono-2.0-bdwgc.dll", "$embedDirArchDestination/mono-2.0-bdwgc.dll") eq 0 or die ("failed copying mono-2.0-bdwgc.dll\n"); + system("cp", "$monoprefix/bin/mono-2.0-bdwgc.pdb", "$embedDirArchDestination/mono-2.0-bdwgc.pdb") eq 0 or die ("failed copying mono-2.0-bdwgc.pdb\n"); + + system("cp", "$monoprefix/bin/mono-2.0-sgen.dll", "$embedDirArchDestination/mono-2.0-sgen.dll") eq 0 or die ("failed copying mono-2.0-sgen.dll\n"); + system("cp", "$monoprefix/bin/mono-2.0-sgen.pdb", "$embedDirArchDestination/mono-2.0-sgen.pdb") eq 0 or die ("failed copying mono-2.0-sgen.pdb\n"); + } + + # monodistribution directory setup + print(">>> Creating monodistribution directory\n"); + if ($android || $iphone || $iphoneCross || $iphoneSimulator || $tizen || $tizenEmulator) + { + # Nothing to do + } + elsif($^O eq "linux") + { + my $distDirArchEtc = $arch32 ? "$distdir/etc-linux32" : "$distdir/etc-linux64"; + + if (-d "$distDirArchEtc") + { + print(">>> Cleaning $distDirArchEtc\n"); + rmtree($distDirArchEtc); + } + + system("mkdir -p $distDirArchBin"); + system("mkdir -p $distDirArchEtc"); + system("mkdir -p $distDirArchEtc/mono"); + + system("ln", "-f", "$monoroot/mono/mini/mono-sgen","$distDirArchBin/mono") eq 0 or die("failed symlinking mono executable\n"); + system("ln", "-f", "$monoroot/tools/pedump/pedump","$distDirArchBin/pedump") eq 0 or die("failed symlinking pedump executable\n"); + system('cp', "$monoroot/data/config","$distDirArchEtc/mono/config") eq 0 or die("failed to copy config\n"); + } + elsif($^O eq 'darwin') + { + system("ln", "-f", "$monoroot/mono/mini/mono","$distDirArchBin/mono") eq 0 or die("failed hardlinking mono executable\n"); + system("ln", "-f", "$monoroot/tools/pedump/pedump","$distDirArchBin/pedump") eq 0 or die("failed hardlinking pedump executable\n"); + } + else + { + system("cp", "$monoprefix/bin/mono-2.0.dll", "$distDirArchBin/mono-2.0.dll") eq 0 or die ("failed copying mono-2.0.dll\n"); + system("cp", "$monoprefix/bin/mono-2.0.pdb", "$distDirArchBin/mono-2.0.pdb") eq 0 or die ("failed copying mono-2.0.pdb\n"); + system("cp", "$monoprefix/bin/mono.exe", "$distDirArchBin/mono.exe") eq 0 or die ("failed copying mono.exe\n"); + } + } + + # cross compiler directory setup + if ($iphoneCross) + { + print ">>> Copying mono-xcompiler\n"; + if($runningOnWindows) + { + die("Not implemented\n"); + } + else + { + system("cp", "$monoroot/mono/mini/mono","$crossCompilerDestination/mono-xcompiler") eq 0 or die ("failed copying mono-xcompiler\n"); + } + } + + # Not all build configurations output to the distro dir, so only chmod it if it exists + system("chmod", "-R", "755", $distDirArchBin) if (-d "$distDirArchBin"); + + # Output version information + print(">>> Creating version file : $versionsOutputFile\n"); + system("echo \"mono-version =\" > $versionsOutputFile"); + + # Not all build configurations output to the distro dir, only try to output version info if there is a distro dir + system("$distDirArchBin/mono --version >> $versionsOutputFile") if (-d "$distDirArchBin"); + + system("echo \"unity-mono-revision = $monoRevision\" >> $versionsOutputFile"); + system("echo \"unity-mono-build-scripts-revision = $buildScriptsRevision\" >> $versionsOutputFile"); + my $tmp = `date`; + system("echo \"build-date = $tmp\" >> $versionsOutputFile"); +} +else +{ + print(">>> Skipping artifact creation\n"); +} + +if ($test) +{ + my $runtimeTestsDir = "$monoroot/mono/mini"; + if ($runRuntimeTests) + { + chdir("$runtimeTestsDir") eq 1 or die ("failed to chdir"); + print("\n>>> Calling make check in $runtimeTestsDir\n\n"); + system("make","check") eq 0 or die ("runtime tests failed\n"); + } + else + { + print(">>> Skipping runtime unit tests\n"); + } + + if ($runClasslibTests) + { + if ($disableMcs) + { + print(">>> Skipping classlib unit tests because building the class libs was disabled\n"); + } + else + { + my $classlibTestsDir = "$monoroot/mcs/class"; + chdir("$classlibTestsDir") eq 1 or die ("failed to chdir"); + print("\n>>> Calling make run-test in $runtimeTestsDir\n\n"); + system("make","run-test") eq 0 or die ("classlib tests failed\n"); + } + } + else + { + print(">>> Skipping classlib unit tests\n"); + } +} +else +{ + print(">>> Skipping unit tests\n"); +} + +chdir ($currentdir); diff --git a/external/buildscripts/build_all_linux.pl b/external/buildscripts/build_all_linux.pl new file mode 100644 index 000000000000..a975f5e7b668 --- /dev/null +++ b/external/buildscripts/build_all_linux.pl @@ -0,0 +1,27 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $currentdir = getcwd(); + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildscriptsdir = "$monoroot/external/buildscripts"; + +my @passAlongArgs = (); +foreach my $arg (@ARGV) +{ + # Filter out --clean if someone uses it. We have to clean since we are doing two builds + if (not $arg =~ /^--clean=/) + { + push @passAlongArgs, $arg; + } +} + +print(">>> Building i386\n"); +system("perl", "$buildscriptsdir/build.pl", "--arch32=1", "--clean=1", "--classlibtests=0", @passAlongArgs) eq 0 or die ('failing building i386'); + +print(">>> Building x86_64\n"); +system("perl", "$buildscriptsdir/build.pl", "--clean=1", "--classlibtests=0", @passAlongArgs) eq 0 or die ('failing building x86_64'); \ No newline at end of file diff --git a/external/buildscripts/build_all_osx.pl b/external/buildscripts/build_all_osx.pl new file mode 100644 index 000000000000..21b2ad81ec78 --- /dev/null +++ b/external/buildscripts/build_all_osx.pl @@ -0,0 +1,176 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $currentdir = getcwd(); + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildscriptsdir = "$monoroot/external/buildscripts"; +my $buildMachine = $ENV{UNITY_THISISABUILDMACHINE}; +my $buildsroot = "$monoroot/builds"; + +my $artifact=0; +my $artifactsCommon=0; +my $buildUsAndBoo=0; + +my @thisScriptArgs = (); +my @passAlongArgs = (); +foreach my $arg (@ARGV) +{ + # Filter out --clean if someone uses it. We have to clean since we are doing two builds + if (not $arg =~ /^--clean=/) + { + # We don't need common artifacts, us, and boo, from both, so filter out temporarily and we'll + # only pass it to the second build + if ($arg =~ /^--artifactscommon=/ || $arg =~ /^--buildusandboo=/) + { + push @thisScriptArgs, $arg; + } + else + { + push @passAlongArgs, $arg; + } + } + + if ($arg =~ /^--artifact=/) + { + push @thisScriptArgs, $arg; + } +} + +print(">>> This Script Args = @thisScriptArgs\n"); +print(">>> Pass Along Args = @passAlongArgs\n"); + +@ARGV = @thisScriptArgs; +GetOptions( + 'artifact=i'=>\$artifact, + 'artifactscommon=i'=>\$artifactsCommon, + 'buildusandboo=i'=>\$buildUsAndBoo, +); + +if ($artifactsCommon) +{ + push @passAlongArgs, "--artifactscommon=1"; +} + +if ($buildUsAndBoo) +{ + push @passAlongArgs, "--buildusandboo=1"; +} + +print(">>> Building x86_64\n"); +system("perl", "$buildscriptsdir/build.pl", "--clean=1", "--classlibtests=0", "--targetarch=x86_64", @passAlongArgs) eq 0 or die ('failing building x86_64'); + +print(">>> Building ARM64\n"); +system("perl", "$buildscriptsdir/build.pl", "--clean=1", "--classlibtests=0", "--targetarch=arm64", @passAlongArgs) eq 0 or die ("failing building ARM64"); + +if ($artifact) +{ + print(">>> Moving built binaries to final output directories\n"); + + # Copy stuff in the embedruntimes directory + my $embedDirRoot = "$buildsroot/embedruntimes"; + my $embedDirSourceX64 = "$embedDirRoot/osx-tmp-x86_64"; + my $embedDirSourceARM64 = "$embedDirRoot/osx-tmp-arm64"; + + CopyEmbedRuntimeBinaries($embedDirSourceX64, "$embedDirRoot/osx"); + CopyEmbedRuntimeBinaries($embedDirSourceARM64, "$embedDirRoot/osx-arm64"); + + # Merge stuff in the monodistribution directory + my $distDirRoot = "$buildsroot/monodistribution"; + my $distDirDestinationBin = "$buildsroot/monodistribution/bin"; + my $distDirDestinationLib = "$buildsroot/monodistribution/lib"; + my $distDirSourceBinX64 = "$distDirRoot/bin-osx-tmp-x86_64"; + my $distDirSourceBinARM64 = "$distDirRoot/bin-osx-tmp-arm64"; + + # Should always exist because build_all would have put stuff in it, but in some situations + # depending on the options it may not. So create it if it does not exist + if (!(-d $distDirDestinationBin)) + { + system("mkdir -p $distDirDestinationBin"); + } + + if (!(-d $distDirDestinationLib)) + { + system("mkdir -p $distDirDestinationLib"); + } + + if (!(-d $distDirSourceBinX64)) + { + die("Expected source directory not found : $distDirSourceBinX64\n"); + } + + if (!(-d $distDirSourceBinARM64)) + { + die("Expected source directory not found : $distDirSourceBinARM64\n"); + } + + for my $file ('mono') + { + MergeIntoFatBinary("$distDirSourceBinX64/$file", "$distDirSourceBinARM64/$file", "$distDirDestinationBin/$file"); + } + + for my $file ('pedump') + { + # pedump doens't get cross-compiled + system ('mv', "$distDirSourceBinX64/$file", "$distDirDestinationBin/$file") eq 0 or die ("Failed to move '$distDirSourceBinX64/$file' to '$distDirDestinationBin/$file'."); + } + + for my $file ('libMonoPosixHelper.dylib') + { + MergeIntoFatBinary("$embedDirSourceX64/$file", "$embedDirSourceARM64/$file", "$distDirDestinationLib/$file"); + } + + if ($buildMachine) + { + print(">>> Clean up temporary arch specific build directories\n"); + + rmtree("$distDirSourceBinX64"); + rmtree("$distDirSourceBinARM64"); + rmtree("$embedDirSourceX64"); + rmtree("$embedDirSourceARM64"); + } +} + +sub CopyEmbedRuntimeBinaries +{ + my ($embedDirSource, $embedDirDestination) = @_; + + system("mkdir -p $embedDirDestination"); + + if (!(-d $embedDirSource)) + { + die("Expected source directory not found : $embedDirSource\n"); + } + + for my $file ('libmonobdwgc-2.0.dylib','libmonosgen-2.0.dylib','libMonoPosixHelper.dylib') + { + print(">>> cp $embedDirSource/$file $embedDirDestination/$file\n\n"); + system('cp', "$embedDirSource/$file", "$embedDirDestination/$file") eq 0 or die("Failed to copy '$embedDirSource/$file' to '$embedDirDestination/$file'."); + } + + if (not $buildMachine) + { + print(">>> Doing non-build machine stuff...\n"); + for my $file ('libmonobdwgc-2.0.dylib','libmonosgen-2.0.dylib','libMonoPosixHelper.dylib') + { + print(">>> Removing $embedDirDestination/$file.dSYM\n"); + rmtree("$embedDirDestination/$file.dSYM"); + print(">>> 'dsymutil $embedDirDestination/$file\n"); + system('dsymutil', "$embedDirDestination/$file") eq 0 or warn("Failed creating $embedDirDestination/$file.dSYM"); + } + + print(">>> Done with non-build machine stuff\n"); + } +} + +sub MergeIntoFatBinary +{ + my ($binary1, $binary2, $binaryOutput) = @_; + + print(">>> Merging '$binary1' and '$binary2' into '$binaryOutput'\n\n"); + system('lipo', "$binary1", "$binary2", "-create", "-output", "$binaryOutput") eq 0 or die("Failed to run lipo!"); +} \ No newline at end of file diff --git a/external/buildscripts/build_all_win.pl b/external/buildscripts/build_all_win.pl new file mode 100644 index 000000000000..65e099b4bec2 --- /dev/null +++ b/external/buildscripts/build_all_win.pl @@ -0,0 +1,27 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $currentdir = getcwd(); + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildscriptsdir = "$monoroot/external/buildscripts"; + +my @passAlongArgs = (); +foreach my $arg (@ARGV) +{ + # Filter out --clean if someone uses it. We have to clean since we are doing two builds + if (not $arg =~ /^--clean=/) + { + push @passAlongArgs, $arg; + } +} + +print(">>> Building i686\n"); +system("perl", "$buildscriptsdir/build_win_wrapper.pl", "--arch32=1", "--clean=1", "--classlibtests=0", @passAlongArgs) eq 0 or die ('failing building win32'); + +print(">>> Building x86_64\n"); +system("perl", "$buildscriptsdir/build_win_wrapper.pl", "--clean=1", "--classlibtests=0", @passAlongArgs) eq 0 or die ('failing building x64'); \ No newline at end of file diff --git a/external/buildscripts/build_classlibs_osx.pl b/external/buildscripts/build_classlibs_osx.pl new file mode 100644 index 000000000000..a67be39429b7 --- /dev/null +++ b/external/buildscripts/build_classlibs_osx.pl @@ -0,0 +1,43 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $build = 1; +my $clean = 1; +my $mcsOnly = 0; +my $skipMonoMake = 0; +my $stevedoreBuildDeps=1; + +# Handy troubleshooting/niche options + +# The prefix hack probably isn't needed anymore. Let's disable it by default and see how things go +my $shortPrefix = 1; + +GetOptions( + "build=i"=>\$build, + "clean=i"=>\$clean, + "mcsOnly=i"=>\$mcsOnly, + 'skipmonomake=i'=>\$skipMonoMake, + 'shortprefix=i'=>\$shortPrefix, + 'stevedorebuilddeps=i'=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +system( + "perl", + "$buildScriptsRoot/build.pl", + "--build=$build", + "--clean=$clean", + "--mcsonly=$mcsOnly", + "--targetarch=x86_64", + "--skipmonomake=$skipMonoMake", + "--artifact=1", + "--artifactscommon=1", + "--forcedefaultbuilddeps=1", + "--stevedorebuilddeps=$stevedoreBuildDeps", + "--shortprefix=$shortPrefix") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_classlibs_wsl.pl b/external/buildscripts/build_classlibs_wsl.pl new file mode 100644 index 000000000000..d65b1c4f3813 --- /dev/null +++ b/external/buildscripts/build_classlibs_wsl.pl @@ -0,0 +1,52 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $build = 1; +my $clean = 1; +my $mcsOnly = 0; +my $skipMonoMake = 0; + +# Handy troubleshooting/niche options + +# The prefix hack probably isn't needed anymore. Let's disable it by default and see how things go +my $shortPrefix = 1; + +# This script should not be ran on windows, if it is, kindly switch over to wsl +if ($^O eq "MSWin32") +{ + print(">>> Called from Windows. Switching over to wsl\n"); + my $monoRootInBash = `bash -c pwd`; + chomp $monoRootInBash; + print(">>> monoRootInBash = $monoRootInBash\n"); + my $cmdForBash = "$monoRootInBash/external/buildscripts/build_classlibs_wsl.pl @ARGV"; + system("bash", "-c", "\"perl $cmdForBash\"") eq 0 or die("\n"); + exit 0; +} + +GetOptions( + "build=i"=>\$build, + "clean=i"=>\$clean, + "mcsOnly=i"=>\$mcsOnly, + 'skipmonomake=i'=>\$skipMonoMake, + 'shortprefix=i'=>\$shortPrefix, +) or die ("illegal cmdline options"); + +system( + "perl", + "$buildScriptsRoot/build.pl", + "--build=$build", + "--clean=$clean", + "--mcsonly=$mcsOnly", + "--skipmonomake=$skipMonoMake", + "--artifact=1", + "--artifactscommon=1", + "--forcedefaultbuilddeps=1", + "--windowssubsystemforlinux=1", + "--shortprefix=$shortPrefix") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_ios_xwin.pl b/external/buildscripts/build_ios_xwin.pl new file mode 100644 index 000000000000..a8d2cde8ec22 --- /dev/null +++ b/external/buildscripts/build_ios_xwin.pl @@ -0,0 +1,18 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $clean = 1; + +GetOptions( + "clean=i"=>\$clean, +) or die ("illegal cmdline options"); + + +system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=$clean", "--artifact=1", "--arch32=1", "--iphonecross=1", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building iphone cross compiler\n"); diff --git a/external/buildscripts/build_ios_xwin.sh b/external/buildscripts/build_ios_xwin.sh new file mode 100644 index 000000000000..8095de7d7146 --- /dev/null +++ b/external/buildscripts/build_ios_xwin.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +BASEDIR=$(dirname $0) + +# Note : Not Implemented yet. Script is here to make the katana build pass so that the mono build artifact is created +# Uncomment the line below when ready to implement. Remove the mkdir +if [ -d builds ]; then + echo "Skip making builds directory. Already exists" +else + mkdir builds +fi + +touch builds/dummy_ios_win.txt + + +#perl "$BASEDIR/build_ios_xwin.pl" "$@" || exit 1 diff --git a/external/buildscripts/build_runtime_android.pl b/external/buildscripts/build_runtime_android.pl new file mode 100644 index 000000000000..c81c9204b0b4 --- /dev/null +++ b/external/buildscripts/build_runtime_android.pl @@ -0,0 +1,32 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $androidArch = ""; +my $clean = 1; +my $windowsSubsystemForLinux = 0; +my $stevedoreBuildDeps = 1; + +GetOptions( + "androidarch=s"=>\$androidArch, + "clean=i"=>\$clean, + "windowssubsystemforlinux=i"=>\$windowsSubsystemForLinux, + "stevedorebuilddeps=i"=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +# By default, build runtime for all the variants we need. But allow something to specify an individual variation to build +if ($androidArch eq "") +{ + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--androidarch=armv7a", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=$windowsSubsystemForLinux", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono for armv7a\n"); + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--androidarch=x86", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=$windowsSubsystemForLinux", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono for x86\n"); +} +else +{ + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=$clean", "--artifact=1", "--arch32=1", "--androidarch=$androidArch", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=$windowsSubsystemForLinux", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono for $androidArch\n"); +} diff --git a/external/buildscripts/build_runtime_android.sh b/external/buildscripts/build_runtime_android.sh new file mode 100644 index 000000000000..2916d73d5e65 --- /dev/null +++ b/external/buildscripts/build_runtime_android.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +BASEDIR=$(dirname $0) + +perl "$BASEDIR/build_runtime_android.pl" "$@" diff --git a/external/buildscripts/build_runtime_iphone.pl b/external/buildscripts/build_runtime_iphone.pl new file mode 100644 index 000000000000..0c4b5bce4568 --- /dev/null +++ b/external/buildscripts/build_runtime_iphone.pl @@ -0,0 +1,67 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $clean = 1; +my $runtime = 0; +my $xcomp = 0; +my $simulator = 0; + +GetOptions( + "clean=i"=>\$clean, + "runtime=i"=>\$runtime, + "xcomp=i"=>\$xcomp, + "simulator=i"=>\$simulator, +) or die ("illegal cmdline options"); + +# Build everything by default +if ($runtime == 0 && $xcomp == 0 && $simulator == 0) +{ + print ">>> All iphone related builds will be ran\n"; + $runtime = 1; + $xcomp = 1; + $simulator = 1; +} + +if ($runtime) +{ + print ">>> Building iphone runtime\n"; + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=$clean", "--artifact=1", "--arch32=1", "--iphoneArch=armv7", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building mono for iphone\n"); +} + +if ($xcomp) +{ + # TODO : This is a horrible waste of time, and we should fix it, but for now it gets things working. + # The mono we have in the build deps for bootstrapping doesn't have a 32bit slice, which we need in order to run the MonoAotOffsetsDumper. + # To get around this for the moment, we'll build the runtime & classlibs first, and then use that to run the MonoAotOffsetsDumper. + # Once we update the mono in the build deps, we can remove this. + if(!(-f "$monoroot/builds/monodistribution/bin/mono")) + { + print ">>> Building mono to use for bootstrapping. The version in mono build deps is missing the 32bit slice and we need a 32bit version to run the MonoAotOffsetsDumper\n"; + #system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--arch32=1", "--clean=$clean", "--classlibtests=0", "--artifact=1", "--artifactscommon=1", "--forcedefaultbuilddeps=1") eq 0 or die ("failing building mono 32bit for bootstrapping\n"); + #system("perl", "$buildScriptsRoot/build_all_osx.pl", "--build=1", "--artifact=1", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building mono\n"); + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--artifactscommon=1", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building mono\n"); + + system("cp", "$monoroot/builds/monodistribution/bin-osx-tmp-i386/mono", "$monoroot/builds/monodistribution/bin/.") eq 0 or die ("failed to copy mono over from bin-osx-tmp-i386 to bin\n"); + system("chmod", "+x", "$monoroot/builds/monodistribution/bin/mono") eq 0 or die("Failed to chmod mono\n"); + + # Need to clean up the tmp build folder so that we don't pollute the final artifact + rmtree("$monoroot/builds/monodistribution/bin-osx-tmp-i386"); + rmtree("$monoroot/builds/embedruntimes/osx-tmp-i386"); + } + + print ">>> Building iphone cross compiler\n"; + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=$clean", "--artifact=1", "--arch32=1", "--iphonecross=1", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building iphone cross compiler\n"); +} + +if ($simulator) +{ + print ">>> Building iphone simulator\n"; + system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=$clean", "--artifact=1", "--arch32=1", "--iphonesimulator=1", "--forcedefaultbuilddeps=1") eq 0 or die ("Failed building iphone simulator\n"); +} diff --git a/external/buildscripts/build_runtime_iphone.sh b/external/buildscripts/build_runtime_iphone.sh new file mode 100644 index 000000000000..21452053675b --- /dev/null +++ b/external/buildscripts/build_runtime_iphone.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +BASEDIR=$(dirname $0) + +if [ -d builds ]; then + echo "Skip making builds directory. Already exists" +else + mkdir builds +fi + +if [ "x$1" == "x--runtime-only" ]; then + touch builds/dummy_iphone_runtime.txt +elif [ "x$1" == "x--xcomp-only" ]; then + touch builds/dummy_iphone_xcomp.txt +elif [ "x$1" == "x--simulator-only" ]; then + touch builds/dummy_iphone_sim.txt +fi diff --git a/external/buildscripts/build_runtime_linux.pl b/external/buildscripts/build_runtime_linux.pl new file mode 100644 index 000000000000..e52e4fb1e173 --- /dev/null +++ b/external/buildscripts/build_runtime_linux.pl @@ -0,0 +1,24 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; +my $stevedoreBuildDeps = 1; +my $build64 = 0; + +GetOptions( + "build64=i"=>\$build64, + "stevedorebuilddeps=i"=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +my $arch32 = 1; +if ($build64) +{ + $arch32 = 0; +} + +system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--test=1", "--artifact=1", "--arch32=$arch32", "--classlibtests=0", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_runtime_osx.pl b/external/buildscripts/build_runtime_osx.pl new file mode 100644 index 000000000000..ccf3ff98e481 --- /dev/null +++ b/external/buildscripts/build_runtime_osx.pl @@ -0,0 +1,16 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; +my $stevedoreBuildDeps=1; + +GetOptions( + 'stevedorebuilddeps=i'=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +system("perl", "$buildScriptsRoot/build_all_osx.pl", "--build=1", "--artifact=1", "--test=1", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_runtime_qnx.sh b/external/buildscripts/build_runtime_qnx.sh new file mode 100644 index 000000000000..b2eaaf63a0a8 --- /dev/null +++ b/external/buildscripts/build_runtime_qnx.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +# Note : Not Implemented yet. Script is here to make the katana build pass so that the mono build artifact is created +if [ -d builds ]; then + echo "Skip making builds directory. Already exists" +else + mkdir builds +fi + +touch builds/dummy_qnx.txt diff --git a/external/buildscripts/build_runtime_stv.sh b/external/buildscripts/build_runtime_stv.sh new file mode 100644 index 000000000000..4d8fb005059c --- /dev/null +++ b/external/buildscripts/build_runtime_stv.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +# Note : Not Implemented yet. Script is here to make the katana build pass so that the mono build artifact is created +if [ -d builds ]; then + echo "Skip making builds directory. Already exists" +else + mkdir builds +fi + +touch builds/dummy_stv.txt + diff --git a/external/buildscripts/build_runtime_tizen.sh b/external/buildscripts/build_runtime_tizen.sh new file mode 100644 index 000000000000..f7bc324230ea --- /dev/null +++ b/external/buildscripts/build_runtime_tizen.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# The build configs are looking for this file rather than the perl script. +# easier to add this + +# Note : Tizen support is removed. Script is here to make the katana build pass so that the mono build artifact is created +if [ -d builds ]; then + echo "Skip making builds directory. Already exists" +else + mkdir builds +fi + +touch builds/dummy_tizen.txt + diff --git a/external/buildscripts/build_runtime_vs.pl b/external/buildscripts/build_runtime_vs.pl new file mode 100644 index 000000000000..59aa19dbb04f --- /dev/null +++ b/external/buildscripts/build_runtime_vs.pl @@ -0,0 +1,57 @@ +sub CompileVCProj; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Spec; +use File::Basename; +use File::Copy; +use File::Path; + +print ">>> PATH in Build VS = $ENV{PATH}\n\n"; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildsroot = "$monoroot/builds"; +my $buildMachine = $ENV{UNITY_THISISABUILDMACHINE}; + +my $build = 0; +my $clean = 0; +my $arch32 = 0; +my $debug = 0; +my $gc = "bdwgc"; +my $msBuildVersion = ""; + +GetOptions( + 'build=i'=>\$build, + 'clean=i'=>\$clean, + 'arch32=i'=>\$arch32, + 'debug=i'=>\$debug, + 'msbuildversion=s'=>\$msBuildVersion, + 'gc=s'=>\$gc, +) or die ("illegal cmdline options"); + +if ($build) +{ + CompileVCProj("$monoroot/msvc/mono.sln"); +} + +sub CompileVCProj +{ + my $sln = shift; + my $config; + + my $msbuild = $ENV{"ProgramFiles(x86)"}."/MSBuild/$msBuildVersion/Bin/MSBuild.exe"; + + if (($ENV{YAMATO_PROJECT_ID}) || ($ENV{USERNAME} eq "bokken")) + { + $msbuild = $ENV{"ProgramFiles(x86)"}."/Microsoft Visual Studio/2017/Professional/MSBuild/15.0/Bin/MSBuild.exe"; + } + + $config = $debug ? "Debug" : "Release"; + my $arch = $arch32 ? "Win32" : "x64"; + my $target = $clean ? "/t:Clean,Build" :"/t:Build"; + my $properties = "/p:Configuration=$config;Platform=$arch;MONO_TARGET_GC=$gc"; + + print ">>> $msbuild $properties $target $sln\n\n"; + system($msbuild, $properties, $target, $sln) eq 0 + or die("MSBuild failed to build $sln\n"); +} diff --git a/external/buildscripts/build_runtime_win.pl b/external/buildscripts/build_runtime_win.pl new file mode 100644 index 000000000000..9988dcd7a930 --- /dev/null +++ b/external/buildscripts/build_runtime_win.pl @@ -0,0 +1,19 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; +my $stevedoreBuildDeps = 1; + +GetOptions( + "stevedorebuilddeps=i"=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +# Note : Ideally we can switch back to this build approach once the random cygwin hangs on the build machines are sorted out +#system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--test=1", "--artifact=1", "--arch32=1", "--classlibtests=0", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); + +system("perl", "$buildScriptsRoot/build_win_no_cygwin.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=1", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_runtime_win64.pl b/external/buildscripts/build_runtime_win64.pl new file mode 100644 index 000000000000..72ee81c780ba --- /dev/null +++ b/external/buildscripts/build_runtime_win64.pl @@ -0,0 +1,20 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +my $stevedoreBuildDeps = 1; + +GetOptions( + "stevedorebuilddeps=i"=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +# Note : Ideally we can switch back to this build approach once the random cygwin hangs on the build machines are sorted out +#system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--test=1", "--artifact=1", "--classlibtests=0", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); + +system("perl", "$buildScriptsRoot/build_win_no_cygwin.pl", "--build=1", "--clean=1", "--artifact=1", "--arch32=0", "--forcedefaultbuilddeps=1", "--stevedorebuilddeps=$stevedoreBuildDeps") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_runtime_wsl.pl b/external/buildscripts/build_runtime_wsl.pl new file mode 100644 index 000000000000..ba8169be68e9 --- /dev/null +++ b/external/buildscripts/build_runtime_wsl.pl @@ -0,0 +1,13 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; + +#Windows Subsystem for Linux currently does not support 32-bit, so the option is 64-bit only + +system("perl", "$buildScriptsRoot/build.pl", "--build=1", "--clean=1", "--test=1", "--artifact=1", "--arch32=0", "--classlibtests=0", "--forcedefaultbuilddeps=1", "--windowssubsystemforlinux=1") eq 0 or die ("Failed building mono\n"); diff --git a/external/buildscripts/build_win_no_cygwin.pl b/external/buildscripts/build_win_no_cygwin.pl new file mode 100644 index 000000000000..b37dc3638665 --- /dev/null +++ b/external/buildscripts/build_win_no_cygwin.pl @@ -0,0 +1,247 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; +use File::Copy; +use lib ('external/buildscripts', "../../Tools/perl_lib","perl_lib", 'external/buildscripts/perl_lib'); +use Tools qw(InstallNameTool); + +print ">>> PATH in Build All = $ENV{PATH}\n\n"; + +my $currentdir = getcwd(); + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); + +$monoroot =~ tr{/}{\\}; + +print ">>> monoroot = $monoroot\n"; + +my $buildscriptsdir = "$monoroot\\external\\buildscripts"; +my $addtoresultsdistdir = "$buildscriptsdir\\add_to_build_results\\monodistribution"; +my $monoprefix = "$monoroot\\tmp\\monoprefix"; +my $buildsroot = "$monoroot\\builds"; +my $distdir = "$buildsroot\\monodistribution"; +my $buildMachine = $ENV{UNITY_THISISABUILDMACHINE}; + +my $build=0; +my $clean=0; +my $artifact=0; +my $debug=0; +my $checkoutOnTheFly=0; +my $forceDefaultBuildDeps=0; +my $existingMonoRootPath = ''; +my $arch32 = 0; +my $winPerl = "perl"; +my $winMonoRoot = $monoroot; +my $msBuildVersion = "14.0"; +my $buildDeps = ""; +my $stevedoreBuildDeps=1; + +if($ENV{YAMATO_PROJECT_ID} || ($ENV{USERNAME} eq "bokken")) +{ + $msBuildVersion = "15.0"; +} + +print(">>> Build All Args = @ARGV\n"); + +GetOptions( + 'build=i'=>\$build, + 'clean=i'=>\$clean, + 'artifact=i'=>\$artifact, + 'debug=i'=>\$debug, + 'arch32=i'=>\$arch32, + 'existingmono=s'=>\$existingMonoRootPath, + 'winperl=s'=>\$winPerl, + 'winmonoroot=s'=>\$winMonoRoot, + 'msbuildversion=s'=>\$msBuildVersion, + 'checkoutonthefly=i'=>\$checkoutOnTheFly, + 'builddeps=s'=>\$buildDeps, + 'forcedefaultbuilddeps=i'=>\$forceDefaultBuildDeps, + 'stevedorebuilddeps=i'=>\$stevedoreBuildDeps, +) or die ("illegal cmdline options"); + +my $monoRevision = `git rev-parse HEAD`; +chdir("$buildscriptsdir") eq 1 or die ("failed to chdir : $buildscriptsdir\n"); +my $buildScriptsRevision = `git rev-parse HEAD`; +chdir("$monoroot") eq 1 or die ("failed to chdir : $monoroot\n"); + +print(">>> Mono Revision = $monoRevision\n"); +print(">>> Build Scripts Revision = $buildScriptsRevision\n"); + +# Do any settings agnostic per-platform stuff +my $externalBuildDeps = ""; + +if ($buildDeps ne "" && not $forceDefaultBuildDeps) +{ + $externalBuildDeps = $buildDeps; +} +else +{ + if($stevedoreBuildDeps) + { + $externalBuildDeps = "$monoroot/external/buildscripts/artifacts/Stevedore"; + } + else + { + $externalBuildDeps = "$monoroot/../../mono-build-deps/build"; + } +} +print(">>> External build deps = $externalBuildDeps\n"); + +my $existingExternalMonoRoot = "$externalBuildDeps\\mono"; +my $existingExternalMono = "$existingExternalMonoRoot\\win"; + +if ($clean) +{ + print(">>> Cleaning $monoprefix\n"); + rmtree($monoprefix); +} + +# ******************* Build Stage ************************** + +if ($build) +{ + if (!(-d "$externalBuildDeps")) + { + print(">>> mono-build-deps is not required for windows runtime builds...\n"); + } + + system("$winPerl", "$winMonoRoot/external/buildscripts/build_runtime_vs.pl", "--build=$build", "--arch32=$arch32", "--msbuildversion=$msBuildVersion", "--clean=$clean", "--debug=$debug", "--gc=bdwgc") eq 0 or die ('failed building mono bdwgc with VS\n'); + system("$winPerl", "$winMonoRoot/external/buildscripts/build_runtime_vs.pl", "--build=$build", "--arch32=$arch32", "--msbuildversion=$msBuildVersion", "--clean=$clean", "--debug=$debug", "--gc=sgen") eq 0 or die ('failed building mono sgen with VS\n'); + + if (!(-d "$monoroot\\tmp")) + { + print(">>> Creating directory $monoroot\\tmp\n"); + system("mkdir $monoroot\\tmp") eq 0 or die ("failing creating $monoroot\\tmp\n");; + } + + if (!(-d "$monoprefix")) + { + print(">>> Creating directory $monoprefix\n"); + system("mkdir $monoprefix") eq 0 or die ("failing creating $monoprefix\n");; + } + + if (!(-d "$monoprefix\\bin")) + { + print(">>> Creating directory $monoprefix\\bin\n"); + system("mkdir $monoprefix\\bin") eq 0 or die ("failing creating $monoprefix\\bin\n");; + } + + # Copy over the VS built stuff that we want to use instead into the prefix directory + my $archNameForBuild = $arch32 ? 'Win32' : 'x64'; + my $configDirName = $debug ? "Debug" : "Release"; + + copy("$monoroot/msvc/build/bdwgc/$archNameForBuild/bin/$configDirName/mono-bdwgc.exe", "$monoprefix/bin/.") or die ("failed copying mono-bdwgc.exe\n"); + copy("$monoroot/msvc/build/bdwgc/$archNameForBuild/bin/$configDirName/mono-2.0-bdwgc.dll", "$monoprefix/bin/.") or die ("failed copying mono-2.0-bdwgc.dll\n"); + copy("$monoroot/msvc/build/bdwgc/$archNameForBuild/bin/$configDirName/mono-2.0-bdwgc.pdb", "$monoprefix/bin/.") or die ("failed copying mono-2.0-bdwgc.pdb\n"); + + copy("$monoroot/msvc/build/sgen/$archNameForBuild/bin/$configDirName/mono-sgen.exe", "$monoprefix/bin/.") or die ("failed copying mono-sgen.exe\n"); + copy("$monoroot/msvc/build/sgen/$archNameForBuild/bin/$configDirName/mono-2.0-sgen.dll", "$monoprefix/bin/.") or die ("failed copying mono-2.0-sgen.dll\n"); + copy("$monoroot/msvc/build/sgen/$archNameForBuild/bin/$configDirName/mono-2.0-sgen.pdb", "$monoprefix/bin/.") or die ("failed copying mono-2.0-sgen.pdb\n"); + + # sgen as default exe + copy("$monoroot/msvc/build/sgen/$archNameForBuild/bin/$configDirName/mono-sgen.exe", "$monoprefix/bin/mono.exe") or die ("failed copying mono-sgen.exe to mono.exe\n"); + + copy("$monoroot/msvc/build/bdwgc/$archNameForBuild/bin/$configDirName/MonoPosixHelper.dll", "$monoprefix/bin/.") or die ("failed copying MonoPosixHelper.dll\n"); + copy("$monoroot/msvc/build/bdwgc/$archNameForBuild/bin/$configDirName/MonoPosixHelper.pdb", "$monoprefix/bin/.") or die ("failed copying MonoPosixHelper.pdb\n"); + + system("xcopy /y /f $addtoresultsdistdir\\bin\\*.* $monoprefix\\bin\\") eq 0 or die ("Failed copying $addtoresultsdistdir/bin to $monoprefix/bin\n"); +} + +# ******************* Artifact Stage ************************** + +if ($artifact) +{ + print(">>> Creating artifact...\n"); + + # Do the platform specific logic to create the builds output structure that we want + + my $embedDirRoot = "$buildsroot\\embedruntimes"; + + my $embedDirArchDestination = $arch32 ? "$embedDirRoot\\win32" : "$embedDirRoot\\win64"; + my $distDirArchBin = $arch32 ? "$distdir\\bin" : "$distdir\\bin-x64"; + my $versionsOutputFile = $arch32 ? "$buildsroot\\versions-win32.txt" : "$buildsroot\\versions-win64.txt"; + + # Make sure the directory for our architecture is clean before we copy stuff into it + if (-d "$embedDirArchDestination") + { + print(">>> Cleaning $embedDirArchDestination\n"); + rmtree($embedDirArchDestination); + } + + if (-d "$distDirArchBin") + { + print(">>> Cleaning $distDirArchBin\n"); + rmtree($distDirArchBin); + } + + if (!(-d "$buildsroot")) + { + print(">>> Creating directory $buildsroot\n"); + system("mkdir $buildsroot") eq 0 or die("failed to create directory $buildsroot\n"); + } + + if (!(-d "$embedDirRoot")) + { + print(">>> Creating directory $embedDirRoot\n"); + system("mkdir $embedDirRoot") eq 0 or die("failed to create directory $embedDirRoot\n"); + } + + if (!(-d "$distdir")) + { + print(">>> Creating directory $distdir\n"); + system("mkdir $distdir") eq 0 or die("failed to create directory $distdir\n"); + } + + print(">>> Creating directory $embedDirArchDestination\n"); + system("mkdir $embedDirArchDestination") eq 0 or die("failed to create directory $embedDirArchDestination\n"); + + print(">>> Creating directory $distDirArchBin\n"); + system("mkdir $distDirArchBin") eq 0 or die("failed to create directory $distDirArchBin\n"); + + # embedruntimes directory setup + print(">>> Creating embedruntimes directory : $embedDirArchDestination\n"); + + copy("$monoprefix/bin/mono-2.0-bdwgc.dll", "$embedDirArchDestination/.") or die ("failed copying mono-2.0-bdwgc.dll\n"); + copy("$monoprefix/bin/mono-2.0-bdwgc.pdb", "$embedDirArchDestination/.") or die ("failed copying mono-2.0-bdwgc.pdb\n"); + + copy("$monoprefix/bin/mono-2.0-sgen.dll", "$embedDirArchDestination/.") or die ("failed copying mono-2.0-sgen.dll\n"); + copy("$monoprefix/bin/mono-2.0-sgen.pdb", "$embedDirArchDestination/.") or die ("failed copying mono-2.0-sgen.pdb\n"); + + copy("$monoprefix/bin/MonoPosixHelper.dll", "$embedDirArchDestination/.") or die ("failed copying MonoPosixHelper.dll\n"); + copy("$monoprefix/bin/MonoPosixHelper.pdb", "$embedDirArchDestination/.") or die ("failed copying MonoPosixHelper.pdb\n"); + + # monodistribution directory setup + print(">>> Creating monodistribution directory\n"); + copy("$monoprefix/bin/mono-2.0-bdwgc.dll", "$distDirArchBin/.") or die ("failed copying mono-2.0-bdwgc.dll\n"); + copy("$monoprefix/bin/mono-2.0-bdwgc.pdb", "$distDirArchBin/.") or die ("failed copying mono-2.0-bdwgc.pdb\n"); + + copy("$monoprefix/bin/mono-2.0-sgen.dll", "$distDirArchBin/.") or die ("failed copying mono-2.0-sgen.dll\n"); + copy("$monoprefix/bin/mono-2.0-sgen.pdb", "$distDirArchBin/.") or die ("failed copying mono-2.0-sgen.pdb\n"); + + copy("$monoprefix/bin/mono-sgen.exe", "$distDirArchBin/.") or die ("failed copying mono-sgen.exe\n"); + copy("$monoprefix/bin/mono-bdwgc.exe", "$distDirArchBin/.") or die ("failed copying mono-bdwgc.exe\n"); + copy("$monoprefix/bin/mono.exe", "$distDirArchBin/.") or die ("failed copying mono.exe\n"); + + copy("$monoprefix/bin/MonoPosixHelper.dll", "$distDirArchBin/.") or die ("failed copying MonoPosixHelper.dll\n"); + copy("$monoprefix/bin/MonoPosixHelper.pdb", "$distDirArchBin/.") or die ("failed copying MonoPosixHelper.pdb\n"); + + + # Output version information + print(">>> Creating version file : $versionsOutputFile\n"); + open(my $fh, '>', $versionsOutputFile) or die "Could not open file '$versionsOutputFile' $!"; + say $fh "mono-version ="; + my $monoVersionInfo = `$distDirArchBin\\mono --version`; + say $fh "$monoVersionInfo"; + say $fh "unity-mono-revision = $monoRevision"; + say $fh "unity-mono-build-scripts-revision = $buildScriptsRevision"; + my $tmp = `date /T`; + say $fh "$tmp"; + close $fh; +} +else +{ + print(">>> Skipping artifact creation\n"); +} diff --git a/external/buildscripts/build_win_wrapper.pl b/external/buildscripts/build_win_wrapper.pl new file mode 100644 index 000000000000..64c787750c34 --- /dev/null +++ b/external/buildscripts/build_win_wrapper.pl @@ -0,0 +1,218 @@ +use Cwd; +use Cwd 'abs_path'; +use Getopt::Long; +use File::Basename; +use File::Path; +use Config; + +print ">>> My Path: $ENV{PATH}\n\n"; + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $buildScriptsRoot = "$monoroot/external/buildscripts"; +print ">>> Mono checkout found in $monoroot\n\n"; + +my $cygwinRootWindows = ""; +my $monoInstallLinux = ""; +my $checkoutOnTheFly=0; +my $buildDeps = ""; +my $forceDefaultBuildDeps = 0; + +my @thisScriptArgs = (); +my @passAlongArgs = (); +foreach my $arg (@ARGV) +{ + push @backupArgs, $arg; + + if ($arg =~ /^--cygwin=/) + { + push @thisScriptArgs, $arg; + } + elsif ($arg =~ /^--existingmono=/) + { + push @thisScriptArgs, $arg; + } + elsif ($arg =~ /^--checkoutonthefly=/) + { + push @thisScriptArgs, $arg; + push @passAlongArgs, $arg; + } + elsif ($arg =~ /^--builddeps=/) + { + push @thisScriptArgs, $arg; + push @passAlongArgs, $arg; + } + elsif ($arg =~ /^--forcedefaultbuilddeps=/) + { + push @thisScriptArgs, $arg; + push @passAlongArgs, $arg; + } + else + { + push @passAlongArgs, $arg; + } +} + +print(">>> This Script Args = @thisScriptArgs\n"); +print(">>> Pass Along Args = @passAlongArgs\n"); + +@ARGV = @thisScriptArgs; +GetOptions( + 'cygwin=s'=>\$cygwinRootWindows, + 'existingmono=s'=>\$monoInstallLinux, + 'checkoutonthefly=i'=>\$checkoutOnTheFly, + 'builddeps=s'=>\$buildDeps, + 'forcedefaultbuilddeps=i'=>\$forceDefaultBuildDeps, +); + +my $externalBuildDeps = ""; + +if ($buildDeps ne "") +{ + $externalBuildDeps = $buildDeps; +} +else +{ + if (-d "$monoroot/../../mono-build-deps/build" || $forceDefaultBuildDeps) + { + $externalBuildDeps = "$monoroot/../../mono-build-deps/build"; + } + + if (!(-d "$externalBuildDeps")) + { + if (not $checkoutonthefly && $cygwinRootWindows eq "") + { + print(">>> No external build deps found and --cygwin not used. Might as well try to check them out. If the checkout fails, we'll continue, but the build will probably fail\n"); + } + + # Check out on the fly + print(">>> Checking out mono build dependencies to : $externalBuildDeps\n"); + my $repo = "https://ono.unity3d.com/unity-extra/mono-build-deps"; + print(">>> Cloning $repo at $externalBuildDeps\n"); + my $checkoutResult = system("hg", "clone", $repo, "$externalBuildDeps"); + + if ($checkoutOnTheFly && $checkoutResult ne 0) + { + die("failed to checkout mono build dependencies\n"); + } + } +} + +print(">>> externalBuildDeps = $externalBuildDeps\n"); + +my $SevenZip = "$externalBuildDeps/7za-win-x64/7za.exe"; + +# Attempt to find common default cygwin install locations +if ($cygwinRootWindows eq "") +{ + print(">>> No cygwin install specified. Looking for defaults...\n"); + + my $externalCygwin = "$externalBuildDeps/cygwin64/builds"; + my $externalCygwinZip = "$externalBuildDeps/cygwin64/builds.zip"; + + if (-d "$externalCygwin") + { + $cygwinRootWindows = $externalCygwin; + print(">>> Found Cygwin at : $cygwinRootWindows\n"); + } + elsif(-f "$externalCygwinZip") + { + print(">>> Found unextracted cygwin builds.zip : $externalCygwinZip\n"); + print(">>> Using 7z : $SevenZip\n"); + print(">>> Extracting...\n"); + system("$SevenZip", "x", "$externalCygwinZip", "-o$externalBuildDeps/cygwin64") eq 0 or die("Failed extracting cygwin\n"); + $cygwinRootWindows = $externalCygwin; + } + else + { + if ($forceDefaultBuildDeps) + { + die("\nCould not fined Cygwin in default external build deps location : $externalBuildDeps\n") + } + else + { + if (-d "C:\\Cygwin64") + { + $cygwinRootWindows = "C:\\Cygwin64"; + print(">>> Found Cygwin at : $cygwinRootWindows\n"); + } + elsif (-d "C:\\Cygwin") + { + $cygwinRootWindows = "C:\\Cygwin"; + print(">>> Found Cygwin at : $cygwinRootWindows\n"); + } + else + { + die("\nCould not fined Cygwin. Define path using --cygwin=\n") + } + } + } +} +else +{ + print(">>> Cygwin Path = $cygwinRootWindows\n"); +} + +if ($monoInstallLinux eq "") +{ + print(">>> No mono install specified. Looking for defaults...\n"); + + my $externalMono = "$externalBuildDeps/mono/win/builds"; + my $externalMonoZip = "$externalBuildDeps/mono/win/builds.zip"; + + if (-d "$externalMono") + { + $monoInstallLinux = $externalMono; + $monoInstallLinux =~ s/\\/\//g; + print(">>> Found Mono at : $monoInstallLinux\n"); + } + elsif(-f "$externalMonoZip") + { + print(">>> Found unextracted mono builds.zip : $externalMonoZip\n"); + print(">>> Using 7z : $SevenZip\n"); + print(">>> Extracting...\n"); + system("$SevenZip", "x", "$externalMonoZip", "-o$externalBuildDeps/mono/win") eq 0 or die("Failed extracting mono\n"); + $monoInstallLinux = $externalMono; + $monoInstallLinux =~ s/\\/\//g; + print(">>> Found Mono at : $monoInstallLinux\n"); + } + else + { + if ($forceDefaultBuildDeps) + { + die("\nCould not fined mono in default external build deps location : $externalBuildDeps\n") + } + else + { + if (-d "C:\\Program Files (x86)\\Mono") + { + # Pass over the cygwin format since I already have it escaped correctly to survive + # crossing over the shell + $monoInstallLinux = "/cygdrive/c/Program\\ Files\\ \\(x86\\)/Mono"; + print(">>> Found Mono at : $monoInstallLinux\n"); + } + else + { + die("\n--existingmono= is required and should be in the cygwin path format\n"); + } + } + } +} +else +{ + $monoInstallLinux =~ s/\\/\//g; + print(">>> Linux Mono Path = $monoInstallLinux\n"); +} + +push @passAlongArgs, "--existingmono=$monoInstallLinux" if $monoInstallLinux ne ""; + +my $windowsPerl = $Config{perlpath}; +print ">>> Perl Exe = $windowsPerl\n"; +push @passAlongArgs, "--winperl=$windowsPerl"; +push @passAlongArgs, "--winmonoroot=$monoroot"; + +# In some cases the file gets windowsified, use SHELLOPTS to avoid issues instead of dos2unixing the file, which will cause it to show up as modified by source control +$ENV{'SHELLOPTS'} = "igncr"; + +print ">>> Calling $cygwinRootWindows\\bin\\sh.exe with @passAlongArgs"; +system("$cygwinRootWindows\\bin\\sh.exe", "$monoroot/external/buildscripts/build_win_wrapper.sh", @passAlongArgs) eq 0 or die("failed building mono\n"); diff --git a/external/buildscripts/build_win_wrapper.sh b/external/buildscripts/build_win_wrapper.sh new file mode 100644 index 000000000000..c71964f87986 --- /dev/null +++ b/external/buildscripts/build_win_wrapper.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# Need to put the cygwin stuff into our PATH +export PATH=/usr/bin:$PATH +BUILD_SCRIPT_ROOT=$(dirname $0) + +echo ">>> Build Script Root = $BUILD_SCRIPT_ROOT" +echo +echo ">>> PATH in Win Shell Script = $PATH" +echo + +perl "$BUILD_SCRIPT_ROOT/build.pl" "$@" \ No newline at end of file diff --git a/external/buildscripts/collect_allbuilds.pl b/external/buildscripts/collect_allbuilds.pl new file mode 100644 index 000000000000..78140185c9d1 --- /dev/null +++ b/external/buildscripts/collect_allbuilds.pl @@ -0,0 +1,124 @@ +use lib ('external/buildscripts/perl_lib'); +use Cwd 'abs_path'; +use File::Basename; +use File::Copy::Recursive qw(dircopy rmove); +use File::Path; +use Tools qw(InstallNameTool); + + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); + +my $path = "incomingbuilds/"; + +rmtree("collectedbuilds"); +mkpath("collectedbuilds"); + +my @folders = (); +opendir(DIR, $path) or die "cant find $path: $!"; +# Sort the directories alphabetically so that classlibs comes before the +# OSX universal runtime (in the osx-i386 directory). Both builds produce the same +# files in some cases (notably libMonoPosixHelper.dylib), and we need the +# universal runtime build to be second, since it produces a universal binary +# and the classlibs build produces a 32-bit binary only. +my @files = sort readdir(DIR); +while (defined(my $file = shift @files)) { + + next if $file =~ /^\.\.?$/; + if (-d "$path$file"){ + if (-f "$path$file/versions.txt") { + system("cat $path$file/versions.txt >> collectedbuilds/versions-aggregated.txt"); + } + dircopy("$path$file","collectedbuilds/") or die ("failed copying $path$file"); + push @folders,"$path$file"; + } +} +closedir(DIR); + +system("find collectedbuilds -type f -name mono -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name mono-sgen -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name pedump -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name cli -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name cli_x86 -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name ilasm -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name mcs -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name mono-env -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name monolinker -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name nunit-console -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name nunit-console2 -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name resgen2 -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); +system("find collectedbuilds -type f -name xbuild -exec chmod +x {} \\;") eq 0 or die("Failed chmodding"); + +chdir("collectedbuilds"); + +rmove('versions-aggregated.txt', 'versions.txt'); + +my $externalBuildDeps = "$monoroot/external/buildscripts/artifacts/Stevedore"; +my $externalzip = ""; +if($^O eq "linux") +{ + $externalzip = "$externalBuildDeps/7za-linux-x64/7za"; +} +elsif($^O eq 'darwin') +{ + $externalzip = "$externalBuildDeps/7za-mac-x64/7za"; +} + + +# Create stevedore artifact +print(">>> Create stevedore artifact $monoroot/stevedore/MonoBleedingEdge.7z"); +if($^O eq "linux" || $^O eq 'darwin') +{ + rmtree("../stevedore"); + my $stevedoreMbePath = "../stevedore/MonoBleedingEdge"; + my $stevedoreMbe7z = "../stevedore/MonoBleedingEdge.7z"; + my $stevedoreMbeArtifactID = "../stevedore/artifactid.txt"; + + system("mkdir -p $stevedoreMbePath") eq 0 or die("failed to mkdir $stevedoreMbePath"); + system("cp -r * $stevedoreMbePath/") eq 0 or die ("failed copying builds to $stevedoreMbePath\n"); + if(-f $externalzip) + { + system("$externalzip a $stevedoreMbe7z $stevedoreMbePath/* -sdel") eq 0 or die("failed 7z up $stevedoreMbePath"); + } + else + { + #Use 7z installed on the machine. If its not installed, please install it. + system("7z a $stevedoreMbe7z $stevedoreMbePath/* -sdel") eq 0 or die("failed 7z up $stevedoreMbePath"); + } + system("rm -rf $stevedoreMbePath") eq 0 or die("failed to delete $stevedoreMbePath"); + + # Write stevedore artifact ID to file + my $revision = `git rev-parse --short HEAD`; + system("mono ../external/buildscripts/bee.exe steve new $stevedoreMbe7z MonoBleedingEdge $revision") eq 0 or die("failed running bee"); + open (my $file, '>', $stevedoreMbeArtifactID); + my $artifactID = `mono ../external/buildscripts/bee.exe steve new $stevedoreMbe7z MonoBleedingEdge $revision`; + print $file $artifactID; + print (">>> MonoBleedingEdge stevedore artifact ID: $artifactID\n"); +} +else +{ + die("Unsupported platform to create stevedore artifact.") +} +print(">>> Done creating stevedore artifact $monoroot/MonoBleedingEdge.7z"); + +system("zip -r builds.zip *") eq 0 or die("failed zipping up builds"); + +if($^O eq "linux" || $^O eq 'darwin') +{ + if(-f $externalzip) + { + system("$externalzip a builds.7z * -x!builds.zip") eq 0 or die("failed 7z up builds"); + } + else + { + #Use 7z installed on the machine. If its not installed, please install it. + system("7z a builds.7z * -x!builds.zip") eq 0 or die("failed 7z up builds"); + } +} +else +{ + die("Unsupported platform for build collection.") +} + + + diff --git a/external/buildscripts/perl_lib/File/Copy/Recursive.pm b/external/buildscripts/perl_lib/File/Copy/Recursive.pm new file mode 100644 index 000000000000..c5fb2576f8df --- /dev/null +++ b/external/buildscripts/perl_lib/File/Copy/Recursive.pm @@ -0,0 +1,696 @@ +package File::Copy::Recursive; + +use strict; +BEGIN { + # Keep older versions of Perl from trying to use lexical warnings + $INC{'warnings.pm'} = "fake warnings entry for < 5.6 perl ($])" if $] < 5.006; +} +use warnings; + +use Carp; +use File::Copy; +use File::Spec; #not really needed because File::Copy already gets it, but for good measure :) + +use vars qw( + @ISA @EXPORT_OK $VERSION $MaxDepth $KeepMode $CPRFComp $CopyLink + $PFSCheck $RemvBase $NoFtlPth $ForcePth $CopyLoop $RMTrgFil $RMTrgDir + $CondCopy $BdTrgWrn $SkipFlop $DirPerms +); + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(fcopy rcopy dircopy fmove rmove dirmove pathmk pathrm pathempty pathrmdir); +$VERSION = '0.38'; + +$MaxDepth = 0; +$KeepMode = 1; +$CPRFComp = 0; +$CopyLink = eval { local $SIG{'__DIE__'};symlink '',''; 1 } || 0; +$PFSCheck = 1; +$RemvBase = 0; +$NoFtlPth = 0; +$ForcePth = 0; +$CopyLoop = 0; +$RMTrgFil = 0; +$RMTrgDir = 0; +$CondCopy = {}; +$BdTrgWrn = 0; +$SkipFlop = 0; +$DirPerms = 0777; + +my $samecheck = sub { + return 1 if $^O eq 'MSWin32'; # need better way to check for this on winders... + return if @_ != 2 || !defined $_[0] || !defined $_[1]; + return if $_[0] eq $_[1]; + + my $one = ''; + if($PFSCheck) { + $one = join( '-', ( stat $_[0] )[0,1] ) || ''; + my $two = join( '-', ( stat $_[1] )[0,1] ) || ''; + if ( $one eq $two && $one ) { + carp "$_[0] and $_[1] are identical"; + return; + } + } + + if(-d $_[0] && !$CopyLoop) { + $one = join( '-', ( stat $_[0] )[0,1] ) if !$one; + my $abs = File::Spec->rel2abs($_[1]); + my @pth = File::Spec->splitdir( $abs ); + while(@pth) { + my $cur = File::Spec->catdir(@pth); + last if !$cur; # probably not necessary, but nice to have just in case :) + my $two = join( '-', ( stat $cur )[0,1] ) || ''; + if ( $one eq $two && $one ) { + # $! = 62; # Too many levels of symbolic links + carp "Caught Deep Recursion Condition: $_[0] contains $_[1]"; + return; + } + + pop @pth; + } + } + + return 1; +}; + +my $glob = sub { + my ($do, $src_glob, @args) = @_; + + local $CPRFComp = 1; + + my @rt; + for my $path ( glob($src_glob) ) { + my @call = [$do->($path, @args)] or return; + push @rt, \@call; + } + + return @rt; +}; + +my $move = sub { + my $fl = shift; + my @x; + if($fl) { + @x = fcopy(@_) or return; + } else { + @x = dircopy(@_) or return; + } + if(@x) { + if($fl) { + unlink $_[0] or return; + } else { + pathrmdir($_[0]) or return; + } + if($RemvBase) { + my ($volm, $path) = File::Spec->splitpath($_[0]); + pathrm(File::Spec->catpath($volm,$path,''), $ForcePth, $NoFtlPth) or return; + } + } + return wantarray ? @x : $x[0]; +}; + +my $ok_todo_asper_condcopy = sub { + my $org = shift; + my $copy = 1; + if(exists $CondCopy->{$org}) { + if($CondCopy->{$org}{'md5'}) { + + } + if($copy) { + + } + } + return $copy; +}; + +sub fcopy { + $samecheck->(@_) or return; + if($RMTrgFil && (-d $_[1] || -e $_[1]) ) { + my $trg = $_[1]; + if( -d $trg ) { + my @trgx = File::Spec->splitpath( $_[0] ); + $trg = File::Spec->catfile( $_[1], $trgx[ $#trgx ] ); + } + $samecheck->($_[0], $trg) or return; + if(-e $trg) { + if($RMTrgFil == 1) { + unlink $trg or carp "\$RMTrgFil failed: $!"; + } else { + unlink $trg or return; + } + } + } + my ($volm, $path) = File::Spec->splitpath($_[1]); + if($path && !-d $path) { + pathmk(File::Spec->catpath($volm,$path,''), $NoFtlPth); + } + if( -l $_[0] && $CopyLink ) { + carp "Copying a symlink ($_[0]) whose target does not exist" + if !-e readlink($_[0]) && $BdTrgWrn; + symlink readlink(shift()), shift() or return; + } else { + copy(@_) or return; + + my @base_file = File::Spec->splitpath($_[0]); + my $mode_trg = -d $_[1] ? File::Spec->catfile($_[1], $base_file[ $#base_file ]) : $_[1]; + + chmod scalar((stat($_[0]))[2]), $mode_trg if $KeepMode; + } + return wantarray ? (1,0,0) : 1; # use 0's incase they do math on them and in case rcopy() is called in list context = no uninit val warnings +} + +sub rcopy { + if (-l $_[0] && $CopyLink) { + goto &fcopy; + } + + goto &dircopy if -d $_[0] || substr( $_[0], ( 1 * -1), 1) eq '*'; + goto &fcopy; +} + +sub rcopy_glob { + $glob->(\&rcopy, @_); +} + +sub dircopy { + if($RMTrgDir && -d $_[1]) { + if($RMTrgDir == 1) { + pathrmdir($_[1]) or carp "\$RMTrgDir failed: $!"; + } else { + pathrmdir($_[1]) or return; + } + } + my $globstar = 0; + my $_zero = $_[0]; + my $_one = $_[1]; + if ( substr( $_zero, ( 1 * -1 ), 1 ) eq '*') { + $globstar = 1; + $_zero = substr( $_zero, 0, ( length( $_zero ) - 1 ) ); + } + + $samecheck->( $_zero, $_[1] ) or return; + if ( !-d $_zero || ( -e $_[1] && !-d $_[1] ) ) { + $! = 20; + return; + } + + if(!-d $_[1]) { + pathmk($_[1], $NoFtlPth) or return; + } else { + if($CPRFComp && !$globstar) { + my @parts = File::Spec->splitdir($_zero); + while($parts[ $#parts ] eq '') { pop @parts; } + $_one = File::Spec->catdir($_[1], $parts[$#parts]); + } + } + my $baseend = $_one; + my $level = 0; + my $filen = 0; + my $dirn = 0; + + my $recurs; #must be my()ed before sub {} since it calls itself + $recurs = sub { + my ($str,$end,$buf) = @_; + $filen++ if $end eq $baseend; + $dirn++ if $end eq $baseend; + + $DirPerms = oct($DirPerms) if substr($DirPerms,0,1) eq '0'; + mkdir($end,$DirPerms) or return if !-d $end; + chmod scalar((stat($str))[2]), $end if $KeepMode; + if($MaxDepth && $MaxDepth =~ m/^\d+$/ && $level >= $MaxDepth) { + return ($filen,$dirn,$level) if wantarray; + return $filen; + } + $level++; + + + my @files; + if ( $] < 5.006 ) { + opendir(STR_DH, $str) or return; + @files = grep( $_ ne '.' && $_ ne '..', readdir(STR_DH)); + closedir STR_DH; + } + else { + opendir(my $str_dh, $str) or return; + @files = grep( $_ ne '.' && $_ ne '..', readdir($str_dh)); + closedir $str_dh; + } + + for my $file (@files) { + my ($file_ut) = $file =~ m{ (.*) }xms; + my $org = File::Spec->catfile($str, $file_ut); + my $new = File::Spec->catfile($end, $file_ut); + if( -l $org && $CopyLink ) { + carp "Copying a symlink ($org) whose target does not exist" + if !-e readlink($org) && $BdTrgWrn; + symlink readlink($org), $new or return; + } + elsif(-d $org) { + $recurs->($org,$new,$buf) if defined $buf; + $recurs->($org,$new) if !defined $buf; + $filen++; + $dirn++; + } + else { + if($ok_todo_asper_condcopy->($org)) { + if($SkipFlop) { + fcopy($org,$new,$buf) or next if defined $buf; + fcopy($org,$new) or next if !defined $buf; + } + else { + fcopy($org,$new,$buf) or return if defined $buf; + fcopy($org,$new) or return if !defined $buf; + } + chmod scalar((stat($org))[2]), $new if $KeepMode; + $filen++; + } + } + } + 1; + }; + + $recurs->($_zero, $_one, $_[2]) or return; + return wantarray ? ($filen,$dirn,$level) : $filen; +} + +sub fmove { $move->(1, @_) } + +sub rmove { + if (-l $_[0] && $CopyLink) { + goto &fmove; + } + + goto &dirmove if -d $_[0] || substr( $_[0], ( 1 * -1), 1) eq '*'; + goto &fmove; +} + +sub rmove_glob { + $glob->(\&rmove, @_); +} + +sub dirmove { $move->(0, @_) } + +sub pathmk { + my @parts = File::Spec->splitdir( shift() ); + my $nofatal = shift; + my $pth = $parts[0]; + my $zer = 0; + if(!$pth) { + $pth = File::Spec->catdir($parts[0],$parts[1]); + $zer = 1; + } + for($zer..$#parts) { + $DirPerms = oct($DirPerms) if substr($DirPerms,0,1) eq '0'; + mkdir($pth,$DirPerms) or return if !-d $pth && !$nofatal; + mkdir($pth,$DirPerms) if !-d $pth && $nofatal; + $pth = File::Spec->catdir($pth, $parts[$_ + 1]) unless $_ == $#parts; + } + 1; +} + +sub pathempty { + my $pth = shift; + + return 2 if !-d $pth; + + my @names; + my $pth_dh; + if ( $] < 5.006 ) { + opendir(PTH_DH, $pth) or return; + @names = grep !/^\.+$/, readdir(PTH_DH); + } + else { + opendir($pth_dh, $pth) or return; + @names = grep !/^\.+$/, readdir($pth_dh); + } + + for my $name (@names) { + my ($name_ut) = $name =~ m{ (.*) }xms; + my $flpth = File::Spec->catdir($pth, $name_ut); + + if( -l $flpth ) { + unlink $flpth or return; + } + elsif(-d $flpth) { + pathrmdir($flpth) or return; + } + else { + unlink $flpth or return; + } + } + + if ( $] < 5.006 ) { + closedir PTH_DH; + } + else { + closedir $pth_dh; + } + + 1; +} + +sub pathrm { + my $path = shift; + return 2 if !-d $path; + my @pth = File::Spec->splitdir( $path ); + my $force = shift; + + while(@pth) { + my $cur = File::Spec->catdir(@pth); + last if !$cur; # necessary ??? + if(!shift()) { + pathempty($cur) or return if $force; + rmdir $cur or return; + } + else { + pathempty($cur) if $force; + rmdir $cur; + } + pop @pth; + } + 1; +} + +sub pathrmdir { + my $dir = shift; + if( -e $dir ) { + return if !-d $dir; + } + else { + return 2; + } + + pathempty($dir) or return; + + rmdir $dir or return; +} + +1; + +__END__ + +=head1 NAME + +File::Copy::Recursive - Perl extension for recursively copying files and directories + +=head1 SYNOPSIS + + use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); + + fcopy($orig,$new[,$buf]) or die $!; + rcopy($orig,$new[,$buf]) or die $!; + dircopy($orig,$new[,$buf]) or die $!; + + fmove($orig,$new[,$buf]) or die $!; + rmove($orig,$new[,$buf]) or die $!; + dirmove($orig,$new[,$buf]) or die $!; + + rcopy_glob("orig/stuff-*", $trg [, $buf]) or die $!; + rmove_glob("orig/stuff-*", $trg [,$buf]) or die $!; + +=head1 DESCRIPTION + +This module copies and moves directories recursively (or single files, well... singley) to an optional depth and attempts to preserve each file or directory's mode. + +=head1 EXPORT + +None by default. But you can export all the functions as in the example above and the path* functions if you wish. + +=head2 fcopy() + +This function uses File::Copy's copy() function to copy a file but not a directory. Any directories are recursively created if need be. +One difference to File::Copy::copy() is that fcopy attempts to preserve the mode (see Preserving Mode below) +The optional $buf in the synopsis if the same as File::Copy::copy()'s 3rd argument +returns the same as File::Copy::copy() in scalar context and 1,0,0 in list context to accomidate rcopy()'s list context on regular files. (See below for more info) + +=head2 dircopy() + +This function recursively traverses the $orig directory's structure and recursively copies it to the $new directory. +$new is created if necessary (multiple non existant directories is ok (IE foo/bar/baz). The script logically and portably creates all of them if necessary). +It attempts to preserve the mode (see Preserving Mode below) and +by default it copies all the way down into the directory, (see Managing Depth) below. +If a directory is not specified it croaks just like fcopy croaks if its not a file that is specified. + +returns true or false, for true in scalar context it returns the number of files and directories copied, +In list context it returns the number of files and directories, number of directories only, depth level traversed. + + my $num_of_files_and_dirs = dircopy($orig,$new); + my($num_of_files_and_dirs,$num_of_dirs,$depth_traversed) = dircopy($orig,$new); + +Normally it stops and return's if a copy fails, to continue on regardless set $File::Copy::Recursive::SkipFlop to true. + + local $File::Copy::Recursive::SkipFlop = 1; + +That way it will copy everythgingit can ina directory and won't stop because of permissions, etc... + +=head2 rcopy() + +This function will allow you to specify a file *or* directory. It calls fcopy() if its a file and dircopy() if its a directory. +If you call rcopy() (or fcopy() for that matter) on a file in list context, the values will be 1,0,0 since no directories and no depth are used. +This is important becasue if its a directory in list context and there is only the initial directory the return value is 1,1,1. + +=head2 rcopy_glob() + +This function lets you specify a pattern suitable for perl's glob() as the first argument. Subsequently each path returned by perl's glob() gets rcopy()ied. + +It returns and array whose items are array refs that contain the return value of each rcopy() call. + +It forces behavior as if $File::Copy::Recursive::CPRFComp is true. + +=head2 fmove() + +Copies the file then removes the original. You can manage the path the original file is in according to $RemvBase. + +=head2 dirmove() + +Uses dircopy() to copy the directory then removes the original. You can manage the path the original directory is in according to $RemvBase. + +=head2 rmove() + +Like rcopy() but calls fmove() or dirmove() instead. + +=head2 rmove_glob() + +Like rcopy_glob() but calls rmove() instead of rcopy() + +=head3 $RemvBase + +Default is false. When set to true the *move() functions will not only attempt to remove the original file or directory but will remove the given path it is in. + +So if you: + + rmove('foo/bar/baz', '/etc/'); + # "baz" is removed from foo/bar after it is successfully copied to /etc/ + + local $File::Copy::Recursive::Remvbase = 1; + rmove('foo/bar/baz','/etc/'); + # if baz is successfully copied to /etc/ : + # first "baz" is removed from foo/bar + # then "foo/bar is removed via pathrm() + +=head4 $ForcePth + +Default is false. When set to true it calls pathempty() before any directories are removed to empty the directory so it can be rmdir()'ed when $RemvBase is in effect. + +=head2 Creating and Removing Paths + +=head3 $NoFtlPth + +Default is false. If set to true rmdir(), mkdir(), and pathempty() calls in pathrm() and pathmk() do not return() on failure. + +If its set to true they just silently go about their business regardless. This isn't a good idea but its there if you want it. + +=head3 $DirPerms + +Mode to pass to any mkdir() calls. Defaults to 0777 as per umask()'s POD. Explicitly having this allows older perls to be able to use FCR and might add a bit of flexibility for you. + +Any value you set it to should be suitable for oct() + +=head3 Path functions + +These functions exist soley because they were necessary for the move and copy functions to have the features they do and not because they are of themselves the purpose of this module. That being said, here is how they work so you can understand how the copy and move funtions work and use them by themselves if you wish. + +=head4 pathrm() + +Removes a given path recursively. It removes the *entire* path so be carefull!!! + +Returns 2 if the given path is not a directory. + + File::Copy::Recursive::pathrm('foo/bar/baz') or die $!; + # foo no longer exists + +Same as: + + rmdir 'foo/bar/baz' or die $!; + rmdir 'foo/bar' or die $!; + rmdir 'foo' or die $!; + +An optional second argument makes it call pathempty() before any rmdir()'s when set to true. + + File::Copy::Recursive::pathrm('foo/bar/baz', 1) or die $!; + # foo no longer exists + +Same as:PFSCheck + + File::Copy::Recursive::pathempty('foo/bar/baz') or die $!; + rmdir 'foo/bar/baz' or die $!; + File::Copy::Recursive::pathempty('foo/bar/') or die $!; + rmdir 'foo/bar' or die $!; + File::Copy::Recursive::pathempty('foo/') or die $!; + rmdir 'foo' or die $!; + +An optional third argument acts like $File::Copy::Recursive::NoFtlPth, again probably not a good idea. + +=head4 pathempty() + +Recursively removes the given directory's contents so it is empty. returns 2 if argument is not a directory, 1 on successfully emptying the directory. + + File::Copy::Recursive::pathempty($pth) or die $!; + # $pth is now an empty directory + +=head4 pathmk() + +Creates a given path recursively. Creates foo/bar/baz even if foo does not exist. + + File::Copy::Recursive::pathmk('foo/bar/baz') or die $!; + +An optional second argument if true acts just like $File::Copy::Recursive::NoFtlPth, which means you'd never get your die() if something went wrong. Again, probably a *bad* idea. + +=head4 pathrmdir() + +Same as rmdir() but it calls pathempty() first to recursively empty it first since rmdir can not remove a directory with contents. +Just removes the top directory the path given instead of the entire path like pathrm(). Return 2 if given argument does not exist (IE its already gone). Return false if it exists but is not a directory. + +=head2 Preserving Mode + +By default a quiet attempt is made to change the new file or directory to the mode of the old one. +To turn this behavior off set + $File::Copy::Recursive::KeepMode +to false; + +=head2 Managing Depth + +You can set the maximum depth a directory structure is recursed by setting: + $File::Copy::Recursive::MaxDepth +to a whole number greater than 0. + +=head2 SymLinks + +If your system supports symlinks then symlinks will be copied as symlinks instead of as the target file. +Perl's symlink() is used instead of File::Copy's copy() +You can customize this behavior by setting $File::Copy::Recursive::CopyLink to a true or false value. +It is already set to true or false dending on your system's support of symlinks so you can check it with an if statement to see how it will behave: + + if($File::Copy::Recursive::CopyLink) { + print "Symlinks will be preserved\n"; + } else { + print "Symlinks will not be preserved because your system does not support it\n"; + } + +If symlinks are being copied you can set $File::Copy::Recursive::BdTrgWrn to true to make it carp when it copies a link whose target does not exist. Its false by default. + + local $File::Copy::Recursive::BdTrgWrn = 1; + +=head2 Removing existing target file or directory before copying. + +This can be done by setting $File::Copy::Recursive::RMTrgFil or $File::Copy::Recursive::RMTrgDir for file or directory behavior respectively. + +0 = off (This is the default) + +1 = carp() $! if removal fails + +2 = return if removal fails + + local $File::Copy::Recursive::RMTrgFil = 1; + fcopy($orig, $target) or die $!; + # if it fails it does warn() and keeps going + + local $File::Copy::Recursive::RMTrgDir = 2; + dircopy($orig, $target) or die $!; + # if it fails it does your "or die" + +This should be unnecessary most of the time but its there if you need it :) + +=head2 Turning off stat() check + +By default the files or directories are checked to see if they are the same (IE linked, or two paths (absolute/relative or different relative paths) to the same file) by comparing the file's stat() info. +It's a very efficient check that croaks if they are and shouldn't be turned off but if you must for some weird reason just set $File::Copy::Recursive::PFSCheck to a false value. ("PFS" stands for "Physical File System") + +=head2 Emulating cp -rf dir1/ dir2/ + +By default dircopy($dir1,$dir2) will put $dir1's contents right into $dir2 whether $dir2 exists or not. + +You can make dircopy() emulate cp -rf by setting $File::Copy::Recursive::CPRFComp to true. + +NOTE: This only emulates -f in the sense that it does not prompt. It does not remove the target file or directory if it exists. +If you need to do that then use the variables $RMTrgFil and $RMTrgDir described in "Removing existing target file or directory before copying" above. + +That means that if $dir2 exists it puts the contents into $dir2/$dir1 instead of $dir2 just like cp -rf. +If $dir2 does not exist then the contents go into $dir2 like normal (also like cp -rf) + +So assuming 'foo/file': + + dircopy('foo', 'bar') or die $!; + # if bar does not exist the result is bar/file + # if bar does exist the result is bar/file + + $File::Copy::Recursive::CPRFComp = 1; + dircopy('foo', 'bar') or die $!; + # if bar does not exist the result is bar/file + # if bar does exist the result is bar/foo/file + +You can also specify a star for cp -rf glob type behavior: + + dircopy('foo/*', 'bar') or die $!; + # if bar does not exist the result is bar/file + # if bar does exist the result is bar/file + + $File::Copy::Recursive::CPRFComp = 1; + dircopy('foo/*', 'bar') or die $!; + # if bar does not exist the result is bar/file + # if bar does exist the result is bar/file + +NOTE: The '*' is only like cp -rf foo/* and *DOES NOT EXPAND PARTIAL DIRECTORY NAMES LIKE YOUR SHELL DOES* (IE not like cp -rf fo* to copy foo/*) + +=head2 Allowing Copy Loops + +If you want to allow: + + cp -rf . foo/ + +type behavior set $File::Copy::Recursive::CopyLoop to true. + +This is false by default so that a check is done to see if the source directory will contain the target directory and croaks to avoid this problem. + +If you ever find a situation where $CopyLoop = 1 is desirable let me know (IE its a bad bad idea but is there if you want it) + +(Note: On Windows this was necessary since it uses stat() to detemine samedness and stat() is essencially useless for this on Windows. +The test is now simply skipped on Windows but I'd rather have an actual reliable check if anyone in Microsoft land would care to share) + +=head1 SEE ALSO + +L L + +=head1 TO DO + +I am currently working on and reviewing some other modules to use in the new interface so we can lose the horrid globals as well as some other undesirable traits and also more easily make available some long standing requests. + +Tests will be easier to do with the new interface and hence the testing focus will shift to the new interface and aim to be comprehensive. + +The old interface will work, it just won't be brought in until it is used, so it will add no overhead for users of the new interface. + +I'll add this after the latest verision has been out for a while with no new features or issues found :) + +=head1 AUTHOR + +Daniel Muey, L + +=head1 COPYRIGHT AND LICENSE + +Copyright 2004 by Daniel Muey + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/external/buildscripts/perl_lib/Tools.pm b/external/buildscripts/perl_lib/Tools.pm new file mode 100644 index 000000000000..8b2d7a6a7e2f --- /dev/null +++ b/external/buildscripts/perl_lib/Tools.pm @@ -0,0 +1,40 @@ +package Tools; + +use warnings; +use strict; +use File::Basename qw(dirname basename ); +use File::Spec; +use Cwd qw(realpath); +use Carp qw(croak carp); +use File::stat; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(InstallNameTool GitClone); + + +sub InstallNameTool +{ + my ($target,$pathtoburnin) = @_; + print "running otool before:\n"; + system("otool","-L",$target); + print "running install_name_tool\n"; + system("install_name_tool -id $pathtoburnin $target") eq 0 or die("Failed running install_name_tool"); + print "running otool after:\n"; + system("otool","-L",$target); +} + +sub GitClone +{ + my $repo = shift; + my $localFolder = shift; + my $branch = shift; + $branch = defined($branch)?$branch:"master"; + + if (-d $localFolder) { + return; + } + print "running git clone --branch $branch $repo $localFolder\n"; + system("git clone --branch $branch $repo $localFolder") eq 0 or die("git clone $repo $localFolder failed!"); +} + diff --git a/external/buildscripts/stub_classlibs.pl b/external/buildscripts/stub_classlibs.pl new file mode 100644 index 000000000000..8287251b0edf --- /dev/null +++ b/external/buildscripts/stub_classlibs.pl @@ -0,0 +1,41 @@ +use Cwd; +use Cwd 'abs_path'; +use File::Basename; +use File::Path; + +if($^O ne "darwin") +{ + print ">>> The ProfileStubber is only built and run in the class library build on macOS\n"; + exit +} + +my $monoroot = File::Spec->rel2abs(dirname(__FILE__) . "/../.."); +my $monoroot = abs_path($monoroot); +my $extraBuildTools = "$monoroot/external/buildscripts/artifacts/Stevedore/mono-build-tools-extra"; + +my $profileRoot = "tmp/lib/mono"; +my $referenceProfile = "$profileRoot/4.7.1-api"; + +print ">>> Modifying the unityjit profile to match the .NET 4.7.1 API\n"; + +$result = system("mono", + "$extraBuildTools/ProfileStubber.exe", + "--reference-profile=$referenceProfile", + "--stub-profile=$profileRoot/unityjit"); + +if ($result ne 0) +{ + die("Failed to stub the unityjit profile\n"); +} + +print ">>> Modifying the unityaot profile to match the .NET 4.7.1 API\n"; + +$result = system("mono", + "$extraBuildTools/ProfileStubber.exe", + "--reference-profile=$referenceProfile", + "--stub-profile=$profileRoot/unityaot"); + +if ($result ne 0) +{ + die("Failed to stub the unityaot profile\n"); +} \ No newline at end of file diff --git a/external/buildscripts/test-driver b/external/buildscripts/test-driver new file mode 100644 index 000000000000..8a05be09ec4b --- /dev/null +++ b/external/buildscripts/test-driver @@ -0,0 +1,100 @@ +#!/usr/bin/env perl + +my $interpreter = shift; +my $test = shift; +my $disabled_tests = shift; +my $output = $test; +my $stdout = $test.'.stdout'; +my $stderr = $test.'.stderr'; + +my $teamcity = $ENV{UNITY_THISISABUILDMACHINE}; +$output =~ s/\.exe$/.output/; + +$| = 0; +if ($teamcity) { + print "##teamcity[testStarted name='$test']\n"; +} +else { + print "Testing $test... "; +} + +foreach $disabled (split (/ /, $disabled_tests)) { + if ($disabled eq $test) { + print "disabled.\n"; + exit (0); + } +} + +my $res; +my $cpid = fork (); +if (!defined ($cpid)) { + $res = system("$interpreter @ARGV $test 2>$stderr 1>$stdout"); +} elsif ($cpid == 0) { + exec ("$interpreter @ARGV $test 2>$stderr 1>$stdout") || die "Cannot exec: $!"; +} else { + # in the parent, setup the alarm + # test must complete in 30 seconds or it is considered buggy + my $timeout = 30; + alarm ($timeout); + $SIG{ALRM} = sub { + print "failed after $timeout seconds timeout.\n"; + if ($teamcity) { + print "##teamcity[testFailed name='$test' message='failed after $timeout seconds timeout.' details='message and stack trace']\n"; + print "##teamcity[testFinished name='$test']\n"; + } + # process group kill + kill (-9, $cpid); + exit (3); + }; + $res = wait (); + $SIG{ALRM} = sub {}; + $res = $? >> 8; +} + +if ($teamcity && -f $stdout) { + $text = read_file ($stdout); + print "##teamcity[testStdOut name='$test' out='$text']\n"; +} + +if ($teamcity && -f $stderr) { + $text = read_file ($stderr); + print "##teamcity[testStdErr name='$test' out='$text']\n"; +} + +if ($res) { + $failedText = sprintf ("failed $? (%d) signal (%d).", $? >> 8, $? & 127); + if ($teamcity) { + print "##teamcity[testFailed name='$test' message='$failedText' details='message and stack trace']\n"; + print "##teamcity[testFinished name='$test']\n"; + } else { + print "$failedText\n"; + } + + if (($? & 127) == 2) { + exit (2); + } else { + exit (1); + } +} +if (-f $output && (read_file ($output) ne read_file ($stdout))) { + print "failed output.\n"; + exit (1); +} + +if ($teamcity) { + print "##teamcity[testFinished name='$test']\n"; +} +else { + print "pass.\n"; +} +unlink ($stderr); +exit (0); + +sub read_file { + local ($/); + my $out = shift; + open (F, "<$out") || die $!; + $out = ; + close(F); + return $out; +} diff --git a/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventCounter.cs b/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventCounter.cs new file mode 100644 index 000000000000..dcb69ca07712 --- /dev/null +++ b/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventCounter.cs @@ -0,0 +1,506 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Threading; +#if ES_BUILD_PCL + using System.Threading.Tasks; +#endif + +#if MONO +using System.Diagnostics.Private; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// + /// Provides the ability to collect statistics through EventSource + /// + /// See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md + /// for a tutorial guide. + /// + /// See https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs + /// which shows tests, which are also useful in seeing actual use. + /// + public class EventCounter : IDisposable + { + /// + /// Initializes a new instance of the class. + /// EVentCounters live as long as the EventSource that they are attached to unless they are + /// explicitly Disposed. + /// + /// The name. + /// The event source. + public EventCounter(string name, EventSource eventSource) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (eventSource == null) + { + throw new ArgumentNullException(nameof(eventSource)); + } + + InitializeBuffer(); + _name = name; + _group = EventCounterGroup.GetEventCounterGroup(eventSource); + _group.Add(this); + _min = float.PositiveInfinity; + _max = float.NegativeInfinity; + } + + /// + /// Writes 'value' to the stream of values tracked by the counter. This updates the sum and other statistics that will + /// be logged on the next timer interval. + /// + /// The value. + public void WriteMetric(float value) + { + Enqueue(value); + } + + /// + /// Removes the counter from set that the EventSource will report on. After being disposed, this + /// counter will do nothing and its resource will be reclaimed if all references to it are removed. + /// If an EventCounter is not explicitly disposed it will be cleaned up automatically when the + /// EventSource it is attached to dies. + /// + public void Dispose() + { + var group = _group; + if (group != null) + { + group.Remove(this); + _group = null; + } + } + + public override string ToString() + { + return "EventCounter '" + _name + "' Count " + _count + " Mean " + (((double)_sum) / _count).ToString("n3"); + } + #region private implementation + + private readonly string _name; + private EventCounterGroup _group; + + #region Buffer Management + + // Values buffering + private const int BufferedSize = 10; + private const float UnusedBufferSlotValue = float.NegativeInfinity; + private const int UnsetIndex = -1; + private volatile float[] _bufferedValues; + private volatile int _bufferedValuesIndex; + + // arbitrarily we use _bufferfValues as the lock object. + private object MyLock { get { return _bufferedValues; } } + + private void InitializeBuffer() + { + _bufferedValues = new float[BufferedSize]; + for (int i = 0; i < _bufferedValues.Length; i++) + { + _bufferedValues[i] = UnusedBufferSlotValue; + } + } + + private void Enqueue(float value) + { + // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay. + int i = _bufferedValuesIndex; + while (true) + { + float result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue); + i++; + if (_bufferedValues.Length <= i) + { + // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other + // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too. + lock (MyLock) // Lock the counter + Flush(); + i = 0; + } + + if (result == UnusedBufferSlotValue) + { + // CompareExchange succeeded + _bufferedValuesIndex = i; + return; + } + } + } + + private void Flush() + { + Debug.Assert(Monitor.IsEntered(MyLock)); + for (int i = 0; i < _bufferedValues.Length; i++) + { + var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue); + if (value != UnusedBufferSlotValue) + { + OnMetricWritten(value); + } + } + + _bufferedValuesIndex = 0; + } + + #endregion // Buffer Management + + #region Statistics Calculation + + // Statistics + private int _count; + private float _sum; + private float _sumSquared; + private float _min; + private float _max; + + private void OnMetricWritten(float value) + { + Debug.Assert(Monitor.IsEntered(MyLock)); + _sum += value; + _sumSquared += value * value; + if (value > _max) + _max = value; + + if (value < _min) + _min = value; + + _count++; + } + + internal EventCounterPayload GetEventCounterPayload() + { + lock (MyLock) // Lock the counter + { + Flush(); + EventCounterPayload result = new EventCounterPayload(); + result.Name = _name; + result.Count = _count; + if (0 < _count) + { + result.Mean = _sum / _count; + result.StandardDeviation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count); + } + else + { + result.Mean = 0; + result.StandardDeviation = 0; + } + result.Min = _min; + result.Max = _max; + ResetStatistics(); + return result; + } + } + + private void ResetStatistics() + { + Debug.Assert(Monitor.IsEntered(MyLock)); + _count = 0; + _sum = 0; + _sumSquared = 0; + _min = float.PositiveInfinity; + _max = float.NegativeInfinity; + } + + #endregion // Statistics Calculation + + #endregion // private implementation + } + + #region internal supporting classes + + [EventData] + internal class EventCounterPayload : IEnumerable> + { + public string Name { get; set; } + + public float Mean { get; set; } + + public float StandardDeviation { get; set; } + + public int Count { get; set; } + + public float Min { get; set; } + + public float Max { get; set; } + + public float IntervalSec { get; internal set; } + + #region Implementation of the IEnumerable interface + + public IEnumerator> GetEnumerator() + { + return ForEnumeration.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ForEnumeration.GetEnumerator(); + } + + private IEnumerable> ForEnumeration + { + get + { + yield return new KeyValuePair("Name", Name); + yield return new KeyValuePair("Mean", Mean); + yield return new KeyValuePair("StandardDeviation", StandardDeviation); + yield return new KeyValuePair("Count", Count); + yield return new KeyValuePair("Min", Min); + yield return new KeyValuePair("Max", Max); + } + } + + #endregion // Implementation of the IEnumerable interface + } + + internal class EventCounterGroup + { + private readonly EventSource _eventSource; + private readonly List _eventCounters; + + internal EventCounterGroup(EventSource eventSource) + { + _eventSource = eventSource; + _eventCounters = new List(); + RegisterCommandCallback(); + } + + internal void Add(EventCounter eventCounter) + { + lock (this) // Lock the EventCounterGroup + _eventCounters.Add(eventCounter); + } + + internal void Remove(EventCounter eventCounter) + { + lock (this) // Lock the EventCounterGroup + _eventCounters.Remove(eventCounter); + } + + #region EventSource Command Processing + + private void RegisterCommandCallback() + { + // Old destktop runtimes don't have this +#if NO_EVENTCOMMANDEXECUTED_SUPPORT + // We could not build against the API that had the EventCommandExecuted but maybe it is there are runtime. + // use reflection to try to get it. + System.Reflection.MethodInfo method = typeof(EventSource).GetMethod("add_EventCommandExecuted"); + if (method != null) + { + method.Invoke(_eventSource, new object[] { (EventHandler)OnEventSourceCommand }); + } + else + { + string msg = "EventCounterError: Old Runtime that does not support EventSource.EventCommandExecuted. EventCounters not Supported"; + _eventSource.Write(msg); + Debug.WriteLine(msg); + } +#else + _eventSource.EventCommandExecuted += OnEventSourceCommand; +#endif + } + + private void OnEventSourceCommand(object sender, EventCommandEventArgs e) + { + if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update) + { + string valueStr; + float value; + if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value)) + { + // Recursion through EventSource callbacks possible. When we enable the timer + // we synchonously issue a EventSource.Write event, which in turn can call back + // to user code (in an EventListener) while holding this lock. This is dangerous + // because it mean this code might inadvertantly participate in a lock loop. + // The scenario seems very unlikely so we ignore that problem for now. + lock (this) // Lock the EventCounterGroup + { + EnableTimer(value); + } + } + } + } + + #endregion // EventSource Command Processing + + #region Global EventCounterGroup Array management + + // We need eventCounters to 'attach' themselves to a particular EventSource. + // this table provides the mapping from EventSource -> EventCounterGroup + // which represents this 'attached' information. + private static WeakReference[] s_eventCounterGroups; + private static readonly object s_eventCounterGroupsLock = new object(); + + private static void EnsureEventSourceIndexAvailable(int eventSourceIndex) + { + Debug.Assert(Monitor.IsEntered(s_eventCounterGroupsLock)); + if (EventCounterGroup.s_eventCounterGroups == null) + { + EventCounterGroup.s_eventCounterGroups = new WeakReference[eventSourceIndex + 1]; + } + else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length) + { + WeakReference[] newEventCounterGroups = new WeakReference[eventSourceIndex + 1]; + Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length); + EventCounterGroup.s_eventCounterGroups = newEventCounterGroups; + } + } + + internal static EventCounterGroup GetEventCounterGroup(EventSource eventSource) + { + lock (s_eventCounterGroupsLock) + { + int eventSourceIndex = EventListenerHelper.EventSourceIndex(eventSource); + EnsureEventSourceIndexAvailable(eventSourceIndex); + WeakReference weakRef = EventCounterGroup.s_eventCounterGroups[eventSourceIndex]; + EventCounterGroup ret = null; + if (weakRef == null || !weakRef.TryGetTarget(out ret)) + { + ret = new EventCounterGroup(eventSource); + EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = new WeakReference(ret); + } + return ret; + } + } + + #endregion // Global EventCounterGroup Array management + + #region Timer Processing + + private DateTime _timeStampSinceCollectionStarted; + private int _pollingIntervalInMilliseconds; + private Timer _pollingTimer; + + private void DisposeTimer() + { + Debug.Assert(Monitor.IsEntered(this)); + if (_pollingTimer != null) + { + _pollingTimer.Dispose(); + _pollingTimer = null; + } + } + + private void EnableTimer(float pollingIntervalInSeconds) + { + Debug.Assert(Monitor.IsEntered(this)); + if (pollingIntervalInSeconds <= 0) + { + DisposeTimer(); + _pollingIntervalInMilliseconds = 0; + } + else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds) + { + Debug.WriteLine("Polling interval changed at " + DateTime.UtcNow.ToString("mm.ss.ffffff")); + _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000); + DisposeTimer(); + _timeStampSinceCollectionStarted = DateTime.UtcNow; + _pollingTimer = new Timer(OnTimer, null, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds); + } + // Always fire the timer event (so you see everything up to this time). + OnTimer(null); + } + + private void OnTimer(object state) + { + Debug.WriteLine("Timer fired at " + DateTime.UtcNow.ToString("mm.ss.ffffff")); + lock (this) // Lock the EventCounterGroup + { + if (_eventSource.IsEnabled()) + { + DateTime now = DateTime.UtcNow; + TimeSpan elapsed = now - _timeStampSinceCollectionStarted; + + foreach (var eventCounter in _eventCounters) + { + EventCounterPayload payload = eventCounter.GetEventCounterPayload(); + payload.IntervalSec = (float)elapsed.TotalSeconds; + _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new PayloadType(payload)); + } + _timeStampSinceCollectionStarted = now; + } + else + { + DisposeTimer(); + } + } + } + + /// + /// This is the payload that is sent in the with EventSource.Write + /// + [EventData] + class PayloadType + { + public PayloadType(EventCounterPayload payload) { Payload = payload; } + public EventCounterPayload Payload { get; set; } + } + + #region PCL timer hack + +#if ES_BUILD_PCL + internal delegate void TimerCallback(object state); + + internal sealed class Timer : CancellationTokenSource, IDisposable + { + private int _period; + private TimerCallback _callback; + private object _state; + + internal Timer(TimerCallback callback, object state, int dueTime, int period) + { + _callback = callback; + _state = state; + _period = period; + Schedule(dueTime); + } + + private void Schedule(int dueTime) + { + Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); + } + + private void OnTimer(Task t, object s) + { + Schedule(_period); + _callback(_state); + } + + public new void Dispose() { base.Cancel(); } + } +#endif + #endregion // PCL timer hack + + #endregion // Timer Processing + + } + + // This class a work-around because .NET V4.6.2 did not make EventSourceIndex public (it was only protected) + // We make this helper class to get around that protection. We want V4.6.3 to make this public at which + // point this class is no longer needed and can be removed. + internal class EventListenerHelper : EventListener + { + public new static int EventSourceIndex(EventSource eventSource) { return EventListener.EventSourceIndex(eventSource); } + #if MONO + internal + #endif + protected override void OnEventWritten(EventWrittenEventArgs eventData) { } // override abstract methods to keep compiler happy + } + + #endregion // internal supporting classes +} diff --git a/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/unsafenativemethods.cs b/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/unsafenativemethods.cs new file mode 100644 index 000000000000..26b6dfddf65e --- /dev/null +++ b/external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/unsafenativemethods.cs @@ -0,0 +1,235 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +/*============================================================ +** +** Class: UnsafeNativeMethods +** +============================================================*/ +namespace Microsoft.Win32 { + using Microsoft.Win32; + using Microsoft.Win32.SafeHandles; + using System; + using System.Runtime.CompilerServices; + using System.Runtime.ConstrainedExecution; + using System.Runtime.InteropServices; + using System.Runtime.Serialization; + using System.Runtime.Versioning; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Diagnostics.Tracing; + + [System.Security.SecurityCritical] // auto-generated + [SuppressUnmanagedCodeSecurityAttribute()] + internal static class UnsafeNativeMethods + { + [SecurityCritical] + [SuppressUnmanagedCodeSecurityAttribute()] + internal static unsafe class ManifestEtw + { + // + // Constants error coded returned by ETW APIs + // + + // The event size is larger than the allowed maximum (64k - header). + internal const int ERROR_ARITHMETIC_OVERFLOW = 534; + + // Occurs when filled buffers are trying to flush to disk, + // but disk IOs are not happening fast enough. + // This happens when the disk is slow and event traffic is heavy. + // Eventually, there are no more free (empty) buffers and the event is dropped. + internal const int ERROR_NOT_ENOUGH_MEMORY = 8; + + internal const int ERROR_MORE_DATA = 0xEA; + internal const int ERROR_NOT_SUPPORTED = 50; + internal const int ERROR_INVALID_PARAMETER = 0x57; + + // + // ETW Methods + // + + internal const int EVENT_CONTROL_CODE_DISABLE_PROVIDER = 0; + internal const int EVENT_CONTROL_CODE_ENABLE_PROVIDER = 1; + internal const int EVENT_CONTROL_CODE_CAPTURE_STATE = 2; + + // + // Callback + // + [SecurityCritical] + internal unsafe delegate void EtwEnableCallback( + [In] ref Guid sourceId, + [In] int isEnabled, + [In] byte level, + [In] long matchAnyKeywords, + [In] long matchAllKeywords, + [In] EVENT_FILTER_DESCRIPTOR* filterData, + [In] void* callbackContext + ); + + // + // Registration APIs + // + [SecurityCritical] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventRegister", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + internal static extern unsafe uint EventRegister( + [In] ref Guid providerId, + [In]EtwEnableCallback enableCallback, + [In]void* callbackContext, + [In][Out]ref long registrationHandle + ); + + // + [SecurityCritical] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventUnregister", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + internal static extern uint EventUnregister([In] long registrationHandle); + + // + // Writing (Publishing/Logging) APIs + // + // + [SecurityCritical] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventWrite", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + internal static extern unsafe int EventWrite( + [In] long registrationHandle, + [In] ref EventDescriptor eventDescriptor, + [In] int userDataCount, + [In] EventProvider.EventData* userData + ); + + [SecurityCritical] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventWriteString", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + internal static extern unsafe int EventWriteString( + [In] long registrationHandle, + [In] byte level, + [In] long keyword, + [In] string msg + ); + + [StructLayout(LayoutKind.Sequential)] + unsafe internal struct EVENT_FILTER_DESCRIPTOR + { + public long Ptr; + public int Size; + public int Type; + }; + + /// + /// Call the ETW native API EventWriteTransfer and checks for invalid argument error. + /// The implementation of EventWriteTransfer on some older OSes (Windows 2008) does not accept null relatedActivityId. + /// So, for these cases we will retry the call with an empty Guid. + /// + internal static int EventWriteTransferWrapper(long registrationHandle, + ref EventDescriptor eventDescriptor, + Guid* activityId, + Guid* relatedActivityId, + int userDataCount, + EventProvider.EventData* userData) + { + int HResult = EventWriteTransfer(registrationHandle, ref eventDescriptor, activityId, relatedActivityId, userDataCount, userData); + if (HResult == ERROR_INVALID_PARAMETER && relatedActivityId == null) + { + Guid emptyGuid = Guid.Empty; + HResult = EventWriteTransfer(registrationHandle, ref eventDescriptor, activityId, &emptyGuid, userDataCount, userData); + } + + return HResult; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventWriteTransfer", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [SuppressUnmanagedCodeSecurityAttribute] // Don't do security checks + private static extern int EventWriteTransfer( + [In] long registrationHandle, + [In] ref EventDescriptor eventDescriptor, + [In] Guid* activityId, + [In] Guid* relatedActivityId, + [In] int userDataCount, + [In] EventProvider.EventData* userData + ); + + internal enum ActivityControl : uint + { + EVENT_ACTIVITY_CTRL_GET_ID = 1, + EVENT_ACTIVITY_CTRL_SET_ID = 2, + EVENT_ACTIVITY_CTRL_CREATE_ID = 3, + EVENT_ACTIVITY_CTRL_GET_SET_ID = 4, + EVENT_ACTIVITY_CTRL_CREATE_SET_ID = 5 + }; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventActivityIdControl", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [SuppressUnmanagedCodeSecurityAttribute] // Don't do security checks + internal static extern int EventActivityIdControl([In] ActivityControl ControlCode, [In][Out] ref Guid ActivityId); + + internal enum EVENT_INFO_CLASS + { + BinaryTrackInfo, + SetEnableAllKeywords, + SetTraits, + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EventSetInformation", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [SuppressUnmanagedCodeSecurityAttribute] // Don't do security checks + internal static extern int EventSetInformation( + [In] long registrationHandle, + [In] EVENT_INFO_CLASS informationClass, + [In] void* eventInformation, + [In] int informationLength); + + // Support for EnumerateTraceGuidsEx + internal enum TRACE_QUERY_INFO_CLASS + { + TraceGuidQueryList, + TraceGuidQueryInfo, + TraceGuidQueryProcess, + TraceStackTracingInfo, + MaxTraceSetInfoClass + }; + + internal struct TRACE_GUID_INFO + { + public int InstanceCount; + public int Reserved; + }; + + internal struct TRACE_PROVIDER_INSTANCE_INFO + { + public int NextOffset; + public int EnableCount; + public int Pid; + public int Flags; + }; + + internal struct TRACE_ENABLE_INFO + { + public int IsEnabled; + public byte Level; + public byte Reserved1; + public ushort LoggerId; + public int EnableProperty; + public int Reserved2; + public long MatchAnyKeyword; + public long MatchAllKeyword; + }; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")] + [DllImport(Win32Native.ADVAPI32, ExactSpelling = true, EntryPoint = "EnumerateTraceGuidsEx", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + [SuppressUnmanagedCodeSecurityAttribute] // Don't do security checks + internal static extern int EnumerateTraceGuidsEx( + TRACE_QUERY_INFO_CLASS TraceQueryInfoClass, + void* InBuffer, + int InBufferSize, + void* OutBuffer, + int OutBufferSize, + ref int ReturnLength); + + } + } +} diff --git a/external/corefx-bugfix/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs b/external/corefx-bugfix/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs new file mode 100644 index 000000000000..80c9a1734f12 --- /dev/null +++ b/external/corefx-bugfix/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs @@ -0,0 +1,390 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + /// + /// Represents a position within a . + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + internal struct CopyPosition + { + /// + /// Constructs a new . + /// + /// The index of the buffer to select. + /// The index within the buffer to select. + internal CopyPosition(int row, int column) + { + Debug.Assert(row >= 0); + Debug.Assert(column >= 0); + + Row = row; + Column = column; + } + + /// + /// Represents a position at the start of a . + /// + public static CopyPosition Start => default(CopyPosition); + + /// + /// The index of the buffer to select. + /// + internal int Row { get; } + + /// + /// The index within the buffer to select. + /// + internal int Column { get; } + + /// + /// If this position is at the end of the current buffer, returns the position + /// at the start of the next buffer. Otherwise, returns this position. + /// + /// The length of the current buffer. + public CopyPosition Normalize(int endColumn) + { + Debug.Assert(Column <= endColumn); + + return Column == endColumn ? + new CopyPosition(Row + 1, 0) : + this; + } + + /// + /// Gets a string suitable for display in the debugger. + /// + private string DebuggerDisplay => $"[{Row}, {Column}]"; + } + + /// + /// Helper type for building dynamically-sized arrays while minimizing allocations and copying. + /// + /// The element type. + internal struct LargeArrayBuilder + { + private const int StartingCapacity = 4; + private const int ResizeLimit = 8; + + private readonly int _maxCapacity; // The maximum capacity this builder can have. + private T[] _first; // The first buffer we store items in. Resized until ResizeLimit. + private ArrayBuilder _buffers; // After ResizeLimit * 2, we store previous buffers we've filled out here. + private T[] _current; // Current buffer we're reading into. If _count <= ResizeLimit, this is _first. + private int _index; // Index into the current buffer. + private int _count; // Count of all of the items in this builder. + + /// + /// Constructs a new builder. + /// + /// Pass true. + public LargeArrayBuilder(bool initialize) + : this(maxCapacity: int.MaxValue) + { + // This is a workaround for C# not having parameterless struct constructors yet. + // Once it gets them, replace this with a parameterless constructor. + Debug.Assert(initialize); + } + + /// + /// Constructs a new builder with the specified maximum capacity. + /// + /// The maximum capacity this builder can have. + /// + /// Do not add more than items to this builder. + /// + public LargeArrayBuilder(int maxCapacity) + : this() + { + Debug.Assert(maxCapacity >= 0); + + _first = _current = Array.Empty(); + _maxCapacity = maxCapacity; + } + + /// + /// Gets the number of items added to the builder. + /// + public int Count => _count; + + /// + /// Adds an item to this builder. + /// + /// The item to add. + /// + /// Use if adding to the builder is a bottleneck for your use case. + /// Otherwise, use . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + Debug.Assert(_maxCapacity > _count); + + if (_index == _current.Length) + { + AllocateBuffer(); + } + + _current[_index++] = item; + _count++; + } + + /// + /// Adds a range of items to this builder. + /// + /// The sequence to add. + /// + /// It is the caller's responsibility to ensure that adding + /// does not cause the builder to exceed its maximum capacity. + /// + public void AddRange(IEnumerable items) + { + Debug.Assert(items != null); + + using (IEnumerator enumerator = items.GetEnumerator()) + { + T[] destination = _current; + int index = _index; + + // Continuously read in items from the enumerator, updating _count + // and _index when we run out of space. + + while (enumerator.MoveNext()) + { + if (index == destination.Length) + { + // No more space in this buffer. Resize. + _count += index - _index; + _index = index; + AllocateBuffer(); + destination = _current; + index = _index; // May have been reset to 0 + } + + destination[index++] = enumerator.Current; + } + + // Final update to _count and _index. + _count += index - _index; + _index = index; + } + } + + /// + /// Copies the contents of this builder to the specified array. + /// + /// The destination array. + /// The index in to start copying to. + /// The number of items to copy. + public void CopyTo(T[] array, int arrayIndex, int count) + { + Debug.Assert(arrayIndex >= 0); + Debug.Assert(count >= 0 && count <= Count); + Debug.Assert(array?.Length - arrayIndex >= count); + + for (int i = 0; count > 0; i++) + { + // Find the buffer we're copying from. + T[] buffer = GetBuffer(index: i); + + // Copy until we satisfy count, or we reach the end of the buffer. + int toCopy = Math.Min(count, buffer.Length); + Array.Copy(buffer, 0, array, arrayIndex, toCopy); + + // Increment variables to that position. + count -= toCopy; + arrayIndex += toCopy; + } + } + + /// + /// Copies the contents of this builder to the specified array. + /// + /// The position in this builder to start copying from. + /// The destination array. + /// The index in to start copying to. + /// The number of items to copy. + /// The position in this builder that was copied up to. + public CopyPosition CopyTo(CopyPosition position, T[] array, int arrayIndex, int count) + { + Debug.Assert(array != null); + Debug.Assert(arrayIndex >= 0); + Debug.Assert(count > 0 && count <= Count); + Debug.Assert(array.Length - arrayIndex >= count); + + // Go through each buffer, which contains one 'row' of items. + // The index in each buffer is referred to as the 'column'. + + /* + * Visual representation: + * + * C0 C1 C2 .. C31 .. C63 + * R0: [0] [1] [2] .. [31] + * R1: [32] [33] [34] .. [63] + * R2: [64] [65] [66] .. [95] .. [127] + */ + + int row = position.Row; + int column = position.Column; + + T[] buffer = GetBuffer(row); + int copied = +#if __MonoCS__ + CopyToCore(buffer, column, array, arrayIndex, count); +#else + CopyToCore(buffer, column); +#endif + + if (count == 0) + { + return new CopyPosition(row, column + copied).Normalize(buffer.Length); + } + + do + { + buffer = GetBuffer(++row); + copied = +#if __MonoCS__ + CopyToCore(buffer, 0, array, arrayIndex, count); +#else + CopyToCore(buffer, 0); +#endif + } while (count > 0); + + return new CopyPosition(row, copied).Normalize(buffer.Length); + +#if __MonoCS__ + } + + static int CopyToCore(T[] sourceBuffer, int sourceIndex, T[] array, int arrayIndex, int count) +#else + int CopyToCore(T[] sourceBuffer, int sourceIndex) +#endif + { + Debug.Assert(sourceBuffer.Length > sourceIndex); + + // Copy until we satisfy `count` or reach the end of the current buffer. + int copyCount = Math.Min(sourceBuffer.Length - sourceIndex, count); + Array.Copy(sourceBuffer, sourceIndex, array, arrayIndex, copyCount); + + arrayIndex += copyCount; + count -= copyCount; + + return copyCount; + } + +#if !__MonoCS__ + } +#endif + + /// + /// Retrieves the buffer at the specified index. + /// + /// The index of the buffer. + public T[] GetBuffer(int index) + { + Debug.Assert(index >= 0 && index < _buffers.Count + 2); + + return index == 0 ? _first : + index <= _buffers.Count ? _buffers[index - 1] : + _current; + } + + /// + /// Adds an item to this builder. + /// + /// The item to add. + /// + /// Use if adding to the builder is a bottleneck for your use case. + /// Otherwise, use . + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public void SlowAdd(T item) => Add(item); + + /// + /// Creates an array from the contents of this builder. + /// + public T[] ToArray() + { + if (TryMove(out T[] array)) + { + // No resizing to do. + return array; + } + + array = new T[_count]; + CopyTo(array, 0, _count); + return array; + } + + /// + /// Attempts to transfer this builder into an array without copying. + /// + /// The transferred array, if the operation succeeded. + /// true if the operation succeeded; otherwise, false. + public bool TryMove(out T[] array) + { + array = _first; + return _count == _first.Length; + } + + private void AllocateBuffer() + { + // - On the first few adds, simply resize _first. + // - When we pass ResizeLimit, allocate ResizeLimit elements for _current + // and start reading into _current. Set _index to 0. + // - When _current runs out of space, add it to _buffers and repeat the + // above step, except with _current.Length * 2. + // - Make sure we never pass _maxCapacity in all of the above steps. + + Debug.Assert((uint)_maxCapacity > (uint)_count); + Debug.Assert(_index == _current.Length, $"{nameof(AllocateBuffer)} was called, but there's more space."); + + // If _count is int.MinValue, we want to go down the other path which will raise an exception. + if ((uint)_count < (uint)ResizeLimit) + { + // We haven't passed ResizeLimit. Resize _first, copying over the previous items. + Debug.Assert(_current == _first && _count == _first.Length); + + int nextCapacity = Math.Min(_count == 0 ? StartingCapacity : _count * 2, _maxCapacity); + + _current = new T[nextCapacity]; + Array.Copy(_first, 0, _current, 0, _count); + _first = _current; + } + else + { + Debug.Assert(_maxCapacity > ResizeLimit); + Debug.Assert(_count == ResizeLimit ^ _current != _first); + + int nextCapacity; + if (_count == ResizeLimit) + { + nextCapacity = ResizeLimit; + } + else + { + // Example scenario: Let's say _count == 64. + // Then our buffers look like this: | 8 | 8 | 16 | 32 | + // As you can see, our count will be just double the last buffer. + // Now, say _maxCapacity is 100. We will find the right amount to allocate by + // doing min(64, 100 - 64). The lhs represents double the last buffer, + // the rhs the limit minus the amount we've already allocated. + + Debug.Assert(_count >= ResizeLimit * 2); + Debug.Assert(_count == _current.Length * 2); + + _buffers.Add(_current); + nextCapacity = Math.Min(_count, _maxCapacity - _count); + } + + _current = new T[nextCapacity]; + _index = 0; + } + } + } +} diff --git a/external/corefx-bugfix/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs b/external/corefx-bugfix/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs new file mode 100644 index 000000000000..b727fb50cb77 --- /dev/null +++ b/external/corefx-bugfix/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs @@ -0,0 +1,1067 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace System.Collections.Concurrent +{ + /// + /// Represents a thread-safe, unordered collection of objects. + /// + /// Specifies the type of elements in the bag. + /// + /// + /// Bags are useful for storing objects when ordering doesn't matter, and unlike sets, bags support + /// duplicates. is a thread-safe bag implementation, optimized for + /// scenarios where the same thread will be both producing and consuming data stored in the bag. + /// + /// + /// accepts null reference (Nothing in Visual Basic) as a valid + /// value for reference types. + /// + /// + /// All public and protected members of are thread-safe and may be used + /// concurrently from multiple threads. + /// + /// + [DebuggerTypeProxy(typeof(IProducerConsumerCollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public class ConcurrentBag : IProducerConsumerCollection, IReadOnlyCollection + { + /// The per-bag, per-thread work-stealing queues. + private ThreadLocal _locals; + /// The head work stealing queue in a linked list of queues. + private volatile WorkStealingQueue _workStealingQueues; + + /// Initializes a new instance of the class. + public ConcurrentBag() + { + _locals = new ThreadLocal(); + } + + /// + /// Initializes a new instance of the + /// class that contains elements copied from the specified collection. + /// + /// The collection whose elements are copied to the new . + /// is a null reference + /// (Nothing in Visual Basic). + public ConcurrentBag(IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection), SR.ConcurrentBag_Ctor_ArgumentNullException); + } + + _locals = new ThreadLocal(); + + WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: true); + foreach (T item in collection) + { + queue.LocalPush(item); + } + } + + /// + /// Adds an object to the . + /// + /// The object to be added to the + /// . The value can be a null reference + /// (Nothing in Visual Basic) for reference types. + public void Add(T item) => GetCurrentThreadWorkStealingQueue(forceCreate: true).LocalPush(item); + + /// + /// Attempts to add an object to the . + /// + /// The object to be added to the + /// . The value can be a null reference + /// (Nothing in Visual Basic) for reference types. + /// Always returns true + bool IProducerConsumerCollection.TryAdd(T item) + { + Add(item); + return true; + } + + /// + /// Attempts to remove and return an object from the . + /// + /// When this method returns, contains the object + /// removed from the or the default value + /// of if the operation failed. + /// true if an object was removed successfully; otherwise, false. + public bool TryTake(out T result) + { + WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: false); + return (queue != null && queue.TryLocalPop(out result)) || TrySteal(out result, take: true); + } + + /// + /// Attempts to return an object from the without removing it. + /// + /// When this method returns, contains an object from + /// the or the default value of + /// if the operation failed. + /// true if and object was returned successfully; otherwise, false. + public bool TryPeek(out T result) + { + WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: false); + return (queue != null && queue.TryLocalPeek(out result)) || TrySteal(out result, take: false); + } + + /// Gets the work-stealing queue data structure for the current thread. + /// Whether to create a new queue if this thread doesn't have one. + /// The local queue object, or null if the thread doesn't have one. + private WorkStealingQueue GetCurrentThreadWorkStealingQueue(bool forceCreate) => + _locals.Value ?? + (forceCreate ? CreateWorkStealingQueueForCurrentThread() : null); + + private WorkStealingQueue CreateWorkStealingQueueForCurrentThread() + { + lock (GlobalQueuesLock) // necessary to update _workStealingQueues, so as to synchronize with freezing operations + { + WorkStealingQueue head = _workStealingQueues; + + WorkStealingQueue queue = head != null ? GetUnownedWorkStealingQueue() : null; + if (queue == null) + { + _workStealingQueues = queue = new WorkStealingQueue(head); + } + _locals.Value = queue; + + return queue; + } + } + + /// + /// Try to reuse an unowned queue. If a thread interacts with the bag and then exits, + /// the bag purposefully retains its queue, as it contains data associated with the bag. + /// + /// The queue object, or null if no unowned queue could be gathered. + private WorkStealingQueue GetUnownedWorkStealingQueue() + { + Debug.Assert(Monitor.IsEntered(GlobalQueuesLock)); + + // Look for a thread that has the same ID as this one. It won't have come from the same thread, + // but if our thread ID is reused, we know that no other thread can have the same ID and thus + // no other thread can be using this queue. + int currentThreadId = Environment.CurrentManagedThreadId; + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + if (queue._ownerThreadId == currentThreadId) + { + return queue; + } + } + + return null; + } + + /// Local helper method to steal an item from any other non empty thread. + /// To receive the item retrieved from the bag + /// Whether to remove or peek. + /// True if succeeded, false otherwise. + private bool TrySteal(out T result, bool take) + { + if (CDSCollectionETWBCLProvider.Log.IsEnabled()) + { + if (take) + { + CDSCollectionETWBCLProvider.Log.ConcurrentBag_TryTakeSteals(); + } + else + { + CDSCollectionETWBCLProvider.Log.ConcurrentBag_TryPeekSteals(); + } + } + + // If there's no local queue for this thread, just start from the head queue + // and try to steal from each queue until we get a result. + WorkStealingQueue localQueue = GetCurrentThreadWorkStealingQueue(forceCreate: false); + if (localQueue == null) + { + return TryStealFromTo(_workStealingQueues, null, out result, take); + } + + // If there is a local queue from this thread, then start from the next queue + // after it, and then iterate around back from the head to this queue, not including it. + return + TryStealFromTo(localQueue._nextQueue, null, out result, take) || + TryStealFromTo(_workStealingQueues, localQueue, out result, take); + + // TODO: Investigate storing the queues in an array instead of a linked list, and then + // randomly choosing a starting location from which to start iterating. + } + + /// + /// Attempts to steal from each queue starting from to . + /// + private bool TryStealFromTo(WorkStealingQueue startInclusive, WorkStealingQueue endExclusive, out T result, bool take) + { + for (WorkStealingQueue queue = startInclusive; queue != endExclusive; queue = queue._nextQueue) + { + if (queue.TrySteal(out result, take)) + { + return true; + } + } + + result = default(T); + return false; + } + + /// + /// Copies the elements to an existing + /// one-dimensional Array, starting at the specified array + /// index. + /// + /// The one-dimensional Array that is the + /// destination of the elements copied from the + /// . The Array must have zero-based indexing. + /// The zero-based index in at which copying + /// begins. + /// is a null reference (Nothing in + /// Visual Basic). + /// is less than + /// zero. + /// is equal to or greater than the + /// length of the + /// -or- the number of elements in the source is greater than the available space from + /// to the end of the destination . + public void CopyTo(T[] array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ConcurrentBag_CopyTo_ArgumentNullException); + } + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.Collection_CopyTo_ArgumentOutOfRangeException); + } + + // Short path if the bag is empty + if (_workStealingQueues == null) + { + return; + } + + bool lockTaken = false; + try + { + FreezeBag(ref lockTaken); + + // Make sure we won't go out of bounds on the array + int count = DangerousCount; + if (index > array.Length - count) + { + throw new ArgumentException(SR.Collection_CopyTo_TooManyElems, nameof(index)); + } + + // Do the copy + try + { + int copied = CopyFromEachQueueToArray(array, index); + Debug.Assert(copied == count); + } + catch (ArrayTypeMismatchException e) + { + // Propagate same exception as in desktop + throw new InvalidCastException(e.Message, e); + } + } + finally + { + UnfreezeBag(lockTaken); + } + } + + /// Copies from each queue to the target array, starting at the specified index. + private int CopyFromEachQueueToArray(T[] array, int index) + { + Debug.Assert(Monitor.IsEntered(GlobalQueuesLock)); + + int i = index; + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + i += queue.DangerousCopyTo(array, i); + } + return i - index; + } + + /// + /// Copies the elements of the to an , starting at a particular + /// index. + /// + /// The one-dimensional Array that is the + /// destination of the elements copied from the + /// . The Array must have zero-based indexing. + /// The zero-based index in at which copying + /// begins. + /// is a null reference (Nothing in + /// Visual Basic). + /// is less than + /// zero. + /// + /// is multidimensional. -or- + /// does not have zero-based indexing. -or- + /// is equal to or greater than the length of the + /// -or- The number of elements in the source is + /// greater than the available space from to the end of the destination + /// . -or- The type of the source cannot be cast automatically to the type of the + /// destination . + /// + void ICollection.CopyTo(Array array, int index) + { + // If the destination is actually a T[], use the strongly-typed + // overload that doesn't allocate/copy an extra array. + T[] szArray = array as T[]; + if (szArray != null) + { + CopyTo(szArray, index); + return; + } + + // Otherwise, fall back to first storing the contents to an array, + // and then relying on its CopyTo to copy to the target Array. + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ConcurrentBag_CopyTo_ArgumentNullException); + } + ToArray().CopyTo(array, index); + } + + /// + /// Copies the elements to a new array. + /// + /// A new array containing a snapshot of elements copied from the . + public T[] ToArray() + { + if (_workStealingQueues != null) + { + bool lockTaken = false; + try + { + FreezeBag(ref lockTaken); + + int count = DangerousCount; + if (count > 0) + { + var arr = new T[count]; + int copied = CopyFromEachQueueToArray(arr, 0); + Debug.Assert(copied == count); + return arr; + } + } + finally + { + UnfreezeBag(lockTaken); + } + } + + // Bag was empty + return Array.Empty(); + } + + /// + /// Removes all values from the . + /// + public void Clear() + { + // If there are no queues in the bag, there's nothing to clear. + if (_workStealingQueues == null) + { + return; + } + + // Clear the local queue. + WorkStealingQueue local = GetCurrentThreadWorkStealingQueue(forceCreate: false); + if (local != null) + { + local.LocalClear(); + if (local._nextQueue == null && local == _workStealingQueues) + { + // If it's the only queue, nothing more to do. + return; + } + } + + // Clear the other queues by stealing all remaining items. We freeze the bag to + // avoid having to contend with too many new items being added while we're trying + // to drain the bag. But we can't just freeze the bag and attempt to remove all + // items from every other queue, as even with freezing the bag it's dangerous to + // manipulate other queues' tail pointers and add/take counts. + bool lockTaken = false; + try + { + FreezeBag(ref lockTaken); + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + T ignored; + while (queue.TrySteal(out ignored, take: true)); + } + } + finally + { + UnfreezeBag(lockTaken); + } + } + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator for the contents of the . + /// + /// The enumeration represents a moment-in-time snapshot of the contents + /// of the bag. It does not reflect any updates to the collection after + /// was called. The enumerator is safe to use + /// concurrently with reads from and writes to the bag. + /// + public IEnumerator GetEnumerator() => new Enumerator(ToArray()); + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator for the contents of the . + /// + /// The items enumerated represent a moment-in-time snapshot of the contents + /// of the bag. It does not reflect any update to the collection after + /// was called. + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Gets the number of elements contained in the . + /// + /// The number of elements contained in the . + /// + /// The count returned represents a moment-in-time snapshot of the contents + /// of the bag. It does not reflect any updates to the collection after + /// was called. + /// + public int Count + { + get + { + // Short path if the bag is empty + if (_workStealingQueues == null) + { + return 0; + } + + bool lockTaken = false; + try + { + FreezeBag(ref lockTaken); + return DangerousCount; + } + finally + { + UnfreezeBag(lockTaken); + } + } + } + + /// Gets the number of items stored in the bag. + /// Only provides a stable result when the bag is frozen. + private int DangerousCount + { + get + { + int count = 0; + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + checked { count += queue.DangerousCount; } + } + + Debug.Assert(count >= 0); + return count; + } + } + + /// + /// Gets a value that indicates whether the is empty. + /// + /// true if the is empty; otherwise, false. + public bool IsEmpty + { + get + { + // Fast-path based on the current thread's local queue. + WorkStealingQueue local = GetCurrentThreadWorkStealingQueue(forceCreate: false); + if (local != null) + { + // We don't need the lock to check the local queue, as no other thread + // could be adding to it, and a concurrent steal that transitions from + // non-empty to empty doesn't matter because if we see this as non-empty, + // then that's a valid moment-in-time answer, and if we see this as empty, + // we check other things. + if (!local.IsEmpty) + { + return false; + } + + // We know the local queue is empty (no one besides this thread could have + // added to it since we checked). If the local queue is the only one + // in the bag, then the bag is empty, too. + if (local._nextQueue == null && local == _workStealingQueues) + { + return true; + } + } + + // Couldn't take a fast path. Freeze the bag, and enumerate the queues to see if + // any is non-empty. + bool lockTaken = false; + try + { + FreezeBag(ref lockTaken); + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + if (!queue.IsEmpty) + { + return false; + } + } + } + finally + { + UnfreezeBag(lockTaken); + } + + // All queues were empty, so the bag was empty. + return true; + } + } + + /// + /// Gets a value indicating whether access to the is + /// synchronized with the SyncRoot. + /// + /// true if access to the is synchronized + /// with the SyncRoot; otherwise, false. For , this property always + /// returns false. + bool ICollection.IsSynchronized => false; + + /// + /// Gets an object that can be used to synchronize access to the . This property is not supported. + /// + /// The SyncRoot property is not supported. + object ICollection.SyncRoot + { + get { throw new NotSupportedException(SR.ConcurrentCollection_SyncRoot_NotSupported); } + } + + /// Global lock used to synchronize the queues pointer and all bag-wide operations (e.g. ToArray, Count, etc.). + private object GlobalQueuesLock + { + get + { + Debug.Assert(_locals != null); + return _locals; + } + } + + /// "Freezes" the bag, such that no concurrent operations will be mutating the bag when it returns. + /// true if the global lock was taken; otherwise, false. + private void FreezeBag(ref bool lockTaken) + { + // Take the global lock to start freezing the bag. This helps, for example, + // to prevent other threads from joining the bag (adding their local queues) + // while a global operation is in progress. + Debug.Assert(!Monitor.IsEntered(GlobalQueuesLock)); + Monitor.Enter(GlobalQueuesLock, ref lockTaken); + WorkStealingQueue head = _workStealingQueues; // stable at least until GlobalQueuesLock is released in UnfreezeBag + + // Then acquire all local queue locks, noting on each that it's been taken. + for (WorkStealingQueue queue = head; queue != null; queue = queue._nextQueue) + { + Monitor.Enter(queue, ref queue._frozen); + } + Interlocked.MemoryBarrier(); // prevent reads of _currentOp from moving before writes to _frozen + + // Finally, wait for all unsynchronized operations on each queue to be done. + for (WorkStealingQueue queue = head; queue != null; queue = queue._nextQueue) + { + if (queue._currentOp != (int)Operation.None) + { + var spinner = new SpinWait(); + do { spinner.SpinOnce(); } + while (queue._currentOp != (int)Operation.None); + } + } + } + + /// "Unfreezes" a bag frozen with . + /// The result of the method. + private void UnfreezeBag(bool lockTaken) + { + Debug.Assert(Monitor.IsEntered(GlobalQueuesLock) == lockTaken); + if (lockTaken) + { + // Release all of the individual queue locks. + for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue) + { + if (queue._frozen) + { + queue._frozen = false; + Monitor.Exit(queue); + } + } + + // Then release the global lock. + Monitor.Exit(GlobalQueuesLock); + } + } + + /// Provides a work-stealing queue data structure stored per thread. + private sealed class WorkStealingQueue + { + /// Initial size of the queue's array. + private const int InitialSize = 32; + /// Starting index for the head and tail indices. + private const int StartIndex = +#if DEBUG + int.MaxValue; // in debug builds, start at the end so we exercise the index reset logic +#else + 0; +#endif + /// Head index from which to steal. This and'd with the is the index into . + private volatile int _headIndex = StartIndex; + /// Tail index at which local pushes/pops happen. This and'd with the is the index into . + private volatile int _tailIndex = StartIndex; + /// The array storing the queue's data. + private volatile T[] _array = new T[InitialSize]; + /// Mask and'd with and to get an index into . + private volatile int _mask = InitialSize - 1; + /// Numbers of elements in the queue from the local perspective; needs to be combined with to get an actual Count. + private int _addTakeCount; + /// Number of steals; needs to be combined with to get an actual Count. + private int _stealCount; + /// The current queue operation. Used to quiesce before performing operations from one thread onto another. + internal volatile int _currentOp; + /// true if this queue's lock is held as part of a global freeze. + internal bool _frozen; + /// Next queue in the 's set of thread-local queues. + internal readonly WorkStealingQueue _nextQueue; + /// Thread ID that owns this queue. + internal readonly int _ownerThreadId; + + /// Initialize the WorkStealingQueue. + /// The next queue in the linked list of work-stealing queues. + internal WorkStealingQueue(WorkStealingQueue nextQueue) + { + _ownerThreadId = Environment.CurrentManagedThreadId; + _nextQueue = nextQueue; + } + + /// Gets whether the queue is empty. + internal bool IsEmpty + { + get + { + /// _tailIndex can be decremented even while the bag is frozen, as the decrement in TryLocalPop happens prior + /// to the check for _frozen. But that's ok, as if _tailIndex is being decremented such that _headIndex becomes + /// >= _tailIndex, then the queue is about to be empty. This does mean, though, that while holding the lock, + /// it is possible to observe Count == 1 but IsEmpty == true. As such, we simply need to avoid doing any operation + /// while the bag is frozen that requires those values to be consistent. + return _headIndex >= _tailIndex; + } + } + + /// + /// Add new item to the tail of the queue. + /// + /// The item to add. + internal void LocalPush(T item) + { + Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId); + bool lockTaken = false; + try + { + // Full fence to ensure subsequent reads don't get reordered before this + Interlocked.Exchange(ref _currentOp, (int)Operation.Add); + int tail = _tailIndex; + + // Rare corner case (at most once every 2 billion pushes on this thread): + // We're going to increment the tail; if we'll overflow, then we need to reset our counts + if (tail == int.MaxValue) + { + _currentOp = (int)Operation.None; // set back to None temporarily to avoid a deadlock + lock (this) + { + Debug.Assert(_tailIndex == int.MaxValue, "No other thread should be changing _tailIndex"); + + // Rather than resetting to zero, we'll just mask off the bits we don't care about. + // This way we don't need to rearrange the items already in the queue; they'll be found + // correctly exactly where they are. One subtlety here is that we need to make sure that + // if head is currently < tail, it remains that way. This happens to just fall out from + // the bit-masking, because we only do this if tail == int.MaxValue, meaning that all + // bits are set, so all of the bits we're keeping will also be set. Thus it's impossible + // for the head to end up > than the tail, since you can't set any more bits than all of them. + _headIndex = _headIndex & _mask; + _tailIndex = tail = _tailIndex & _mask; + Debug.Assert(_headIndex <= _tailIndex); + + _currentOp = (int)Operation.Add; + } + } + + // We'd like to take the fast path that doesn't require locking, if possible. It's not possible if another + // thread is currently requesting that the whole bag synchronize, e.g. a ToArray operation. It's also + // not possible if there are fewer than two spaces available. One space is necessary for obvious reasons: + // to store the element we're trying to push. The other is necessary due to synchronization with steals. + // A stealing thread first increments _headIndex to reserve the slot at its old value, and then tries to + // read from that slot. We could potentially have a race condition whereby _headIndex is incremented just + // before this check, in which case we could overwrite the element being stolen as that slot would appear + // to be empty. Thus, we only allow the fast path if there are two empty slots. + if (!_frozen && tail < (_headIndex + _mask)) + { + _array[tail & _mask] = item; + _tailIndex = tail + 1; + } + else + { + // We need to contend with foreign operations (e.g. steals, enumeration, etc.), so we lock. + _currentOp = (int)Operation.None; // set back to None to avoid a deadlock + Monitor.Enter(this, ref lockTaken); + + int head = _headIndex; + int count = _tailIndex - _headIndex; + + // If we're full, expand the array. + if (count >= _mask) + { + // Expand the queue by doubling its size. + var newArray = new T[_array.Length << 1]; + int headIdx = head & _mask; + if (headIdx == 0) + { + Array.Copy(_array, 0, newArray, 0, _array.Length); + } + else + { + Array.Copy(_array, headIdx, newArray, 0, _array.Length - headIdx); + Array.Copy(_array, 0, newArray, _array.Length - headIdx, headIdx); + } + + // Reset the field values + _array = newArray; + _headIndex = 0; + _tailIndex = tail = count; + _mask = (_mask << 1) | 1; + } + + // Add the element + _array[tail & _mask] = item; + _tailIndex = tail + 1; + + // Update the count to avoid overflow. We can trust _stealCount here, + // as we're inside the lock and it's only manipulated there. + _addTakeCount -= _stealCount; + _stealCount = 0; + } + + // Increment the count from the add/take perspective + checked { _addTakeCount++; } + } + finally + { + _currentOp = (int)Operation.None; + if (lockTaken) + { + Monitor.Exit(this); + } + } + } + + /// Clears the contents of the local queue. + internal void LocalClear() + { + Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId); + lock (this) // synchronize with steals + { + // If the queue isn't empty, reset the state to clear out all items. + if (_headIndex < _tailIndex) + { + _headIndex = _tailIndex = StartIndex; + _addTakeCount = _stealCount = 0; + Array.Clear(_array, 0, _array.Length); + } + } + } + + /// Remove an item from the tail of the queue. + /// The removed item + internal bool TryLocalPop(out T result) + { + Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId); + + int tail = _tailIndex; + if (_headIndex >= tail) + { + result = default(T); + return false; + } + + bool lockTaken = false; + try + { + // Decrement the tail using a full fence to ensure subsequent reads don't reorder before this. + // If the read of _headIndex moved before this write to _tailIndex, we could erroneously end up + // popping an element that's concurrently being stolen, leading to the same element being + // dequeued from the bag twice. + _currentOp = (int)Operation.Take; + Interlocked.Exchange(ref _tailIndex, --tail); + + // If there is no interaction with a steal, we can head down the fast path. + // Note that we use _headIndex < tail rather than _headIndex <= tail to account + // for stealing peeks, which don't increment _headIndex, and which could observe + // the written default(T) in a race condition to peek at the element. + if (!_frozen && _headIndex < tail) + { + int idx = tail & _mask; + result = _array[idx]; + _array[idx] = default(T); + _addTakeCount--; + return true; + } + else + { + // Interaction with steals: 0 or 1 elements left. + _currentOp = (int)Operation.None; // set back to None to avoid a deadlock + Monitor.Enter(this, ref lockTaken); + if (_headIndex <= tail) + { + // Element still available. Take it. + int idx = tail & _mask; + result = _array[idx]; + _array[idx] = default(T); + _addTakeCount--; + return true; + } + else + { + // We encountered a race condition and the element was stolen, restore the tail. + _tailIndex = tail + 1; + result = default(T); + return false; + } + } + } + finally + { + _currentOp = (int)Operation.None; + if (lockTaken) + { + Monitor.Exit(this); + } + } + } + + /// Peek an item from the tail of the queue. + /// the peeked item + /// True if succeeded, false otherwise + internal bool TryLocalPeek(out T result) + { + Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId); + + int tail = _tailIndex; + if (_headIndex < tail) + { + // It is possible to enable lock-free peeks, following the same general approach + // that's used in TryLocalPop. However, peeks are more complicated as we can't + // do the same kind of index reservation that's done in TryLocalPop; doing so could + // end up making a steal think that no item is available, even when one is. To do + // it correctly, then, we'd need to add spinning to TrySteal in case of a concurrent + // peek happening. With a lock, the common case (no contention with steals) will + // effectively only incur two interlocked operations (entering/exiting the lock) instead + // of one (setting Peek as the _currentOp). Combined with Peeks on a bag being rare, + // for now we'll use the simpler/safer code. + lock (this) + { + if (_headIndex < tail) + { + result = _array[(tail - 1) & _mask]; + return true; + } + } + } + + result = default(T); + return false; + } + + /// Steal an item from the head of the queue. + /// the removed item + /// true to take the item; false to simply peek at it + internal bool TrySteal(out T result, bool take) + { + // Fast-path check to see if the queue is empty. + if (_headIndex < _tailIndex) + { + // Anything other than empty requires synchronization. + lock (this) + { + int head = _headIndex; + if (take) + { + // Increment head to tentatively take an element: a full fence is used to ensure the read + // of _tailIndex doesn't move earlier, as otherwise we could potentially end up stealing + // the same element that's being popped locally. + Interlocked.Exchange(ref _headIndex, unchecked(head + 1)); + + // If there's an element to steal, do it. + if (head < _tailIndex) + { + int idx = head & _mask; + result = _array[idx]; + _array[idx] = default(T); + _stealCount++; + return true; + } + else + { + // We contended with the local thread and lost the race, so restore the head. + _headIndex = head; + } + } + else if (head < _tailIndex) + { + // Peek, if there's an element available + result = _array[head & _mask]; + return true; + } + } + } + + // The queue was empty. + result = default(T); + return false; + } + + /// Copies the contents of this queue to the target array starting at the specified index. + internal int DangerousCopyTo(T[] array, int arrayIndex) + { + Debug.Assert(Monitor.IsEntered(this)); + Debug.Assert(_frozen); + Debug.Assert(array != null); + Debug.Assert(arrayIndex >= 0 && arrayIndex <= array.Length); + + int headIndex = _headIndex; + int count = DangerousCount; + Debug.Assert( + count == (_tailIndex - _headIndex) || + count == (_tailIndex + 1 - _headIndex), + "Count should be the same as tail - head, but allowing for the possibility that " + + "a peek decremented _tailIndex before seeing that a freeze was happening."); + Debug.Assert(arrayIndex <= array.Length - count); + + // Copy from this queue's array to the destination array, but in reverse + // order to match the ordering of desktop. + for (int i = arrayIndex + count - 1; i >= arrayIndex; i--) + { + array[i] = _array[headIndex++ & _mask]; + } + + return count; + } + + /// Gets the total number of items in the queue. + /// + /// This is not thread safe, only providing an accurate result either from the owning + /// thread while its lock is held or from any thread while the bag is frozen. + /// + internal int DangerousCount + { + get + { + Debug.Assert(Monitor.IsEntered(this)); + int count = _addTakeCount - _stealCount; + Debug.Assert(count >= 0); + return count; + } + } + } + + /// Lock-free operations performed on a queue. + internal enum Operation + { + None, + Add, + Take + }; + + /// Provides an enumerator for the bag. + /// + /// The original implementation of ConcurrentBag used a as part of + /// the GetEnumerator implementation. That list was then changed to be an array, but array's + /// GetEnumerator has different behavior than does list's, in particular for the case where + /// Current is used after MoveNext returns false. To avoid any concerns around compatibility, + /// we use a custom enumerator rather than just returning array's. This enumerator provides + /// the essential elements of both list's and array's enumerators. + /// + [Serializable] + private sealed class Enumerator : IEnumerator + { + private readonly T[] _array; + private T _current; + private int _index; + + public Enumerator(T[] array) + { + Debug.Assert(array != null); + _array = array; + } + + public bool MoveNext() + { + if (_index < _array.Length) + { + _current = _array[_index++]; + return true; + } + + _index = _array.Length + 1; + return false; + } + + public T Current => _current; + + object IEnumerator.Current + { + get + { + if (_index == 0 || _index == _array.Length + 1) + { + throw new InvalidOperationException(SR.ConcurrentBag_Enumerator_EnumerationNotStartedOrAlreadyFinished); + } + return Current; + } + } + + public void Reset() + { + _index = 0; + _current = default(T); + } + + public void Dispose() { } + } + } +} \ No newline at end of file diff --git a/libgc/gcj_mlc.c b/libgc/gcj_mlc.c index 8b1da826c7a8..24d3aa9b6eae 100644 --- a/libgc/gcj_mlc.c +++ b/libgc/gcj_mlc.c @@ -50,6 +50,8 @@ int GC_gcj_debug_kind; /* The kind of objects that is always marked */ ptr_t * GC_gcjobjfreelist; ptr_t * GC_gcjdebugobjfreelist; +void GC_start_debugging(); + /* Caller does not hold allocation lock. */ void GC_init_gcj_malloc(int mp_index, void * /* really GC_mark_proc */mp) { diff --git a/mcs/Makefile b/mcs/Makefile index 59db5e77267f..87e5ace2c96a 100644 --- a/mcs/Makefile +++ b/mcs/Makefile @@ -23,6 +23,8 @@ xammac_net_4_5_SUBDIRS := build class xbuild_12_SUBDIRS := build class tools/xbuild xbuild_14_SUBDIRS := build class tools/xbuild winaot_SUBDIRS := build class +unityjit_SUBDIRS := build class +unityaot_SUBDIRS := build class orbis_SUBDIRS := build class unreal_SUBDIRS := build class wasm_SUBDIRS := build class @@ -80,6 +82,8 @@ $(_boot_:%=profile-do--xammac_net_4_5--%): profile-do--xammac_net_4_5--%: $(_boot_:%=profile-do--testing_aot_hybrid--%): profile-do--testing_aot_hybrid--%: profile-do--build--% $(_boot_:%=profile-do--testing_aot_full--%): profile-do--testing_aot_full--%: profile-do--build--% $(_boot_:%=profile-do--winaot--%): profile-do--winaot--%: profile-do--build--% +$(_boot_:%=profile-do--unityjit--%): profile-do--unityjit--%: profile-do--build--% +$(_boot_:%=profile-do--unityaot--%): profile-do--unityaot--%: profile-do--build--% $(_boot_:%=profile-do--orbis--%): profile-do--orbis--%: profile-do--build--% $(_boot_:%=profile-do--unreal--%): profile-do--unreal--%: profile-do--build--% $(_boot_:%=profile-do--wasm--%): profile-do--wasm--%: profile-do--build--% diff --git a/mcs/build/common/Consts.cs.in b/mcs/build/common/Consts.cs.in index 8bb64bc43a70..a6cdfdd19611 100644 --- a/mcs/build/common/Consts.cs.in +++ b/mcs/build/common/Consts.cs.in @@ -40,7 +40,7 @@ static class Consts public const string MonoCopyright = "(c) Various Mono authors"; public const int MonoCorlibVersion = @MONO_CORLIB_VERSION@; -#if MOBILE +#if MOBILE && !UNITY_AOT // Versions of .NET Framework for Silverlight 4.0 public const string FxVersion = "2.0.5.0"; public const string VsVersion = "9.0.0.0"; // unused, but needed for compilation @@ -78,7 +78,7 @@ static class Consts #error No profile symbols defined. #endif -#if MOBILE +#if MOBILE && !UNITY_AOT const string PublicKeyToken = "7cec85d7bea7798e"; #else const string PublicKeyToken = "b77a5c561934e089"; diff --git a/mcs/build/profiles/net_4_x.make b/mcs/build/profiles/net_4_x.make index 5463812acc6e..a7cc2e81dc1f 100644 --- a/mcs/build/profiles/net_4_x.make +++ b/mcs/build/profiles/net_4_x.make @@ -13,7 +13,7 @@ profile-check: @: DEFAULT_REFERENCES = -r:$(topdir)/class/lib/$(PROFILE_DIRECTORY)/mscorlib.dll -PROFILE_MCS_FLAGS = -d:NET_4_0 -d:NET_4_5 -d:NET_4_6 -d:MONO -d:WIN_PLATFORM -d:MULTIPLEX_OS -nowarn:1699 -nostdlib $(DEFAULT_REFERENCES) $(PLATFORM_DEBUG_FLAGS) +PROFILE_MCS_FLAGS = -d:NET_4_0 -d:NET_4_5 -d:NET_4_6 -d:MONO -d:UNITY_AOT -d:UNITY -d:WIN_PLATFORM -d:MULTIPLEX_OS -nowarn:1699 -nostdlib $(DEFAULT_REFERENCES) $(PLATFORM_DEBUG_FLAGS) API_BIN_PROFILE = v4.7.1 FRAMEWORK_VERSION = 4.5 diff --git a/mcs/build/profiles/unityaot.make b/mcs/build/profiles/unityaot.make new file mode 100644 index 000000000000..9b69620953c1 --- /dev/null +++ b/mcs/build/profiles/unityaot.make @@ -0,0 +1,41 @@ +#! -*- makefile -*- + +BOOTSTRAP_PROFILE = build + +BOOTSTRAP_MCS = MONO_PATH="$(topdir)/class/lib/$(BOOTSTRAP_PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(INTERNAL_CSC) +MCS = $(BOOTSTRAP_MCS) + +profile-check: + @: + +DEFAULT_REFERENCES = -r:$(topdir)/class/lib/$(PROFILE)/mscorlib.dll + +PROFILE_MCS_FLAGS = \ + -d:NET_1_1 \ + -d:NET_2_0 \ + -d:NET_2_1 \ + -d:NET_3_5 \ + -d:NET_4_0 \ + -d:NET_4_5 \ + -d:MONO \ + -d:UNITY \ + -d:UNITY_AOT \ + -d:MOBILE,MOBILE_LEGACY \ + -d:FULL_AOT_DESKTOP \ + -d:FULL_AOT_RUNTIME \ + -d:DISABLE_REMOTING \ + -d:WIN_PLATFORM \ + -nowarn:1699 \ + -nostdlib \ + $(DEFAULT_REFERENCES) \ + $(PLATFORM_DEBUG_FLAGS) +API_BIN_PROFILE = v4.7.1 + +FRAMEWORK_VERSION = 2.1 + +NO_INSTALL = yes +AOT_FRIENDLY_PROFILE = yes +#ALWAYS_AOT = yes +MOBILE_PROFILE = yes +NO_TEST = yes +#NO_SIGN_ASSEMBLY = yes diff --git a/mcs/build/profiles/unityjit.make b/mcs/build/profiles/unityjit.make new file mode 100644 index 000000000000..b943ecf40f99 --- /dev/null +++ b/mcs/build/profiles/unityjit.make @@ -0,0 +1,18 @@ +# -*- makefile -*- + +BOOTSTRAP_PROFILE = build + +BOOTSTRAP_MCS = MONO_PATH="$(topdir)/class/lib/$(BOOTSTRAP_PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(INTERNAL_CSC) +MCS = $(BOOTSTRAP_MCS) + + +profile-check: + @: + +DEFAULT_REFERENCES = -r:$(topdir)/class/lib/$(PROFILE)/mscorlib.dll +PROFILE_MCS_FLAGS = -d:NET_4_0 -d:NET_4_5 -d:NET_4_6 -d:MONO -d:UNITY_JIT -d:UNITY -d:WIN_PLATFORM -nowarn:1699 -nostdlib $(DEFAULT_REFERENCES) $(PLATFORM_DEBUG_FLAGS) +API_BIN_PROFILE = v4.7.1 + +FRAMEWORK_VERSION = 4.5 +XBUILD_VERSION = 4.0 +MONO_FEATURE_APPLETLS=1 diff --git a/mcs/class/Facades/System.Drawing.Primitives/Makefile b/mcs/class/Facades/System.Drawing.Primitives/Makefile index 036ec852fa5e..174df4af8ee8 100644 --- a/mcs/class/Facades/System.Drawing.Primitives/Makefile +++ b/mcs/class/Facades/System.Drawing.Primitives/Makefile @@ -23,7 +23,7 @@ EMBEDDED_DRAWING_DEP := $(filter xammac_net_4_5 testing_aot_full testing_aot_hyb ifndef EMBEDDED_DRAWING_DEP # profiles which build a System.Drawing.dll in the repo -REPO_DRAWING_DEP := $(filter net_4_x orbis winaot unreal wasm, $(PROFILE)) +REPO_DRAWING_DEP := $(filter net_4_x orbis winaot unityaot unityjit unreal wasm, $(PROFILE)) ifdef REPO_DRAWING_DEP LIB_REFS += System.Drawing diff --git a/mcs/class/Facades/System.Net.Http.Rtc/AssemblyInfo.cs b/mcs/class/Facades/System.Net.Http.Rtc/AssemblyInfo.cs new file mode 100644 index 000000000000..19cd3907a866 --- /dev/null +++ b/mcs/class/Facades/System.Net.Http.Rtc/AssemblyInfo.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle ("System.Net.Http.Rtc")] +[assembly: AssemblyDescription ("System.Net.Http.Rtc")] +[assembly: AssemblyDefaultAlias ("System.Net.Http.Rtc")] +[assembly: AssemblyCompany ("Mono development team")] +[assembly: AssemblyProduct ("Mono Common Language Infrastructure")] +[assembly: AssemblyCopyright ("(c) Various Mono authors")] +[assembly: AssemblyVersion ("4.0.0.0")] +[assembly: AssemblyInformationalVersion ("4.0.0.0")] +[assembly: AssemblyFileVersion ("4.0.0.0")] diff --git a/mcs/class/Facades/System.Net.Http.Rtc/Makefile b/mcs/class/Facades/System.Net.Http.Rtc/Makefile new file mode 100644 index 000000000000..f730763a925e --- /dev/null +++ b/mcs/class/Facades/System.Net.Http.Rtc/Makefile @@ -0,0 +1,21 @@ +MCS_BUILD_DIR = ../../../build + +thisdir = class/Facades/System.Net.Http.Rtc +SUBDIRS = +include $(MCS_BUILD_DIR)/rules.make + +LIBRARY_SUBDIR = Facades +LIBRARY_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION)/Facades + +LIBRARY = System.Net.Http.Rtc.dll + +KEYFILE = ../../msfinal.pub +SIGN_FLAGS = /delaysign /nowarn:1616,1699 +LIB_REFS = System +LIB_MCS_FLAGS = $(SIGN_FLAGS) + +PLATFORM_DEBUG_FLAGS = + +NO_TEST = yes + +include $(MCS_BUILD_DIR)/library.make diff --git a/mcs/class/Facades/System.Net.Http.Rtc/System.Net.Http.Rtc.dll.sources b/mcs/class/Facades/System.Net.Http.Rtc/System.Net.Http.Rtc.dll.sources new file mode 100644 index 000000000000..719628dc7c61 --- /dev/null +++ b/mcs/class/Facades/System.Net.Http.Rtc/System.Net.Http.Rtc.dll.sources @@ -0,0 +1,2 @@ +TypeForwarders.cs +AssemblyInfo.cs diff --git a/mcs/class/System/System.Net/IWebConnectionState.cs b/mcs/class/Facades/System.Net.Http.Rtc/TypeForwarders.cs similarity index 71% rename from mcs/class/System/System.Net/IWebConnectionState.cs rename to mcs/class/Facades/System.Net.Http.Rtc/TypeForwarders.cs index 7b032decbb7b..b8c87d21f333 100644 --- a/mcs/class/System/System.Net/IWebConnectionState.cs +++ b/mcs/class/Facades/System.Net.Http.Rtc/TypeForwarders.cs @@ -1,10 +1,5 @@ // -// IWebConnectionState.cs -// -// Author: -// Martin Baulig -// -// Copyright (c) 2014 Xamarin Inc. (http://www.xamarin.com) +// Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,30 +18,8 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System; -using System.Threading; - -namespace System.Net -{ - interface IWebConnectionState { - WebConnectionGroup Group { - get; - } - - ServicePoint ServicePoint { - get; - } - - bool Busy { - get; - } - - DateTime IdleSince { - get; - } - - bool TrySetBusy (); +// - void SetIdle (); - } -} +#if false // it is forwarded to System.Net.Http.WebRequest.dll but doesn't actually exist there in the .NET 4.7.1 reference assemblies ... +[assembly:System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Net.Http.RtcRequestFactory))] +#endif diff --git a/mcs/class/Facades/System.Resources.Reader/AssemblyInfo.cs b/mcs/class/Facades/System.Resources.Reader/AssemblyInfo.cs new file mode 100644 index 000000000000..fe58b174b957 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Reader/AssemblyInfo.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle ("System.Resources.Reader")] +[assembly: AssemblyDescription ("System.Resources.Reader")] +[assembly: AssemblyDefaultAlias ("System.Resources.Reader")] +[assembly: AssemblyCompany ("Mono development team")] +[assembly: AssemblyProduct ("Mono Common Language Infrastructure")] +[assembly: AssemblyCopyright ("(c) Various Mono authors")] +[assembly: AssemblyVersion ("4.0.2.0")] +[assembly: AssemblyInformationalVersion ("4.0.0.0")] +[assembly: AssemblyFileVersion ("4.0.0.0")] diff --git a/mcs/class/Facades/System.Resources.Reader/Makefile b/mcs/class/Facades/System.Resources.Reader/Makefile new file mode 100644 index 000000000000..b16ae43ee303 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Reader/Makefile @@ -0,0 +1,23 @@ +MCS_BUILD_DIR = ../../../build + +thisdir = class/Facades/System.Resources.Reader +SUBDIRS = +include $(MCS_BUILD_DIR)/rules.make + +LIBRARY_SUBDIR = Facades +LIBRARY_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION)/Facades + +LIBRARY = System.Resources.Reader.dll + +KEYFILE = ../../msfinal.pub +SIGN_FLAGS = /delaysign /nowarn:1616,1699 +LIB_REFS = System +LIB_MCS_FLAGS = $(SIGN_FLAGS) + +PLATFORM_DEBUG_FLAGS = + +NO_TEST = yes + +include $(MCS_BUILD_DIR)/library.make + + diff --git a/mcs/class/Facades/System.Resources.Reader/System.Resources.Reader.dll.sources b/mcs/class/Facades/System.Resources.Reader/System.Resources.Reader.dll.sources new file mode 100644 index 000000000000..8e33d4ddeae9 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Reader/System.Resources.Reader.dll.sources @@ -0,0 +1,3 @@ +TypeForwarders.cs +AssemblyInfo.cs + diff --git a/mcs/class/Facades/System.Resources.Reader/TypeForwarders.cs b/mcs/class/Facades/System.Resources.Reader/TypeForwarders.cs new file mode 100644 index 000000000000..310362270c75 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Reader/TypeForwarders.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Resources.ResourceReader))] + + diff --git a/mcs/class/Facades/System.Resources.Writer/AssemblyInfo.cs b/mcs/class/Facades/System.Resources.Writer/AssemblyInfo.cs new file mode 100644 index 000000000000..71222db9e31e --- /dev/null +++ b/mcs/class/Facades/System.Resources.Writer/AssemblyInfo.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle ("System.Resources.Writer")] +[assembly: AssemblyDescription ("System.Resources.Writer")] +[assembly: AssemblyDefaultAlias ("System.Resources.Writer")] +[assembly: AssemblyCompany ("Mono development team")] +[assembly: AssemblyProduct ("Mono Common Language Infrastructure")] +[assembly: AssemblyCopyright ("(c) Various Mono authors")] +[assembly: AssemblyVersion ("4.0.2.0")] +[assembly: AssemblyInformationalVersion ("4.0.0.0")] +[assembly: AssemblyFileVersion ("4.0.0.0")] diff --git a/mcs/class/Facades/System.Resources.Writer/Makefile b/mcs/class/Facades/System.Resources.Writer/Makefile new file mode 100644 index 000000000000..f2f622eb1bf0 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Writer/Makefile @@ -0,0 +1,23 @@ +MCS_BUILD_DIR = ../../../build + +thisdir = class/Facades/System.Resources.Writer +SUBDIRS = +include $(MCS_BUILD_DIR)/rules.make + +LIBRARY_SUBDIR = Facades +LIBRARY_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION)/Facades + +LIBRARY = System.Resources.Writer.dll + +KEYFILE = ../../msfinal.pub +SIGN_FLAGS = /delaysign /nowarn:1616,1699 +LIB_REFS = System +LIB_MCS_FLAGS = $(SIGN_FLAGS) + +PLATFORM_DEBUG_FLAGS = + +NO_TEST = yes + +include $(MCS_BUILD_DIR)/library.make + + diff --git a/mcs/class/Facades/System.Resources.Writer/System.Resources.Writer.dll.sources b/mcs/class/Facades/System.Resources.Writer/System.Resources.Writer.dll.sources new file mode 100644 index 000000000000..8e33d4ddeae9 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Writer/System.Resources.Writer.dll.sources @@ -0,0 +1,3 @@ +TypeForwarders.cs +AssemblyInfo.cs + diff --git a/mcs/class/Facades/System.Resources.Writer/TypeForwarders.cs b/mcs/class/Facades/System.Resources.Writer/TypeForwarders.cs new file mode 100644 index 000000000000..e82cdb0ad509 --- /dev/null +++ b/mcs/class/Facades/System.Resources.Writer/TypeForwarders.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Resources.ResourceWriter))] + + diff --git a/mcs/class/Facades/System.Runtime.Serialization.Primitives/System.Runtime.Serialization.Primitives.dll.sources b/mcs/class/Facades/System.Runtime.Serialization.Primitives/System.Runtime.Serialization.Primitives.dll.sources index fd4c99b18ad3..719628dc7c61 100644 --- a/mcs/class/Facades/System.Runtime.Serialization.Primitives/System.Runtime.Serialization.Primitives.dll.sources +++ b/mcs/class/Facades/System.Runtime.Serialization.Primitives/System.Runtime.Serialization.Primitives.dll.sources @@ -1,4 +1,2 @@ TypeForwarders.cs AssemblyInfo.cs - -../../../../external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs diff --git a/mcs/class/Facades/System.Runtime.Serialization.Primitives/TypeForwarders.cs b/mcs/class/Facades/System.Runtime.Serialization.Primitives/TypeForwarders.cs index ea71c80449cb..8768234c08da 100644 --- a/mcs/class/Facades/System.Runtime.Serialization.Primitives/TypeForwarders.cs +++ b/mcs/class/Facades/System.Runtime.Serialization.Primitives/TypeForwarders.cs @@ -30,6 +30,7 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.IExtensibleDataObject))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.IgnoreDataMemberAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.InvalidDataContractException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.ISerializationSurrogateProvider))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.KnownTypeAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.OnDeserializedAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.Serialization.OnDeserializingAttribute))] diff --git a/mcs/class/Facades/System.Runtime.Serialization.Xml/DataContractSerializerExtensions.cs b/mcs/class/Facades/System.Runtime.Serialization.Xml/DataContractSerializerExtensions.cs index 3ccdff7a3463..aef7a2114746 100644 --- a/mcs/class/Facades/System.Runtime.Serialization.Xml/DataContractSerializerExtensions.cs +++ b/mcs/class/Facades/System.Runtime.Serialization.Xml/DataContractSerializerExtensions.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if !NO_CODEDOM +#if !NO_CODEDOM || UNITY_AOT using System.CodeDom; #endif using System.Collections.ObjectModel; @@ -73,7 +73,7 @@ public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, obj throw NotImplemented.ByDesign; } -#if !NO_CODEDOM +#if !NO_CODEDOM || UNITY_AOT public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) { throw NotImplemented.ByDesign; diff --git a/mcs/class/Facades/System.Security.Cryptography.Algorithms/System.Security.Cryptography.Algorithms.dll.sources b/mcs/class/Facades/System.Security.Cryptography.Algorithms/System.Security.Cryptography.Algorithms.dll.sources index bc50224ae2c5..a2f4cd1a9d72 100644 --- a/mcs/class/Facades/System.Security.Cryptography.Algorithms/System.Security.Cryptography.Algorithms.dll.sources +++ b/mcs/class/Facades/System.Security.Cryptography.Algorithms/System.Security.Cryptography.Algorithms.dll.sources @@ -1,4 +1,3 @@ TypeForwarders.cs AssemblyInfo.cs SR.cs -../../../../external/corefx/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.net46.cs diff --git a/mcs/class/Facades/System.Security.Cryptography.Algorithms/TypeForwarders.cs b/mcs/class/Facades/System.Security.Cryptography.Algorithms/TypeForwarders.cs index 2a7c09f47141..10ded3bedd96 100644 --- a/mcs/class/Facades/System.Security.Cryptography.Algorithms/TypeForwarders.cs +++ b/mcs/class/Facades/System.Security.Cryptography.Algorithms/TypeForwarders.cs @@ -43,6 +43,7 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.HMACSHA256))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.HMACSHA384))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.HMACSHA512))] +[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.IncrementalHash))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.MaskGenerationMethod))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.MD5))] [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.Cryptography.PKCS1MaskGenerationMethod))] diff --git a/mcs/class/Facades/System.Security.SecureString/System.Security.SecureString.dll.sources b/mcs/class/Facades/System.Security.SecureString/System.Security.SecureString.dll.sources index d12a84b6abd2..719628dc7c61 100644 --- a/mcs/class/Facades/System.Security.SecureString/System.Security.SecureString.dll.sources +++ b/mcs/class/Facades/System.Security.SecureString/System.Security.SecureString.dll.sources @@ -1,3 +1,2 @@ TypeForwarders.cs AssemblyInfo.cs -../../../../external/corefx/src/System.Runtime.InteropServices/src/System/Security/SecureStringMarshal.cs diff --git a/mcs/class/Facades/System.Security.SecureString/TypeForwarders.cs b/mcs/class/Facades/System.Security.SecureString/TypeForwarders.cs index 4267f40e03b0..e34303ce1947 100644 --- a/mcs/class/Facades/System.Security.SecureString/TypeForwarders.cs +++ b/mcs/class/Facades/System.Security.SecureString/TypeForwarders.cs @@ -21,3 +21,4 @@ // [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.SecureString))] +[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Security.SecureStringMarshal))] diff --git a/mcs/class/Facades/netstandard/Makefile b/mcs/class/Facades/netstandard/Makefile index b9974695d6d1..e6487dc44d16 100644 --- a/mcs/class/Facades/netstandard/Makefile +++ b/mcs/class/Facades/netstandard/Makefile @@ -23,13 +23,15 @@ LIB_MCS_FLAGS = $(SIGN_FLAGS) $(EXTRA_LIB_MCS_FLAGS) ifeq ($(PROFILE),xammac_net_4_5) LIB_REFS += System.Web.Services else ifeq (2.1, $(FRAMEWORK_VERSION)) +ifneq ($(PROFILE),unityaot) LIB_REFS += System.Web.Services +endif else LIB_REFS += System.Web endif # profiles which build a System.Drawing.dll in the repo -REPO_DRAWING_DEP := $(filter net_4_x orbis winaot unreal wasm, $(PROFILE)) +REPO_DRAWING_DEP := $(filter net_4_x orbis winaot unityaot unityjit unreal wasm, $(PROFILE)) ifdef REPO_DRAWING_DEP LIB_REFS += System.Drawing diff --git a/mcs/class/Facades/subdirs.make b/mcs/class/Facades/subdirs.make index 992516931274..3901ea16eeb2 100644 --- a/mcs/class/Facades/subdirs.make +++ b/mcs/class/Facades/subdirs.make @@ -27,7 +27,8 @@ System.Xml.XPath System.Xml.XmlDocument System.Xml.Xsl.Primitives Microsoft.Win3 System.IO.FileSystem.AccessControl System.Reflection.TypeExtensions System.Reflection.Emit.Lightweight System.Reflection.Emit.ILGeneration System.Reflection.Emit \ System.Threading.AccessControl System.ValueTuple \ System.Security.Cryptography.Primitives System.Text.Encoding.CodePages System.IO.FileSystem.Watcher \ -System.Security.Cryptography.ProtectedData System.ServiceProcess.ServiceController System.IO.Pipes +System.Security.Cryptography.ProtectedData System.ServiceProcess.ServiceController System.IO.Pipes \ +System.Net.Http.Rtc System.Net.Ping System.Resources.Reader System.Resources.Writer System.Runtime.Serialization.Formatters System.Security.Cryptography.Csp # common_SUBDIRS dependencies common_DEPS_SUBDIRS = System.Security.Cryptography.X509Certificates System.ServiceModel.Primitives System.Runtime.Serialization.Primitives \ @@ -80,6 +81,12 @@ monotouch_tv_PARALLEL_SUBDIRS = $(monotouch_PARALLEL_SUBDIRS) winaot_SUBDIRS = $(common_DEPS_SUBDIRS) $(netstandard_drawing_SUBDIRS) $(mobile_only_DEPS_SUBDIRS) winaot_PARALLEL_SUBDIRS = $(common_SUBDIRS) $(mobile_only_SUBDIRS) +unityjit_SUBDIRS = $(common_DEPS_SUBDIRS) $(netstandard_drawing_SUBDIRS) +unityjit_PARALLEL_SUBDIRS = $(common_SUBDIRS) + +unityaot_SUBDIRS = $(filter-out System.ServiceModel.Primitives,$(common_DEPS_SUBDIRS)) $(netstandard_drawing_SUBDIRS) $(mobile_only_DEPS_SUBDIRS) +unityaot_PARALLEL_SUBDIRS = $(filter-out System.ServiceModel.Http System.ServiceModel.Security System.ServiceModel.Duplex System.ServiceModel.NetTcp ,$(common_SUBDIRS)) $(mobile_only_SUBDIRS) + orbis_SUBDIRS = $(common_DEPS_SUBDIRS) $(netstandard_drawing_SUBDIRS) $(mobile_only_DEPS_SUBDIRS) orbis_PARALLEL_SUBDIRS = $(common_SUBDIRS) $(mobile_only_SUBDIRS) @@ -89,7 +96,7 @@ unreal_PARALLEL_SUBDIRS = $(common_SUBDIRS) $(mobile_only_SUBDIRS) wasm_SUBDIRS = $(common_DEPS_SUBDIRS) $(netstandard_drawing_SUBDIRS) $(mobile_only_DEPS_SUBDIRS) wasm_PARALLEL_SUBDIRS = $(common_SUBDIRS) $(mobile_only_SUBDIRS) -mobile_only_SUBDIRS = System.Net.Ping System.Runtime.Serialization.Formatters System.Security.Cryptography.Csp System.Security.Cryptography.Pkcs \ +mobile_only_SUBDIRS = System.Security.Cryptography.Pkcs \ System.Security.Cryptography.Cng System.Runtime.Loader System.Xml.XPath.XmlDocument System.Reflection.DispatchProxy mobile_only_DEPS_SUBDIRS = System.Security.Cryptography.OpenSsl diff --git a/mcs/class/Makefile b/mcs/class/Makefile index 13d7aae28738..03fc35fe3beb 100644 --- a/mcs/class/Makefile +++ b/mcs/class/Makefile @@ -342,6 +342,29 @@ net_4_x_parallel_dirs := \ legacy/Mono.Cecil \ $(pcl_facade_dirs) +unityjit_dirs := \ + $(net_4_x_dirs) \ + System.ComponentModel.Composition.4.5 \ + System.Data.DataSetExtensions \ + I18N \ + $(pcl_facade_dirs) + +unityaot_dirs := \ + $(filter-out \ + Mono.CSharp \ + System.Data.Services.Client \ + System.Net.Http.WinHttpHandler \ + System.ServiceModel.Web \ + System.ServiceModel \ + System.Web.Services \ + System.Reflection.Context \ + System.Windows \ + System.Xml.Serialization \ + System.Runtime.CompilerServices.Unsafe, $(mobile_common_dirs)) \ + System.Drawing \ + System.Data.DataSetExtensions \ + $(pcl_facade_dirs) + xbuild_2_0_dirs := \ Microsoft.Build.Framework \ Microsoft.Build.Utilities \ @@ -376,13 +399,15 @@ net_4_x_PARALLEL_SUBDIRS := $(net_4_x_parallel_dirs) xbuild_12_SUBDIRS := $(xbuild_4_0_dirs) xbuild_14_SUBDIRS := $(xbuild_4_0_dirs) Microsoft.NuGet.Build.Tasks winaot_SUBDIRS := $(winaot_dirs) +unityjit_SUBDIRS := $(unityjit_dirs) +unityaot_SUBDIRS := $(unityaot_dirs) orbis_SUBDIRS := $(orbis_dirs) unreal_SUBDIRS := $(unreal_dirs) wasm_SUBDIRS := $(wasm_dirs) include ../build/rules.make -SUBDIRS = $(testing_aot_full_dirs) $(testing_aot_hybrid_dirs) $(monotouch_dirs) $(monodroid_dirs) $(monodroid_tools_dirs) $(xammac_dirs) $(net_4_x_dirs) $(net_4_x_parallel_dirs) $(xammac_net_4_5_SUBDIRS) $(unreal_dirs) +SUBDIRS = $(testing_aot_full_dirs) $(testing_aot_hybrid_dirs) $(monotouch_dirs) $(monodroid_dirs) $(monodroid_tools_dirs) $(xammac_dirs) $(net_4_x_dirs) $(net_4_x_parallel_dirs) $(xammac_net_4_5_SUBDIRS) $(unityjit_dirs) $(unityaot_dirs) $(unreal_dirs) DIST_ONLY_SUBDIRS = dlr aot-compiler reference-assemblies $(xbuild_4_0_dirs) Microsoft.NuGet.Build.Tasks diff --git a/mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs b/mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs index 602c827091de..e7c43aca1a29 100644 --- a/mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs +++ b/mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs @@ -393,6 +393,9 @@ public class ErrorHandlerEventArgs : EventArgs { public ErrorCode ErrorCode { get; set; } + public string ErrorMessage { + get; set; + } } /* @@ -420,7 +423,7 @@ public abstract class Connection * with newer runtimes, and vice versa. */ internal const int MAJOR_VERSION = 2; - internal const int MINOR_VERSION = 45; + internal const int MINOR_VERSION = 46; enum WPSuspendPolicy { NONE = 0, @@ -442,7 +445,8 @@ enum CommandSet { TYPE = 23, MODULE = 24, FIELD = 25, - EVENT = 64 + EVENT = 64, + POINTER = 65 } enum EventKind { @@ -574,7 +578,8 @@ enum CmdType { GET_INTERFACES = 16, GET_INTERFACE_MAP = 17, IS_INITIALIZED = 18, - CREATE_INSTANCE = 19 + CREATE_INSTANCE = 19, + GET_VALUE_SIZE = 20 } enum CmdField { @@ -606,6 +611,10 @@ enum CmdStringRef { GET_CHARS = 3 } + enum CmdPointer { + GET_VALUE = 1 + } + enum CmdObjectRef { GET_TYPE = 1, GET_VALUES = 2, @@ -730,10 +739,12 @@ internal static byte[] EncodePacket (int id, int commandSet, int command, byte[] } class PacketReader { + Connection connection; byte[] packet; int offset; - public PacketReader (byte[] packet) { + public PacketReader (Connection connection, byte[] packet) { + this.connection = connection; this.packet = packet; // For event packets @@ -743,10 +754,12 @@ public PacketReader (byte[] packet) { // For reply packets offset = 0; - ReadInt (); // length + var len = ReadInt (); // length ReadInt (); // id ReadByte (); // flags ErrorCode = ReadShort (); + if (ErrorCode == (int)Mono.Debugger.Soft.ErrorCode.INVALID_ARGUMENT && len > offset) + ErrorMsg = ReadString (); } public CommandSet CommandSet { @@ -761,6 +774,10 @@ public int ErrorCode { get; set; } + public string ErrorMsg { + get; internal set; + } + public int Offset { get { return offset; @@ -845,9 +862,16 @@ public ValueImpl ReadValue () { return new ValueImpl { Type = etype, Value = ReadDouble () }; case ElementType.I: case ElementType.U: - case ElementType.Ptr: // FIXME: The client and the debuggee might have different word sizes return new ValueImpl { Type = etype, Value = ReadLong () }; + case ElementType.Ptr: + long value = ReadLong (); + if (connection.Version.AtLeast (2, 46)) { + long pointerClass = ReadId (); + return new ValueImpl { Type = etype, Klass = pointerClass, Value = value }; + } else { + return new ValueImpl { Type = etype, Value = value }; + } case ElementType.String: case ElementType.SzArray: case ElementType.Class: @@ -1283,7 +1307,7 @@ bool ReceivePacket () { if (cb != null) cb.Invoke (id, packet); } else { - PacketReader r = new PacketReader (packet); + PacketReader r = new PacketReader (this, packet); if (r.CommandSet == CommandSet.EVENT && r.Command == (int)CmdEvent.COMPOSITE) { int spolicy = r.ReadByte (); @@ -1502,7 +1526,7 @@ int Send (CommandSet command_set, int command, PacketWriter packet, Action parent_vtypes) { - if (v.Value != null) + if (v.Value != null) { + if (Version.AtLeast (2, 46) && v.Type == ElementType.Ptr) + return new PointerValue(this, GetType(v.Klass), (long)v.Value); return new PrimitiveValue (this, v.Value); + } switch (v.Type) { case ElementType.Void: @@ -682,8 +685,11 @@ internal ValueImpl EncodeValue (Value v, List duplicates = null) { duplicates.Add (v); return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields, duplicates) }; + } else if (v is PointerValue) { + PointerValue val = (PointerValue)v; + return new ValueImpl { Type = ElementType.Ptr, Klass = val.Type.Id, Value = val.Address }; } else { - throw new NotSupportedException (); + throw new NotSupportedException ("Value of type " + v.GetType()); } } @@ -786,13 +792,18 @@ public void VMDisconnect (int req_id, long thread_id, string vm_uri) { public class CommandException : Exception { - internal CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") { + internal CommandException (ErrorCode error_code, string error_message) : base ("Debuggee returned error code " + error_code + (error_message == null || error_message.Length == 0 ? "." : " - " + error_message + ".")) { ErrorCode = error_code; + ErrorMessage = error_message; } public ErrorCode ErrorCode { get; set; } + + public string ErrorMessage { + get; internal set; + } } public class VMNotSuspendedException : InvalidOperationException diff --git a/mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs b/mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs index 82efc79e51d8..7e55dae9989d 100644 --- a/mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs +++ b/mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs @@ -145,6 +145,12 @@ public override string ToString () { } } + +public struct BlittableStruct { + public int i; + public double d; +} + public class GClass { public T field; public static T static_field; @@ -343,6 +349,8 @@ public static int Main (String[] args) { gc_suspend (); set_ip (); step_filters (); + pointers (); + ref_return (); if (args.Length > 0 && args [0] == "local-reflect") local_reflect (); if (args.Length > 0 && args [0] == "domain-test") @@ -357,6 +365,10 @@ public static int Main (String[] args) { if (args.Length > 0 && args [0] == "invoke-abort") new Tests ().invoke_abort (); new Tests ().evaluate_method (); + + test_async_debug_generics(); + test_invalid_argument_assembly_get_type (); + return 3; } @@ -365,6 +377,10 @@ public static void local_reflect () { LocalReflectClass.RunMe (); } + public static void test_invalid_argument_assembly_get_type () { + + } + public static void breakpoints () { /* Call these early so it is JITted by the time a breakpoint is placed on it */ bp3 (); @@ -543,6 +559,17 @@ public static void ss_nested () { ss_nested_3 (); } + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void test_async_debug_generics () { + ExecuteAsync_Broken().Wait (); + } + + async static Task ExecuteAsync_Broken() + { + await Task.Delay(2); + return default; + } + [MethodImplAttribute (MethodImplOptions.NoInlining)] public static void ss_nested_1 (int i) { } @@ -1720,6 +1747,38 @@ public void attach () { attach_break (); } } + + public static unsafe void pointer_arguments (int* a, BlittableStruct* s) { + *a = 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static unsafe void pointers () { + int[] a = new [] {1,2,3}; + BlittableStruct s = new BlittableStruct () { i = 2, d = 3.0 }; + fixed (int* pa = a) + pointer_arguments (pa, &s); + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void ref_return () { + + } + + static int ret_val = 1; + public static ref int get_ref_int() { + return ref ret_val; + } + + static string ref_return_string = "byref"; + public static ref string get_ref_string() { + return ref ref_return_string; + } + + static BlittableStruct ref_return_struct = new BlittableStruct () { i = 1, d = 2.0 }; + public static ref BlittableStruct get_ref_struct () { + return ref ref_return_struct; + } } public class SentinelClass : MarshalByRefObject { diff --git a/mcs/class/Mono.Debugger.Soft/Test/dtest.cs b/mcs/class/Mono.Debugger.Soft/Test/dtest.cs index ccd3b6b7602e..7294c53a37ca 100644 --- a/mcs/class/Mono.Debugger.Soft/Test/dtest.cs +++ b/mcs/class/Mono.Debugger.Soft/Test/dtest.cs @@ -324,6 +324,8 @@ void AssertValue (object expected, object val) { Assert.AreEqual (expected, (val as StringMirror).Value); } else if (val is StructMirror && (val as StructMirror).Type.Name == "IntPtr") { AssertValue (expected, (val as StructMirror).Fields [0]); + } else if (val is PointerValue) { + Assert.AreEqual (expected, (val as PointerValue).Address); } else { Assert.IsTrue (val is PrimitiveValue); Assert.AreEqual (expected, (val as PrimitiveValue).Value); @@ -4361,6 +4363,95 @@ public void Hash () } } -} + [Test] + public void Pointer_GetValue () { + var e = run_until ("pointer_arguments"); + var frame = e.Thread.GetFrames () [0]; + + var param = frame.Method.GetParameters()[0]; + Assert.AreEqual("Int32*", param.ParameterType.Name); + + var pointerValue = frame.GetValue(param) as PointerValue; + Assert.AreEqual("Int32*", pointerValue.Type.Name); + + AssertValue(1, pointerValue.Value); + + var pointerValue2 = new PointerValue (pointerValue.VirtualMachine, pointerValue.Type, pointerValue.Address + pointerValue.Type.GetElementType().GetValueSize()); + + AssertValue(2, pointerValue2.Value); + + + param = frame.Method.GetParameters()[1]; + Assert.AreEqual("BlittableStruct*", param.ParameterType.Name); + + pointerValue = frame.GetValue(param) as PointerValue; + Assert.AreEqual("BlittableStruct*", pointerValue.Type.Name); + + var structValue = pointerValue.Value as StructMirror; + Assert.AreEqual("BlittableStruct", structValue.Type.Name); -} + object f = structValue.Fields[0]; + AssertValue (2, f); + f = structValue.Fields[1]; + AssertValue (3.0, f); + + } + + [Test] + public void InvokeGenericMethod () { + Event e = run_until ("bp1"); + StackFrame frame = e.Thread.GetFrames()[0]; + TypeMirror t = frame.Method.DeclaringType; + MethodMirror m; + m = t.GetMethod ("generic_method"); + AssertThrows (delegate { + t.InvokeMethod (e.Thread, m, null); + }); + } + + [Test] + public void InvokeRefReturnMethod () { + Event e = run_until ("ref_return"); + StackFrame frame = e.Thread.GetFrames()[0]; + TypeMirror t = frame.Method.DeclaringType; + MethodMirror m; + + m = t.GetMethod ("get_ref_int"); + var v = t.InvokeMethod (e.Thread, m, null); + AssertValue (1, v); + + m = t.GetMethod ("get_ref_string"); + v = t.InvokeMethod (e.Thread, m, null); + AssertValue ("byref", v); + + m = t.GetMethod ("get_ref_struct"); + v = t.InvokeMethod (e.Thread, m, null); + Assert.IsTrue(v is StructMirror); + var mirror = (StructMirror)v; + AssertValue (1, mirror["i"]); + AssertValue (2.0, mirror["d"]); + } + + [Test] + public void InvalidArgumentAssemblyGetType () { + Event e = run_until ("test_invalid_argument_assembly_get_type"); + var assembly = entry_point.DeclaringType.Assembly; + try { + var type = assembly.GetType ("System.Collections.Generic.Dictionary.Main"); + } + catch (CommandException ex) { + Assert.AreEqual(ex.ErrorMessage, "Unexpected assembly-qualified type \"System.Collections.Generic.Dictionary.Main\" was provided"); + } + } + + [Test] + public void TestAsyncDebugGenerics () { + Event e = run_until ("test_async_debug_generics"); + e = step_in_await ("test_async_debug_generics", e); + e = step_in_await ("MoveNext", e); + e = step_in_await ("MoveNext", e); + e = step_in_await ("MoveNext", e); + e = step_in_await ("MoveNext", e); + } +} // class DebuggerTests +} // namespace diff --git a/mcs/class/System.Core/System.IO.Pipes/NamedPipeClientStream.cs b/mcs/class/System.Core/System.IO.Pipes/NamedPipeClientStream.cs index a27c4dc0b4d3..bd71ec318014 100644 --- a/mcs/class/System.Core/System.IO.Pipes/NamedPipeClientStream.cs +++ b/mcs/class/System.Core/System.IO.Pipes/NamedPipeClientStream.cs @@ -74,7 +74,7 @@ public NamedPipeClientStream (string serverName, string pipeName, PipeDirection } public NamedPipeClientStream (string serverName, string pipeName, PipeDirection direction, PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) -#if MOBILE +#if MOBILE && !UNITY_AOT : base (direction, DefaultBufferSize) { throw new NotImplementedException (); @@ -88,13 +88,17 @@ public NamedPipeClientStream (string serverName, string pipeName, PipeDirection public NamedPipeClientStream (PipeDirection direction, bool isAsync, bool isConnected, SafePipeHandle safePipeHandle) : base (direction, DefaultBufferSize) { -#if MOBILE +#if MOBILE && !UNITY_AOT throw new NotImplementedException (); #else if (IsWindows) impl = new Win32NamedPipeClient (this, safePipeHandle); else +#if UNITY_AOT + throw new NotImplementedException (); +#else impl = new UnixNamedPipeClient (this, safePipeHandle); +#endif IsConnected = isConnected; InitializeHandle (safePipeHandle, true, isAsync); #endif @@ -106,13 +110,17 @@ public NamedPipeClientStream (string serverName, string pipeName, PipeAccessRigh if (impersonationLevel != TokenImpersonationLevel.None || inheritability != HandleInheritability.None) throw ThrowACLException (); -#if MOBILE +#if MOBILE && !UNITY_AOT throw new NotImplementedException (); #else if (IsWindows) impl = new Win32NamedPipeClient (this, serverName, pipeName, desiredAccessRights, options, inheritability); else +#if UNITY_AOT + throw new NotImplementedException (); +#else impl = new UnixNamedPipeClient (this, serverName, pipeName, desiredAccessRights, options, inheritability); +#endif #endif } @@ -121,13 +129,13 @@ public NamedPipeClientStream (string serverName, string pipeName, PipeAccessRigh Dispose (false); } -#if !MOBILE +#if !MOBILE || UNITY_AOT INamedPipeClient impl; #endif public void Connect () { -#if MOBILE +#if MOBILE && !UNITY_AOT throw new NotImplementedException (); #else impl.Connect (); @@ -138,7 +146,7 @@ public void Connect () public void Connect (int timeout) { -#if MOBILE +#if MOBILE && !UNITY_AOT throw new NotImplementedException (); #else impl.Connect (timeout); @@ -174,7 +182,7 @@ protected override internal void CheckPipePropertyOperations () { public int NumberOfServerInstances { get { CheckPipePropertyOperations (); -#if MOBILE +#if MOBILE && !UNITY_AOT throw new NotImplementedException (); #else return impl.NumberOfServerInstances; diff --git a/mcs/class/System.Core/System.IO.Pipes/PipeStream.cs b/mcs/class/System.Core/System.IO.Pipes/PipeStream.cs index d60b71b1f5b7..52b0773020f1 100644 --- a/mcs/class/System.Core/System.IO.Pipes/PipeStream.cs +++ b/mcs/class/System.Core/System.IO.Pipes/PipeStream.cs @@ -46,7 +46,7 @@ public abstract class PipeStream : Stream // FIXME: not precise. internal const int DefaultBufferSize = 0x400; -#if !MOBILE +#if !MOBILE || UNITY_AOT internal static bool IsWindows { get { return Win32Marshal.IsWindows; } } @@ -137,7 +137,7 @@ internal Stream Stream { #pragma warning disable 618 stream = new FileStream (handle.DangerousGetHandle (), CanRead ? (CanWrite ? FileAccess.ReadWrite : FileAccess.Read) - : FileAccess.Write, true, buffer_size, IsAsync); + : FileAccess.Write, false, buffer_size, IsAsync); #pragma warning restore 618 } return stream; diff --git a/mcs/class/System.Core/common_System.Core.dll.sources b/mcs/class/System.Core/common_System.Core.dll.sources index cfe271aeee0f..7a83de3eb8f6 100644 --- a/mcs/class/System.Core/common_System.Core.dll.sources +++ b/mcs/class/System.Core/common_System.Core.dll.sources @@ -278,7 +278,7 @@ System.Security.Cryptography/SHA512CryptoServiceProvider.cs ../../../external/corefx/src/Common/src/System/Collections/Generic/ArrayBuilder.cs ../../../external/corefx/src/Common/src/System/Collections/Generic/EnumerableHelpers.cs ../../../external/corefx/src/Common/src/System/Collections/Generic/EnumerableHelpers.Linq.cs -../../../external/corefx/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs +../../../external/corefx-bugfix/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs ../../../external/corefx/src/Common/src/System/Collections/Generic/SparseArrayBuilder.cs ../../../external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/CacheDict.cs @@ -289,3 +289,5 @@ System.Security.Cryptography/SHA512CryptoServiceProvider.cs ../../../external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.cs ../../../external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs + +../../../external/corefx/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.net46.cs diff --git a/mcs/class/System.Core/unityaot_System.Core.dll.exclude.sources b/mcs/class/System.Core/unityaot_System.Core.dll.exclude.sources new file mode 100644 index 000000000000..7953f1b8f0ef --- /dev/null +++ b/mcs/class/System.Core/unityaot_System.Core.dll.exclude.sources @@ -0,0 +1 @@ +../../../external/corefx/src/System.Linq/src/System/Linq/*.cs diff --git a/mcs/class/System.Core/unityaot_System.Core.dll.sources b/mcs/class/System.Core/unityaot_System.Core.dll.sources new file mode 100644 index 000000000000..4baa8f0c00b7 --- /dev/null +++ b/mcs/class/System.Core/unityaot_System.Core.dll.sources @@ -0,0 +1,4 @@ +#include winaot_System.Core.dll.sources +../referencesource/System.Core/System/Linq/Enumerable.cs +corefx/SR.cs +System.IO.Pipes/PipeWin32.cs diff --git a/mcs/class/System.Core/unityjit_System.Core.dll.sources b/mcs/class/System.Core/unityjit_System.Core.dll.sources new file mode 100644 index 000000000000..e5681ff8ff5b --- /dev/null +++ b/mcs/class/System.Core/unityjit_System.Core.dll.sources @@ -0,0 +1 @@ +#include net_4_x_System.Core.dll.sources diff --git a/mcs/class/System.Data/unityjit_System.Data.dll.sources b/mcs/class/System.Data/unityjit_System.Data.dll.sources new file mode 100644 index 000000000000..63aa133be189 --- /dev/null +++ b/mcs/class/System.Data/unityjit_System.Data.dll.sources @@ -0,0 +1,4 @@ +#include corefx.unix.sources +#include net_4_x_System.Data.dll.sources + +../../../external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Windows.cs diff --git a/mcs/class/System.Design/System.Web.UI.Design.WebControls/CompositeControlDesigner.cs b/mcs/class/System.Design/System.Web.UI.Design.WebControls/CompositeControlDesigner.cs index 17997212533c..8a13185c5ab1 100644 --- a/mcs/class/System.Design/System.Web.UI.Design.WebControls/CompositeControlDesigner.cs +++ b/mcs/class/System.Design/System.Web.UI.Design.WebControls/CompositeControlDesigner.cs @@ -35,7 +35,7 @@ namespace System.Web.UI.Design.WebControls { - class CompositeControlDesigner : ControlDesigner + public class CompositeControlDesigner : ControlDesigner { public CompositeControlDesigner () { throw new NotImplementedException (); diff --git a/mcs/class/System.Drawing/unityaot_System.Drawing.dll.sources b/mcs/class/System.Drawing/unityaot_System.Drawing.dll.sources new file mode 100644 index 000000000000..0403fdc41fcc --- /dev/null +++ b/mcs/class/System.Drawing/unityaot_System.Drawing.dll.sources @@ -0,0 +1 @@ +#include winaot_System.Drawing.dll.sources diff --git a/mcs/class/System.IdentityModel/unityaot_System.IdentityModel.dll.sources b/mcs/class/System.IdentityModel/unityaot_System.IdentityModel.dll.sources new file mode 100644 index 000000000000..429636751c2e --- /dev/null +++ b/mcs/class/System.IdentityModel/unityaot_System.IdentityModel.dll.sources @@ -0,0 +1 @@ +#include mobile_System.IdentityModel.dll.sources diff --git a/mcs/class/System.IdentityModel/unityjit_System.IdentityModel.dll.sources b/mcs/class/System.IdentityModel/unityjit_System.IdentityModel.dll.sources new file mode 100644 index 000000000000..7d312678c9ce --- /dev/null +++ b/mcs/class/System.IdentityModel/unityjit_System.IdentityModel.dll.sources @@ -0,0 +1 @@ +#include net_4_x_System.IdentityModel.dll.sources diff --git a/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs b/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs index 7d154cd8fdc7..dae2cd2350ae 100644 --- a/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs +++ b/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs @@ -387,7 +387,7 @@ protected async internal override Task SendAsync (HttpReque } } - wrequest.ResendContentFactory = content.CopyTo; + wrequest.ResendContentFactory = content.CopyToAsync; using (var stream = await wrequest.GetRequestStreamAsync ().ConfigureAwait (false)) { await request.Content.CopyToAsync (stream).ConfigureAwait (false); diff --git a/mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs b/mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs index 53ad58d58e51..dec51db3f671 100644 --- a/mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs +++ b/mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs @@ -508,7 +508,9 @@ void ReadSimpleDictionary (CollectionDataContract collectionContract, Type keyVa var jsonMemberName = XmlObjectSerializerReadContextComplexJson.GetJsonMemberName (xmlReader); object key = null; - if (keyParseMode == KeyParseMode.UsingParseEnum) + if (keyParseMode == KeyParseMode.AsString) + key = jsonMemberName; + else if (keyParseMode == KeyParseMode.UsingParseEnum) key = Enum.Parse (keyType, jsonMemberName); else if (keyParseMode == KeyParseMode.UsingCustomParse) key = keyDataContract.ParseMethod.Invoke (null, new object [] {jsonMemberName}); diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources index c1ebbd981530..bdc4b9adcc20 100644 --- a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources +++ b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources @@ -16,3 +16,5 @@ ReferenceSources/XmlDataContract_static.cs ReferenceSources/XmlFormatReaderGenerator_static.cs ReferenceSources/XmlFormatWriterGenerator_static.cs + +../../../external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/ISerializationSurrogateProvider.cs diff --git a/mcs/class/System.Runtime.Serialization/unityaot_System.Runtime.Serialization.dll.sources b/mcs/class/System.Runtime.Serialization/unityaot_System.Runtime.Serialization.dll.sources new file mode 100644 index 000000000000..6caafd411987 --- /dev/null +++ b/mcs/class/System.Runtime.Serialization/unityaot_System.Runtime.Serialization.dll.sources @@ -0,0 +1 @@ +#include mobile_System.Runtime.Serialization.dll.sources diff --git a/mcs/class/System.Runtime.Serialization/unityjit_System.Runtime.Serialization.dll.sources b/mcs/class/System.Runtime.Serialization/unityjit_System.Runtime.Serialization.dll.sources new file mode 100644 index 000000000000..5feabf388ccf --- /dev/null +++ b/mcs/class/System.Runtime.Serialization/unityjit_System.Runtime.Serialization.dll.sources @@ -0,0 +1 @@ +#include net_4_x_System.Runtime.Serialization.dll.sources diff --git a/mcs/class/System.ServiceModel.Internals/EventLogEntryType.cs b/mcs/class/System.ServiceModel.Internals/EventLogEntryType.cs index 74811394aa85..601f4574a999 100644 --- a/mcs/class/System.ServiceModel.Internals/EventLogEntryType.cs +++ b/mcs/class/System.ServiceModel.Internals/EventLogEntryType.cs @@ -41,7 +41,12 @@ build without adding this public API in System.dll. IF we brought this using System.Diagnostics; namespace System.Diagnostics { - public enum EventLogEntryType { +#if UNITY_AOT + internal +#else + public +#endif + enum EventLogEntryType { Error = 0x01, Warning = 0x02, Information = 0x04, diff --git a/mcs/class/System.ServiceModel.Web/unityaot_System.ServiceModel.Web.dll.sources b/mcs/class/System.ServiceModel.Web/unityaot_System.ServiceModel.Web.dll.sources new file mode 100644 index 000000000000..cbea0fae7b4f --- /dev/null +++ b/mcs/class/System.ServiceModel.Web/unityaot_System.ServiceModel.Web.dll.sources @@ -0,0 +1 @@ +#include mobile_System.ServiceModel.Web.dll.sources diff --git a/mcs/class/System.ServiceModel/Dummy_XM_4_5.cs b/mcs/class/System.ServiceModel/Dummy_XM_4_5.cs index 989c4dddc527..588ada68ccf6 100644 --- a/mcs/class/System.ServiceModel/Dummy_XM_4_5.cs +++ b/mcs/class/System.ServiceModel/Dummy_XM_4_5.cs @@ -126,7 +126,11 @@ internal EndpointDispatcher () namespace System.ServiceModel.Channels { +#if UNITY + static class UrlUtility { +#else public static class UrlUtility { +#endif public static string UrlEncode (string s, Encoding e) { return System.Runtime.UrlUtility.UrlEncode (s, e); diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Channels/TransactionMessageProperty.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Channels/TransactionMessageProperty.cs index 7fc83b92bae6..37d4688d6f83 100644 --- a/mcs/class/System.ServiceModel/System.ServiceModel.Channels/TransactionMessageProperty.cs +++ b/mcs/class/System.ServiceModel/System.ServiceModel.Channels/TransactionMessageProperty.cs @@ -36,7 +36,7 @@ namespace System.ServiceModel.Channels { - internal class TransactionMessageProperty + public class TransactionMessageProperty { Transaction tx; Message msg; diff --git a/mcs/class/System.ServiceModel/unityaot_System.ServiceModel.dll.sources b/mcs/class/System.ServiceModel/unityaot_System.ServiceModel.dll.sources new file mode 100644 index 000000000000..22ece5ce8cec --- /dev/null +++ b/mcs/class/System.ServiceModel/unityaot_System.ServiceModel.dll.sources @@ -0,0 +1 @@ +#include mobile_System.ServiceModel.dll.sources diff --git a/mcs/class/System.Web.Services/unityaot_System.Web.Services.dll.sources b/mcs/class/System.Web.Services/unityaot_System.Web.Services.dll.sources new file mode 100644 index 000000000000..9e39dcc73b13 --- /dev/null +++ b/mcs/class/System.Web.Services/unityaot_System.Web.Services.dll.sources @@ -0,0 +1 @@ +#include mobile_System.Web.Services.dll.sources diff --git a/mcs/class/System.Web/System.Web.UI.WebControls.WebParts/IPersonalizable.cs b/mcs/class/System.Web/System.Web.UI.WebControls.WebParts/IPersonalizable.cs index 719371ad7be3..8ffea6886041 100644 --- a/mcs/class/System.Web/System.Web.UI.WebControls.WebParts/IPersonalizable.cs +++ b/mcs/class/System.Web/System.Web.UI.WebControls.WebParts/IPersonalizable.cs @@ -35,9 +35,6 @@ namespace System.Web.UI.WebControls.WebParts { public interface IPersonalizable { - void Load (IDictionary sharedState, IDictionary userState); - void Save (IDictionary state); - bool IsDirty { get; } } } diff --git a/mcs/class/System.XML/System.Xml.Serialization/XmlSerializationReader.cs b/mcs/class/System.XML/System.Xml.Serialization/XmlSerializationReader.cs index de6710af0a5f..e49e1feeca9b 100644 --- a/mcs/class/System.XML/System.Xml.Serialization/XmlSerializationReader.cs +++ b/mcs/class/System.XML/System.Xml.Serialization/XmlSerializationReader.cs @@ -1193,10 +1193,15 @@ protected Exception CreateMissingIXmlSerializableType (string name, string ns, s throw new NotImplementedException (); } - [MonoTODO] protected string ReadString (string value, bool trim) { - throw new NotImplementedException (); + readCount++; + string str = reader.ReadString (); + if (str != null && trim) + str = str.Trim(); + if (value == null || value.Length == 0) + return str; + return value + str; } [MonoTODO] diff --git a/mcs/class/System.XML/unityaot_System.Xml.dll.sources b/mcs/class/System.XML/unityaot_System.Xml.dll.sources new file mode 100644 index 000000000000..b6630930f75f --- /dev/null +++ b/mcs/class/System.XML/unityaot_System.Xml.dll.sources @@ -0,0 +1 @@ +#include mobile_System.Xml.dll.sources diff --git a/mcs/class/System.Xaml/System.Xaml.Schema/XamlCollectionKind.cs b/mcs/class/System.Xaml/System.Xaml.Schema/XamlCollectionKind.cs index 3e3253936feb..27fd119253ec 100644 --- a/mcs/class/System.Xaml/System.Xaml.Schema/XamlCollectionKind.cs +++ b/mcs/class/System.Xaml/System.Xaml.Schema/XamlCollectionKind.cs @@ -25,7 +25,7 @@ namespace System.Xaml.Schema { - public enum XamlCollectionKind + public enum XamlCollectionKind : byte { None, Collection, diff --git a/mcs/class/System.Xaml/System.Xaml/XamlNodeType.cs b/mcs/class/System.Xaml/System.Xaml/XamlNodeType.cs index b71fcdd23bb9..eb3400f37005 100644 --- a/mcs/class/System.Xaml/System.Xaml/XamlNodeType.cs +++ b/mcs/class/System.Xaml/System.Xaml/XamlNodeType.cs @@ -28,7 +28,7 @@ namespace System.Xaml { - public enum XamlNodeType + public enum XamlNodeType : byte { None, StartObject, diff --git a/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs b/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs index 3406cae2c0f2..8f426fa3aa16 100644 --- a/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs +++ b/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs @@ -70,7 +70,10 @@ public void Reset () { Offset = Size = 0; TotalBytes = 0; - Buffer = new byte[InitialSize]; + if (Buffer.Length <= InitialSize) + Array.Clear (Buffer, 0, Buffer.Length); + else + Buffer = new byte[InitialSize]; Complete = false; } diff --git a/mcs/class/System/Mono.Net.Security/MonoTlsProviderFactory.cs b/mcs/class/System/Mono.Net.Security/MonoTlsProviderFactory.cs index dcc640ad30fe..7e2135f49c55 100644 --- a/mcs/class/System/Mono.Net.Security/MonoTlsProviderFactory.cs +++ b/mcs/class/System/Mono.Net.Security/MonoTlsProviderFactory.cs @@ -211,6 +211,7 @@ internal static void Debug (string message, params object[] args) #endregion + internal static readonly Guid UnityTlsId = new Guid("06414A97-74F6-488F-877B-A6CA9BBEB82E"); internal static readonly Guid AppleTlsId = new Guid ("981af8af-a3a3-419a-9f01-a518e3a17c1c"); internal static readonly Guid BtlsId = new Guid ("432d18c9-9348-4b90-bfbf-9f2a10e1f15b"); internal static readonly Guid LegacyId = new Guid ("809e77d5-56cc-4da8-b9f0-45e65ba9cceb"); @@ -226,6 +227,16 @@ static void InitializeProviderRegistration () providerRegistration = new Dictionary> (); providerCache = new Dictionary (); +#if UNITY + if (Mono.Unity.UnityTls.IsSupported) + { + var unityTlsEntry = new Tuple (UnityTlsId, "Mono.Unity.UnityTlsProvider"); + providerRegistration.Add ("default", unityTlsEntry); + providerRegistration.Add ("unitytls", unityTlsEntry); + return; + } +#endif + var appleTlsEntry = new Tuple (AppleTlsId, "Mono.AppleTls.AppleTlsProvider"); #if ONLY_APPLETLS || MONOTOUCH || XAMMAC diff --git a/mcs/class/System/Mono.Net.Security/MonoTlsStream.cs b/mcs/class/System/Mono.Net.Security/MonoTlsStream.cs index ba6d5197231e..52d1be27eec8 100644 --- a/mcs/class/System/Mono.Net.Security/MonoTlsStream.cs +++ b/mcs/class/System/Mono.Net.Security/MonoTlsStream.cs @@ -52,7 +52,7 @@ namespace Mono.Net.Security { class MonoTlsStream { -#if SECURITY_DEP +#if SECURITY_DEP readonly MonoTlsProvider provider; readonly NetworkStream networkStream; readonly HttpWebRequest request; @@ -99,9 +99,11 @@ public MonoTlsStream (HttpWebRequest request, NetworkStream networkStream) #endif } - internal Stream CreateStream (byte[] buffer) + internal async Task CreateStream (WebConnectionTunnel tunnel, CancellationToken cancellationToken) { #if SECURITY_DEP + var socket = networkStream.InternalSocket; + WebConnection.Debug ($"MONO TLS STREAM CREATE STREAM: {socket.ID}"); sslStream = provider.CreateSslStream (networkStream, false, settings); try { @@ -112,16 +114,21 @@ internal Stream CreateStream (byte[] buffer) host = host.Substring (0, pos); } - sslStream.AuthenticateAsClient ( + await sslStream.AuthenticateAsClientAsync ( host, request.ClientCertificates, (SslProtocols)ServicePointManager.SecurityProtocol, - ServicePointManager.CheckCertificateRevocationList); + ServicePointManager.CheckCertificateRevocationList).ConfigureAwait (false); status = WebExceptionStatus.Success; - } catch { - status = WebExceptionStatus.SecureChannelFailure; + } catch (Exception ex) { + WebConnection.Debug ($"MONO TLS STREAM ERROR: {socket.ID} {socket.CleanedUp} {ex.Message}"); + if (socket.CleanedUp) + status = WebExceptionStatus.RequestCanceled; + else + status = WebExceptionStatus.SecureChannelFailure; throw; } finally { + WebConnection.Debug ($"MONO TLS STREAM CREATE STREAM DONE: {socket.ID} {socket.CleanedUp}"); if (CertificateValidationFailed) status = WebExceptionStatus.TrustFailure; @@ -134,8 +141,8 @@ internal Stream CreateStream (byte[] buffer) } try { - if (buffer != null) - sslStream.Write (buffer, 0, buffer.Length); + if (tunnel?.Data != null) + await sslStream.WriteAsync (tunnel.Data, 0, tunnel.Data.Length, cancellationToken).ConfigureAwait (false); } catch { status = WebExceptionStatus.SendFailure; sslStream = null; diff --git a/mcs/class/System/Mono.UnityTls/CertHelper.cs b/mcs/class/System/Mono.UnityTls/CertHelper.cs new file mode 100644 index 000000000000..1cddc190ee51 --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/CertHelper.cs @@ -0,0 +1,36 @@ +#if SECURITY_DEP +using System.Security.Cryptography.X509Certificates; + +using size_t = System.IntPtr; + +namespace Mono.Unity +{ + internal unsafe static class CertHelper + { + public static void AddCertificatesToNativeChain (UnityTls.unitytls_x509list* nativeCertificateChain, X509CertificateCollection certificates, UnityTls.unitytls_errorstate* errorState) + { + foreach (var certificate in certificates) { + AddCertificateToNativeChain (nativeCertificateChain, certificate, errorState); + } + } + + public static void AddCertificateToNativeChain (UnityTls.unitytls_x509list* nativeCertificateChain, X509Certificate certificate, UnityTls.unitytls_errorstate* errorState) + { + byte[] certDer = certificate.GetRawCertData (); + fixed(byte* certDerPtr = certDer) { + UnityTls.NativeInterface.unitytls_x509list_append_der (nativeCertificateChain, certDerPtr, (size_t)certDer.Length, errorState); + } + + var certificateImpl2 = certificate.Impl as X509Certificate2Impl; + if (certificateImpl2 != null) { + var intermediates = certificateImpl2.IntermediateCertificates; + if (intermediates != null && intermediates.Count > 0) { + for (int i=0; i NativeInterface != null; + + public static unitytls_interface_struct NativeInterface + { + get + { + if (marshalledInterface == null) { + IntPtr rawInterface = GetUnityTlsInterface (); + if (rawInterface == IntPtr.Zero) + return null; + marshalledInterface = Marshal.PtrToStructure (rawInterface); + } + return marshalledInterface; + } + } + } +} \ No newline at end of file diff --git a/mcs/class/System/Mono.UnityTls/UnityTlsContext.cs b/mcs/class/System/Mono.UnityTls/UnityTlsContext.cs new file mode 100644 index 000000000000..fbf0f68a6e3f --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/UnityTlsContext.cs @@ -0,0 +1,513 @@ +#if SECURITY_DEP +#if MONO_SECURITY_ALIAS +extern alias MonoSecurity; +#endif + +using System; +using System.IO; +using System.Text; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Security.Authentication; + +#if MONO_SECURITY_ALIAS +using MonoSecurity::Mono.Security.Interface; +using MonoSecurity::Mono.Security.Cryptography; +#else +using Mono.Security.Interface; +using Mono.Security.Cryptography; +#endif + +using Mono.Net.Security; +using Mono.Util; + +using Int8 = System.Byte; +using size_t = System.IntPtr; + +namespace Mono.Unity +{ + unsafe internal class UnityTlsContext : MobileTlsContext + { + private const bool ActivateTracing = false; + + // Native UnityTls objects + UnityTls.unitytls_tlsctx* tlsContext = null; + UnityTls.unitytls_x509list* requestedClientCertChain = null; + UnityTls.unitytls_key* requestedClientKey = null; + + // Delegates we passed to native to ensure they are not garbage collected + UnityTls.unitytls_tlsctx_read_callback readCallback; + UnityTls.unitytls_tlsctx_write_callback writeCallback; + UnityTls.unitytls_tlsctx_trace_callback traceCallback; + UnityTls.unitytls_tlsctx_certificate_callback certificateCallback; + UnityTls.unitytls_tlsctx_x509verify_callback verifyCallback; + + // States and certificates + X509Certificate localClientCertificate; + X509Certificate remoteCertificate; + MonoTlsConnectionInfo connectioninfo; + bool isAuthenticated = false; + bool hasContext = false; + bool closedGraceful = false; + + // Memory-buffer + byte [] writeBuffer; + byte [] readBuffer; + + GCHandle handle; + Exception lastException; + + public UnityTlsContext ( + MobileAuthenticatedStream parent, + bool serverMode, string targetHost, + SslProtocols enabledProtocols, X509Certificate serverCertificate, + X509CertificateCollection clientCertificates, bool askForClientCert) + : base (parent, serverMode, targetHost, enabledProtocols, serverCertificate, clientCertificates, askForClientCert) + { + // Need GCHandle to get a consistent pointer to this instance + handle = GCHandle.Alloc (this); + + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + + // Map selected protocols as best as we can. + UnityTls.unitytls_tlsctx_protocolrange protocolRange = new UnityTls.unitytls_tlsctx_protocolrange { + min = UnityTlsConversions.GetMinProtocol (enabledProtocols), + max = UnityTlsConversions.GetMaxProtocol (enabledProtocols), + }; + + readCallback = ReadCallback; + writeCallback = WriteCallback; + UnityTls.unitytls_tlsctx_callbacks callbacks = new UnityTls.unitytls_tlsctx_callbacks { + write = writeCallback, + read = readCallback, + data = (void*)(IntPtr)handle, + }; + + if (serverMode) { + ExtractNativeKeyAndChainFromManagedCertificate(serverCertificate, &errorState, out var serverCerts, out var serverPrivateKey); + try { + var serverCertsRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (serverCerts, &errorState); + var serverKeyRef = UnityTls.NativeInterface.unitytls_key_get_ref (serverPrivateKey, &errorState); + Mono.Unity.Debug.CheckAndThrow (errorState, "Failed to parse server key/certificate"); + + tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_server (protocolRange, callbacks, serverCertsRef.handle, serverKeyRef.handle, &errorState); + + if (askForClientCert) { + UnityTls.unitytls_x509list* clientAuthCAList = null; + try { + clientAuthCAList = UnityTls.NativeInterface.unitytls_x509list_create (&errorState); + var clientAuthCAListRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (clientAuthCAList, &errorState); + UnityTls.NativeInterface.unitytls_tlsctx_server_require_client_authentication (tlsContext, clientAuthCAListRef, &errorState); + } finally { + UnityTls.NativeInterface.unitytls_x509list_free (clientAuthCAList); + } + } + } finally { + UnityTls.NativeInterface.unitytls_x509list_free (serverCerts); + UnityTls.NativeInterface.unitytls_key_free (serverPrivateKey); + } + } + else { + byte [] targetHostUtf8 = Encoding.UTF8.GetBytes (targetHost); + fixed (byte* targetHostUtf8Ptr = targetHostUtf8) { + tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_client (protocolRange, callbacks, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, &errorState); + } + + certificateCallback = CertificateCallback; + UnityTls.NativeInterface.unitytls_tlsctx_set_certificate_callback (tlsContext, certificateCallback, (void*)(IntPtr)handle, &errorState); + } + + verifyCallback = VerifyCallback; + UnityTls.NativeInterface.unitytls_tlsctx_set_x509verify_callback (tlsContext, verifyCallback, (void*)(IntPtr)handle, &errorState); + + Mono.Unity.Debug.CheckAndThrow (errorState, "Failed to create UnityTls context"); + + #pragma warning disable CS0162 // Disable unreachable code warning + if (ActivateTracing) { + traceCallback = TraceCallback; + UnityTls.NativeInterface.unitytls_tlsctx_set_trace_callback (tlsContext, traceCallback, null, &errorState); + Mono.Unity.Debug.CheckAndThrow (errorState, "Failed to set trace callback"); + } + #pragma warning restore CS0162 // Reenable unreachable code warning. + + hasContext = true; + } + + static private void ExtractNativeKeyAndChainFromManagedCertificate(X509Certificate cert, UnityTls.unitytls_errorstate* errorState, out UnityTls.unitytls_x509list* nativeCertChain, out UnityTls.unitytls_key* nativeKey) + { + if (cert == null) + throw new ArgumentNullException ("cert"); + X509Certificate2 cert2 = cert as X509Certificate2; + if (cert2 == null || cert2.PrivateKey == null) + throw new ArgumentException ("Certificate does not have a private key", "cert"); + + nativeCertChain = null; + nativeKey = null; + try { + nativeCertChain = UnityTls.NativeInterface.unitytls_x509list_create (errorState); + CertHelper.AddCertificateToNativeChain (nativeCertChain, cert, errorState); + + byte[] privateKeyDer = PKCS8.PrivateKeyInfo.Encode (cert2.PrivateKey); + fixed(byte* privateKeyDerPtr = privateKeyDer) { + nativeKey = UnityTls.NativeInterface.unitytls_key_parse_der (privateKeyDerPtr, (size_t)privateKeyDer.Length, null, (size_t)0, errorState); + } + } catch { + UnityTls.NativeInterface.unitytls_x509list_free (nativeCertChain); + UnityTls.NativeInterface.unitytls_key_free (nativeKey); + throw; + } + } + + public override bool HasContext { + get { return hasContext; } + } + public override bool IsAuthenticated { + get { return isAuthenticated; } + } + public override MonoTlsConnectionInfo ConnectionInfo { + get { return connectioninfo; } + } + internal override bool IsRemoteCertificateAvailable { + get { return remoteCertificate != null; } + } + internal override X509Certificate LocalClientCertificate { + get { return localClientCertificate; } + } + public override X509Certificate RemoteCertificate { + get { return remoteCertificate; } + } + public override TlsProtocols NegotiatedProtocol { + get { return ConnectionInfo.ProtocolVersion; } + } + + public override void Flush () + { + // NO-OP + } + + public override (int ret, bool wantMore) Read (byte[] buffer, int offset, int count) + { + int numBytesRead = 0; + + lastException = null; + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + fixed (byte* bufferPtr = buffer) { + numBytesRead = (int)UnityTls.NativeInterface.unitytls_tlsctx_read (tlsContext, bufferPtr + offset, (size_t)count, &errorState); + } + if (lastException != null) + throw lastException; + + switch (errorState.code) + { + case UnityTls.unitytls_error_code.UNITYTLS_SUCCESS: + // In contrast to some other APIs (like Apple security) WOULD_BLOCK is not set if we did a partial write. + // The Mono Api however requires us to set the wantMore flag also if we didn't read all the data. + return (numBytesRead, numBytesRead < count); + + case UnityTls.unitytls_error_code.UNITYTLS_USER_WOULD_BLOCK: + return (numBytesRead, true); + + case UnityTls.unitytls_error_code.UNITYTLS_STREAM_CLOSED: + return (0, false); // According to Apple and Btls implementation this is how we should handle gracefully closed connections. + + default: + if (!closedGraceful) { + Mono.Unity.Debug.CheckAndThrow (errorState, "Failed to read data to TLS context"); + } + return (0, false); + } + } + + public override (int ret, bool wantMore) Write (byte[] buffer, int offset, int count) + { + int numBytesWritten = 0; + + lastException = null; + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + fixed (byte* bufferPtr = buffer) { + numBytesWritten = (int)UnityTls.NativeInterface.unitytls_tlsctx_write (tlsContext, bufferPtr + offset, (size_t)count, &errorState); + } + if (lastException != null) + throw lastException; + + switch (errorState.code) + { + case UnityTls.unitytls_error_code.UNITYTLS_SUCCESS: + // In contrast to some other APIs (like Apple security) WOULD_BLOCK is not set if we did a partial write. + // The Mono Api however requires us to set the wantMore flag also if we didn't write all the data. + return (numBytesWritten, numBytesWritten < count); + + case UnityTls.unitytls_error_code.UNITYTLS_USER_WOULD_BLOCK: + return (numBytesWritten, true); + + case UnityTls.unitytls_error_code.UNITYTLS_STREAM_CLOSED: + return (0, false); // According to Apple and Btls implementation this is how we should handle gracefully closed connections. + + default: + Mono.Unity.Debug.CheckAndThrow (errorState, "Failed to write data to TLS context"); + return (0, false); + } + } + + public override void Shutdown () + { + if(Settings != null && Settings.SendCloseNotify) { + var err = UnityTls.NativeInterface.unitytls_errorstate_create (); + UnityTls.NativeInterface.unitytls_tlsctx_notify_close (tlsContext, &err); + } + + // Destroy native UnityTls objects + UnityTls.NativeInterface.unitytls_x509list_free (requestedClientCertChain); + UnityTls.NativeInterface.unitytls_key_free (requestedClientKey); + UnityTls.NativeInterface.unitytls_tlsctx_free (tlsContext); + tlsContext = null; + + hasContext = false; + } + + protected override void Dispose (bool disposing) + { + try { + if (disposing) + { + Shutdown(); + + // reset states + localClientCertificate = null; + remoteCertificate = null; + + if (localClientCertificate != null) { + localClientCertificate.Dispose (); + localClientCertificate = null; + } + if (remoteCertificate != null) { + remoteCertificate.Dispose (); + remoteCertificate = null; + } + + connectioninfo = null; + isAuthenticated = false; + hasContext = false; + } + + handle.Free(); + + } finally { + base.Dispose (disposing); + } + } + + public override void StartHandshake () + { + if (Settings != null && Settings.EnabledCiphers != null) { + var ciphers = new UnityTls.unitytls_ciphersuite [Settings.EnabledCiphers.Length]; + for (int i = 0; i < ciphers.Length; i++) + ciphers [i] = (UnityTls.unitytls_ciphersuite)Settings.EnabledCiphers [i]; + + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + fixed (UnityTls.unitytls_ciphersuite* ciphersPtr = ciphers) + UnityTls.NativeInterface.unitytls_tlsctx_set_supported_ciphersuites (tlsContext, ciphersPtr, (size_t)ciphers.Length, &errorState); + Unity.Debug.CheckAndThrow (errorState, "Failed to set list of supported ciphers", AlertDescription.HandshakeFailure); + } + } + + public override bool ProcessHandshake () + { + lastException = null; + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + var result = UnityTls.NativeInterface.unitytls_tlsctx_process_handshake (tlsContext, &errorState); + if (errorState.code == UnityTls.unitytls_error_code.UNITYTLS_USER_WOULD_BLOCK) + return false; + if (lastException != null) + throw lastException; + + // Not done is only an error if we are a client. Even servers with AskForClientCertificate should ignore it since .Net client authentification is always optional. + if (IsServer && result == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_NOT_DONE) { + Unity.Debug.CheckAndThrow (errorState, "Handshake failed", AlertDescription.HandshakeFailure); + + // .Net implementation gives the server a verification callback (with null cert) even if AskForClientCertificate is false. + // We stick to this behavior here. + if (!ValidateCertificate (null, null)) + throw new TlsException (AlertDescription.HandshakeFailure, "Verification failure during handshake"); + } + else + Unity.Debug.CheckAndThrow (errorState, result, "Handshake failed", AlertDescription.HandshakeFailure); + + return true; + } + + public override void FinishHandshake () + { + // Query some data. Ignore errors on the way since failure is not crucial. + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + var cipherSuite = UnityTls.NativeInterface.unitytls_tlsctx_get_ciphersuite(tlsContext, &errorState); + var protocolVersion = UnityTls.NativeInterface.unitytls_tlsctx_get_protocol(tlsContext, &errorState); + + connectioninfo = new MonoTlsConnectionInfo () { + CipherSuiteCode = (CipherSuiteCode)cipherSuite, + ProtocolVersion = UnityTlsConversions.ConvertProtocolVersion(protocolVersion), + PeerDomainName = ServerName + + // TODO: + // The following properties can be deducted from CipherSuiteCode. + // It looks though like as of writing no Mono implemention fills it out and there is also no mechanism that does that automatically + // + //CipherAlgorithmType + //HashAlgorithmType + //ExchangeAlgorithmType + }; + isAuthenticated = true; + } + + [MonoPInvokeCallback (typeof (UnityTls.unitytls_tlsctx_write_callback))] + static private size_t WriteCallback (void* userData, byte* data, size_t bufferLen, UnityTls.unitytls_errorstate* errorState) + { + var handle = (GCHandle)(IntPtr)userData; + var context = (UnityTlsContext)handle.Target; + return context.WriteCallback (data, bufferLen, errorState); + } + + private size_t WriteCallback (byte* data, size_t bufferLen, UnityTls.unitytls_errorstate* errorState) + { + try { + if (writeBuffer == null || writeBuffer.Length < (int)bufferLen) + writeBuffer = new byte[(int)bufferLen]; + Marshal.Copy ((IntPtr)data, writeBuffer, 0, (int)bufferLen); + + if (!Parent.InternalWrite (writeBuffer, 0, (int)bufferLen)) { + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_WRITE_FAILED); + return (size_t)0; + } + + return bufferLen; + } catch (Exception ex) { // handle all exceptions and store them for later since we don't want to let them go through native code. + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_UNKNOWN_ERROR); + if (lastException == null) + lastException = ex; + return (size_t)0; + } + } + + [MonoPInvokeCallback (typeof (UnityTls.unitytls_tlsctx_read_callback))] + static private size_t ReadCallback (void* userData, byte* buffer, size_t bufferLen, UnityTls.unitytls_errorstate* errorState) + { + var handle = (GCHandle)(IntPtr)userData; + var context = (UnityTlsContext)handle.Target; + return context.ReadCallback (buffer, bufferLen, errorState); + } + + private size_t ReadCallback (byte* buffer, size_t bufferLen, UnityTls.unitytls_errorstate* errorState) + { + try { + if (readBuffer == null || readBuffer.Length < (int)bufferLen) + readBuffer = new byte [(int)bufferLen]; + + bool wouldBlock; + int numBytesRead = Parent.InternalRead (readBuffer, 0, (int)bufferLen, out wouldBlock); + + // Non graceful exit. + if (numBytesRead < 0) { + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_READ_FAILED); + } else if (numBytesRead > 0) { + Marshal.Copy (readBuffer, 0, (IntPtr)buffer, (int)bufferLen); + } else { // numBytesRead == 0 + // careful when rearranging this: wouldBlock might be true even if stream was closed abruptly. + if (wouldBlock) { + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_WOULD_BLOCK); + } + // indicates graceful exit. + // UnityTls only accepts an exit as gracful, if it was closed via a special TLS protocol message. + // Both .Net and MobileTlsContext have a different idea of this concept though! + else { + closedGraceful = true; + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_READ_FAILED); + } + } + + // Note that UnityTls ignores this number when raising an error. + return (size_t)numBytesRead; + } catch (Exception ex) { // handle all exceptions and store them for later since we don't want to let them go through native code. + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_UNKNOWN_ERROR); + if (lastException == null) + lastException = ex; + return (size_t)0; + } + } + + [MonoPInvokeCallback (typeof (UnityTls.unitytls_tlsctx_x509verify_callback))] + static private UnityTls.unitytls_x509verify_result VerifyCallback (void* userData, UnityTls.unitytls_x509list_ref chain, UnityTls.unitytls_errorstate* errorState) + { + var handle = (GCHandle)(IntPtr)userData; + var context = (UnityTlsContext)handle.Target; + return context.VerifyCallback (chain, errorState); + } + + private UnityTls.unitytls_x509verify_result VerifyCallback (UnityTls.unitytls_x509list_ref chain, UnityTls.unitytls_errorstate* errorState) + { + try { + using (var chainImpl = new X509ChainImplUnityTls(chain)) + using (var managedChain = new X509Chain (chainImpl)) { + remoteCertificate = managedChain.ChainElements[0].Certificate; + + // Note that the overload of this method that takes a X509CertificateCollection will not pass on the chain to + // user callbacks like ServicePointManager.ServerCertificateValidationCallback which can cause issues along the line. + if (ValidateCertificate (remoteCertificate, managedChain)) + return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS; + else + return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED; + } + } catch (Exception ex) { // handle all exceptions and store them for later since we don't want to let them go through native code. + if (lastException == null) + lastException = ex; + return UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FATAL_ERROR; + } + } + + [MonoPInvokeCallback (typeof (UnityTls.unitytls_tlsctx_certificate_callback))] + static private void CertificateCallback (void* userData, UnityTls.unitytls_tlsctx* ctx, Int8* cn, size_t cnLen, UnityTls.unitytls_x509name* caList, size_t caListLen, UnityTls.unitytls_x509list_ref* chain, UnityTls.unitytls_key_ref* key, UnityTls.unitytls_errorstate* errorState) + { + var handle = (GCHandle)(IntPtr)userData; + var context = (UnityTlsContext)handle.Target; + context.CertificateCallback (ctx, cn, cnLen, caList, caListLen, chain, key, errorState); + } + + private void CertificateCallback (UnityTls.unitytls_tlsctx* ctx, Int8* cn, size_t cnLen, UnityTls.unitytls_x509name* caList, size_t caListLen, UnityTls.unitytls_x509list_ref* chain, UnityTls.unitytls_key_ref* key, UnityTls.unitytls_errorstate* errorState) + { + try { + if (remoteCertificate == null) + throw new TlsException (AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server."); + + localClientCertificate = SelectClientCertificate (remoteCertificate, null); + + if (localClientCertificate == null) { + *chain = new UnityTls.unitytls_x509list_ref { handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE }; + *key = new UnityTls.unitytls_key_ref { handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE }; + } else { + // Need to create native objects for client chain/key. Need to keep them cached. + // Make sure we don't have old native objects still around. + UnityTls.NativeInterface.unitytls_x509list_free (requestedClientCertChain); + UnityTls.NativeInterface.unitytls_key_free (requestedClientKey); + + ExtractNativeKeyAndChainFromManagedCertificate(localClientCertificate, errorState, out requestedClientCertChain, out requestedClientKey); + *chain = UnityTls.NativeInterface.unitytls_x509list_get_ref (requestedClientCertChain, errorState); + *key = UnityTls.NativeInterface.unitytls_key_get_ref (requestedClientKey, errorState); + } + + Unity.Debug.CheckAndThrow (*errorState, "Failed to retrieve certificates on request.", AlertDescription.HandshakeFailure); + } catch (Exception ex) { // handle all exceptions and store them for later since we don't want to let them go through native code. + UnityTls.NativeInterface.unitytls_errorstate_raise_error (errorState, UnityTls.unitytls_error_code.UNITYTLS_USER_UNKNOWN_ERROR); + if (lastException == null) + lastException = ex; + } + } + + [MonoPInvokeCallback (typeof (UnityTls.unitytls_tlsctx_trace_callback))] + static private void TraceCallback (void* userData, UnityTls.unitytls_tlsctx* ctx, byte* traceMessage, size_t traceMessageLen) + { + string message = Encoding.UTF8.GetString (traceMessage, (int)traceMessageLen); + System.Console.Write (message); + } + } +} +#endif \ No newline at end of file diff --git a/mcs/class/System/Mono.UnityTls/UnityTlsConversions.cs b/mcs/class/System/Mono.UnityTls/UnityTlsConversions.cs new file mode 100644 index 000000000000..59aff9bd45e7 --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/UnityTlsConversions.cs @@ -0,0 +1,132 @@ +#if SECURITY_DEP + +#if MONO_SECURITY_ALIAS +extern alias MonoSecurity; +using MonoSecurity::Mono.Security.Interface; +#else +using Mono.Security.Interface; +#endif + +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + +namespace Mono.Unity +{ + internal static class UnityTlsConversions + { + public static UnityTls.unitytls_protocol GetMinProtocol (SslProtocols protocols) + { + if (protocols.HasFlag (SslProtocols.Tls)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_0; + if (protocols.HasFlag (SslProtocols.Tls11)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_1; + if (protocols.HasFlag (SslProtocols.Tls12)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_2; + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_2; // Behavior as in AppleTlsContext + } + + public static UnityTls.unitytls_protocol GetMaxProtocol (SslProtocols protocols) + { + if (protocols.HasFlag (SslProtocols.Tls12)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_2; + if (protocols.HasFlag (SslProtocols.Tls11)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_1; + if (protocols.HasFlag (SslProtocols.Tls)) + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_0; + return UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_0; // Behavior as in AppleTlsContext + } + + public static TlsProtocols ConvertProtocolVersion(UnityTls.unitytls_protocol protocol) + { + switch (protocol) + { + case UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_0: + return TlsProtocols.Tls10; + case UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_1: + return TlsProtocols.Tls11; + case UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_TLS_1_2: + return TlsProtocols.Tls12; + case UnityTls.unitytls_protocol.UNITYTLS_PROTOCOL_INVALID: + return TlsProtocols.Zero; + } + return TlsProtocols.Zero; + } + + public static AlertDescription VerifyResultToAlertDescription (UnityTls.unitytls_x509verify_result verifyResult, AlertDescription defaultAlert = AlertDescription.InternalError) + { + if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FATAL_ERROR) + return AlertDescription.CertificateUnknown; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_EXPIRED)) + return AlertDescription.CertificateExpired; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_REVOKED)) + return AlertDescription.CertificateRevoked; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_CN_MISMATCH)) + return AlertDescription.UnknownCA; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED)) + return AlertDescription.CertificateUnknown; + + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR1)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR2)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR2)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR3)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR4)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR5)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR6)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR7)) + return AlertDescription.UserCancelled; + else if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_USER_ERROR8)) + return AlertDescription.UserCancelled; + + return defaultAlert; + } + + public static MonoSslPolicyErrors VerifyResultToPolicyErrror (UnityTls.unitytls_x509verify_result verifyResult) + { + // First, check "non-flags" + if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS) + return MonoSslPolicyErrors.None; + else if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FATAL_ERROR) + return MonoSslPolicyErrors.RemoteCertificateChainErrors; + + MonoSslPolicyErrors error = MonoSslPolicyErrors.None; + if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_CN_MISMATCH)) + error |= MonoSslPolicyErrors.RemoteCertificateNameMismatch; + // Anything else translates to MonoSslPolicyErrors.RemoteCertificateChainErrors. So if it is not the only flag, add it. + if (verifyResult != UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_CN_MISMATCH) + error |= MonoSslPolicyErrors.RemoteCertificateChainErrors; + return error; + } + + public static X509ChainStatusFlags VerifyResultToChainStatus (UnityTls.unitytls_x509verify_result verifyResult) + { + // First, check "non-flags" + if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS) + return X509ChainStatusFlags.NoError; + else if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FATAL_ERROR) + return X509ChainStatusFlags.UntrustedRoot; // Inaccurate, throw exception instead? + + // Yes, we ignore user error flags here. They still affect if a chain is accepted, but they are not status flags of the chain! + X509ChainStatusFlags error = X509ChainStatusFlags.NoError; + if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_EXPIRED)) + error |= X509ChainStatusFlags.NotTimeValid; + if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_REVOKED)) + error |= X509ChainStatusFlags.Revoked; + if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_CN_MISMATCH)) + // Unclear what to return, behaving like Mono's BTLS impl + // https://github.com/mono/mono/blob/1553889bc54f87060158febca7e6b8b9910975f8/mcs/class/System/Mono.Btls/MonoBtlsProvider.cs#L312 + error |= X509ChainStatusFlags.UntrustedRoot; + if (verifyResult.HasFlag (UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED)) + error |= X509ChainStatusFlags.UntrustedRoot; + + return error; + } + } +} +#endif \ No newline at end of file diff --git a/mcs/class/System/Mono.UnityTls/UnityTlsProvider.cs b/mcs/class/System/Mono.UnityTls/UnityTlsProvider.cs new file mode 100644 index 000000000000..877ca24a8642 --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/UnityTlsProvider.cs @@ -0,0 +1,145 @@ +#if SECURITY_DEP +#if MONO_SECURITY_ALIAS +extern alias MonoSecurity; +#endif + +using System; +using System.Text; +using System.IO; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + +using MNS = Mono.Net.Security; +#if MONO_SECURITY_ALIAS +using MonoSecurity::Mono.Security.Interface; +#else +using Mono.Security.Interface; +#endif + +using size_t = System.IntPtr; + +namespace Mono.Unity +{ + unsafe internal class UnityTlsProvider : MonoTlsProvider + { + public override string Name { + get { return "unitytls"; } + } + + public override Guid ID => MNS.MonoTlsProviderFactory.UnityTlsId; + public override bool SupportsSslStream => true; + public override bool SupportsMonoExtensions => true; + public override bool SupportsConnectionInfo => true; + internal override bool SupportsCleanShutdown => true; + public override SslProtocols SupportedProtocols => SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; + + public override IMonoSslStream CreateSslStream ( + Stream innerStream, bool leaveInnerStreamOpen, + MonoTlsSettings settings = null) + { + return SslStream.CreateMonoSslStream (innerStream, leaveInnerStreamOpen, this, settings); + } + + internal override IMonoSslStream CreateSslStreamInternal ( + SslStream sslStream, Stream innerStream, bool leaveInnerStreamOpen, + MonoTlsSettings settings) + { + return new UnityTlsStream (innerStream, leaveInnerStreamOpen, sslStream, settings, this); + } + + internal override bool ValidateCertificate ( + ICertificateValidator2 validator, string targetHost, bool serverMode, + X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain, + ref MonoSslPolicyErrors errors, ref int status11) + { + var errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + + var unityTlsChainImpl = chain.Impl as X509ChainImplUnityTls; + if (unityTlsChainImpl == null) + { + if (certificates == null || certificates.Count == 0) { + errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable; + return false; + } + + if (wantsChain) + chain = MNS.SystemCertificateValidator.CreateX509Chain (certificates); + } + else + { + var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (unityTlsChainImpl.NativeCertificateChain, (size_t)0, &errorState); + if (cert.handle == UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE) { + errors |= MonoSslPolicyErrors.RemoteCertificateNotAvailable; + return false; + } + } + + // fixup targetHost name by removing port + if (!string.IsNullOrEmpty (targetHost)) { + var pos = targetHost.IndexOf (':'); + if (pos > 0) + targetHost = targetHost.Substring (0, pos); + } + + // convert cert to native or extract from unityTlsChainImpl. + var result = UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_NOT_DONE; + UnityTls.unitytls_x509list* certificatesNative = null; + try + { + // Things the validator provides that we might want to make use of here: + //validator.Settings.CheckCertificateName // not used by mono? + //validator.Settings.CheckCertificateRevocationStatus // not used by mono? + //validator.Settings.CertificateValidationTime + //validator.Settings.CertificateSearchPaths // currently only used by MonoBtlsProvider + + UnityTls.unitytls_x509list_ref certificatesNativeRef; + if (unityTlsChainImpl == null) + { + certificatesNative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState); + CertHelper.AddCertificatesToNativeChain (certificatesNative, certificates, &errorState); + certificatesNativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (certificatesNative, &errorState); + } + else + certificatesNativeRef = unityTlsChainImpl.NativeCertificateChain; + + var targetHostUtf8 = Encoding.UTF8.GetBytes (targetHost); + + if (validator.Settings.TrustAnchors != null) { + UnityTls.unitytls_x509list* trustCAnative = null; + try + { + trustCAnative = UnityTls.NativeInterface.unitytls_x509list_create (&errorState); + CertHelper.AddCertificatesToNativeChain (trustCAnative, validator.Settings.TrustAnchors, &errorState); + var trustCAnativeRef = UnityTls.NativeInterface.unitytls_x509list_get_ref (trustCAnative, &errorState); + + fixed (byte* targetHostUtf8Ptr = targetHostUtf8) { + result = UnityTls.NativeInterface.unitytls_x509verify_explicit_ca (certificatesNativeRef, trustCAnativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState); + } + } + finally { + UnityTls.NativeInterface.unitytls_x509list_free (trustCAnative); + } + } else { + fixed (byte* targetHostUtf8Ptr = targetHostUtf8) { + result = UnityTls.NativeInterface.unitytls_x509verify_default_ca (certificatesNativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState); + } + } + } + finally { + UnityTls.NativeInterface.unitytls_x509list_free (certificatesNative); + } + + errors = UnityTlsConversions.VerifyResultToPolicyErrror(result); + // There should be a status per certificate, but once again we're following closely the BTLS implementation + // https://github.com/mono/mono/blob/1553889bc54f87060158febca7e6b8b9910975f8/mcs/class/System/Mono.Btls/MonoBtlsProvider.cs#L180 + // which also provides only a single status for the entire chain. + // It is notoriously tricky to implement in OpenSSL to get a status for all invididual certificates without finishing the handshake in the process. + // This is partially the reason why unitytls_x509verify_X doesn't expose it (TODO!) and likely the reason Mono's BTLS impl ignores this. + unityTlsChainImpl?.AddStatus(UnityTlsConversions.VerifyResultToChainStatus(result)); + return result == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS && + errorState.code == UnityTls.unitytls_error_code.UNITYTLS_SUCCESS; + } + } +} +#endif diff --git a/mcs/class/System/Mono.UnityTls/UnityTlsStream.cs b/mcs/class/System/Mono.UnityTls/UnityTlsStream.cs new file mode 100644 index 000000000000..9f99ae8e6a2d --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/UnityTlsStream.cs @@ -0,0 +1,41 @@ +#if SECURITY_DEP +#if MONO_SECURITY_ALIAS +extern alias MonoSecurity; +#endif + +using System.IO; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Security.Authentication; + +#if MONO_SECURITY_ALIAS +using MonoSecurity::Mono.Security.Interface; +#else +using Mono.Security.Interface; +#endif + +using MNS = Mono.Net.Security; + +namespace Mono.Unity +{ + class UnityTlsStream : MNS.MobileAuthenticatedStream + { + public UnityTlsStream (Stream innerStream, bool leaveInnerStreamOpen, SslStream owner, + MonoTlsSettings settings, MonoTlsProvider provider) + : base (innerStream, leaveInnerStreamOpen, owner, settings, provider) + { + } + + protected override MNS.MobileTlsContext CreateContext ( + bool serverMode, string targetHost, SslProtocols enabledProtocols, + X509Certificate serverCertificate, X509CertificateCollection clientCertificates, + bool askForClientCert) + { + return new UnityTlsContext ( + this, serverMode, targetHost, + enabledProtocols, serverCertificate, + clientCertificates, askForClientCert); + } + } +} +#endif \ No newline at end of file diff --git a/mcs/class/System/Mono.UnityTls/X509ChainImplUnityTls.cs b/mcs/class/System/Mono.UnityTls/X509ChainImplUnityTls.cs new file mode 100644 index 000000000000..f3b120b39275 --- /dev/null +++ b/mcs/class/System/Mono.UnityTls/X509ChainImplUnityTls.cs @@ -0,0 +1,100 @@ +#if SECURITY_DEP + +using System; +using System.Collections.Generic; +using System.Text; +using System.Security; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using size_t = System.IntPtr; + +namespace Mono.Unity +{ + // Follows mostly X509ChainImplBtls + class X509ChainImplUnityTls : X509ChainImpl + { + private X509ChainElementCollection elements; + private UnityTls.unitytls_x509list_ref nativeCertificateChain; + private X509ChainPolicy policy = new X509ChainPolicy (); + private List chainStatusList; + + internal X509ChainImplUnityTls (UnityTls.unitytls_x509list_ref nativeCertificateChain) + { + this.elements = null; + this.nativeCertificateChain = nativeCertificateChain; + } + + public override bool IsValid { + get { return nativeCertificateChain.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; } + } + + public override IntPtr Handle { + get { return new IntPtr((long)nativeCertificateChain.handle); } + } + + internal UnityTls.unitytls_x509list_ref NativeCertificateChain => nativeCertificateChain; + + public override X509ChainElementCollection ChainElements { + get { + ThrowIfContextInvalid (); + if (elements != null) + return elements; + + unsafe + { + elements = new X509ChainElementCollection (); + UnityTls.unitytls_errorstate errorState = UnityTls.NativeInterface.unitytls_errorstate_create (); + var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)0, &errorState); + for (int i = 0; cert.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; ++i) { + size_t certBufferSize = UnityTls.NativeInterface.unitytls_x509_export_der (cert, null, (size_t)0, &errorState); + var certBuffer = new byte[(int)certBufferSize]; // Need to reallocate every time since X509Certificate constructor takes no length but only a byte array. + fixed(byte* certBufferPtr = certBuffer) { + UnityTls.NativeInterface.unitytls_x509_export_der (cert, certBufferPtr, certBufferSize, &errorState); + } + elements.Add (new X509Certificate2 (certBuffer)); + + cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, (size_t)i, &errorState); + } + } + + return elements; + } + } + + public override X509ChainPolicy ChainPolicy { + get { return policy; } + set { policy = value; } + } + + public override X509ChainStatus[] ChainStatus => chainStatusList?.ToArray() ?? new X509ChainStatus[0]; + + public void AddStatus (X509ChainStatusFlags errorCode) + { + if (chainStatusList == null) + chainStatusList = new List(); + chainStatusList.Add (new X509ChainStatus(errorCode)); + } + + public override bool Build (X509Certificate2 certificate) + { + return false; + } + + public override void Reset () + { + if (elements != null) { + nativeCertificateChain.handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; + elements.Clear (); + elements = null; + } + } + + protected override void Dispose (bool disposing) + { + Reset(); + base.Dispose (disposing); + } + } +} + +#endif diff --git a/mcs/class/System/Mono.Util/MonoPInvokeCallbackAttribute.cs b/mcs/class/System/Mono.Util/MonoPInvokeCallbackAttribute.cs index 72acfed3d502..1e7130f58f8f 100644 --- a/mcs/class/System/Mono.Util/MonoPInvokeCallbackAttribute.cs +++ b/mcs/class/System/Mono.Util/MonoPInvokeCallbackAttribute.cs @@ -28,6 +28,7 @@ namespace Mono.Util { [Conditional("MONOTOUCH")] [Conditional("FULL_AOT_RUNTIME")] + [Conditional("UNITY")] [AttributeUsage (AttributeTargets.Method)] sealed class MonoPInvokeCallbackAttribute : Attribute { diff --git a/mcs/class/System/ReferenceSources/AutoWebProxyScriptEngine.cs b/mcs/class/System/ReferenceSources/AutoWebProxyScriptEngine.cs index f3b840e8f7f5..b667bdc9792b 100644 --- a/mcs/class/System/ReferenceSources/AutoWebProxyScriptEngine.cs +++ b/mcs/class/System/ReferenceSources/AutoWebProxyScriptEngine.cs @@ -46,14 +46,22 @@ public WebProxyData GetWebProxyData () WebProxyData data; // TODO: Could re-use some pieces from _AutoWebProxyScriptEngine.cs - if (IsWindows ()) { - data = InitializeRegistryGlobalProxy (); + try { + if (IsWindows ()) { + data = InitializeRegistryGlobalProxy (); + if (data != null) + return data; + } + + data = ReadEnvVariables (); if (data != null) return data; } + catch (DllNotFoundException) { + // This path will be hit on UWP since we're not allowed to read from registry + } - data = ReadEnvVariables (); - return data ?? new WebProxyData (); + return new WebProxyData (); } WebProxyData ReadEnvVariables () diff --git a/mcs/class/System/ReferenceSources/SR2.cs b/mcs/class/System/ReferenceSources/SR2.cs index 896362d98a4b..72c27496c3bf 100644 --- a/mcs/class/System/ReferenceSources/SR2.cs +++ b/mcs/class/System/ReferenceSources/SR2.cs @@ -15,4 +15,6 @@ partial class SR public const string net_log_set_socketoption_reuseport_default_on = "net_log_set_socketoption_reuseport_default_on"; public const string net_log_set_socketoption_reuseport_not_supported = "net_log_set_socketoption_reuseport_not_supported"; public const string net_log_set_socketoption_reuseport = "net_log_set_socketoption_reuseport"; + + public const string net_reqaborted = "The request was aborted: The request was canceled."; } diff --git a/mcs/class/System/ReferenceSources/SecureStringHelper.cs b/mcs/class/System/ReferenceSources/SecureStringHelper.cs index 31372e2cc6cc..de5ac5809fa1 100644 --- a/mcs/class/System/ReferenceSources/SecureStringHelper.cs +++ b/mcs/class/System/ReferenceSources/SecureStringHelper.cs @@ -14,7 +14,18 @@ internal static string CreateString(SecureString secureString) if (secureString == null || secureString.Length == 0) return String.Empty; - +#if MONO + try + { + bstr = Marshal.SecureStringToGlobalAllocUnicode(secureString); + plainString = Marshal.PtrToStringUni(bstr); + } + finally + { + if (bstr != IntPtr.Zero) + Marshal.ZeroFreeGlobalAllocUnicode(bstr); + } +#else try { bstr = Marshal.SecureStringToBSTR(secureString); @@ -25,6 +36,7 @@ internal static string CreateString(SecureString secureString) if (bstr != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstr); } +#endif return plainString; } diff --git a/mcs/class/System/ReferenceSources/SettingsSectionInternal.cs b/mcs/class/System/ReferenceSources/SettingsSectionInternal.cs index e7719c1afaad..c3bda137bfa7 100644 --- a/mcs/class/System/ReferenceSources/SettingsSectionInternal.cs +++ b/mcs/class/System/ReferenceSources/SettingsSectionInternal.cs @@ -30,6 +30,7 @@ internal static SettingsSectionInternal Section { internal bool Ipv6Enabled { get { +#if !UNITY #if CONFIGURATION_DEP && !MOBILE try { var config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings"); @@ -37,6 +38,7 @@ internal bool Ipv6Enabled { return config.Ipv6.Enabled; } catch { } +#endif #endif return true; diff --git a/mcs/class/System/ReferenceSources/Win32Exception.cs b/mcs/class/System/ReferenceSources/Win32Exception.cs index a18b9fba8f69..f70f5e46474c 100644 --- a/mcs/class/System/ReferenceSources/Win32Exception.cs +++ b/mcs/class/System/ReferenceSources/Win32Exception.cs @@ -3,6 +3,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +#if UNITY +using System.Collections.Generic; +#endif namespace System.ComponentModel { @@ -11,25 +14,39 @@ partial class Win32Exception #if !MOBILE [DllImport ("Kernel32", CharSet = CharSet.Unicode)] static extern int FormatMessage(int dwFlags, IntPtr lpSource, uint dwMessageId, int dwLanguageId, - [Out] StringBuilder lpBuffer, int nSize, IntPtr[] arguments); + [Out] char[] lpBuffer, int nSize, IntPtr[] arguments); + + const int MAX_MESSAGE_LENGTH = 256; +#endif + +#if UNITY + static bool s_ErrorMessagesInitialized = false; + static Dictionary s_ErrorMessage = new Dictionary(); #endif internal static string GetErrorMessage (int error) { #if !MOBILE if (Environment.IsRunningOnWindows) { - StringBuilder sb = new StringBuilder (256); + char[] messageArray = new char[MAX_MESSAGE_LENGTH]; int result = FormatMessage (0x1200 /* FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM */, - IntPtr.Zero, (uint)error, 0, sb, sb.Capacity, null); + IntPtr.Zero, (uint)error, 0, messageArray, MAX_MESSAGE_LENGTH, null); if (result == 0) return "Error looking up error string"; - return sb.ToString (); + return new string(messageArray); } #endif - +#if UNITY + if (!s_ErrorMessagesInitialized) + InitializeErrorMessages(); + string message; + if (s_ErrorMessage.TryGetValue(error, out message)) + return message; + return string.Format ("mono-io-layer-error ({0})", error); +#else switch (error) { case 0: /* ERROR_SUCCESS */ return "Success"; case 2: /* ERROR_FILE_NOT_FOUND */ return "Cannot find the specified file"; @@ -1738,6 +1755,1774 @@ internal static string GetErrorMessage (int error) default: return string.Format ("mono-io-layer-error ({0})", error); } +#endif // UNITY + } + +#if UNITY + static void InitializeErrorMessages() + { + if (s_ErrorMessagesInitialized) + return; + + lock (s_ErrorMessage) + { + if (s_ErrorMessagesInitialized) + return; + + // The Unity WebGL build has a nested loop limit per method. + // These many error messages go beyond that limit, so break them + // up into a number of smaller functions. + InitializeErrorMessages1(); +#if !MOBILE + InitializeErrorMessages2(); + InitializeErrorMessages3(); + InitializeErrorMessages4(); + InitializeErrorMessages5(); + InitializeErrorMessages6(); + InitializeErrorMessages7(); + InitializeErrorMessages8(); + InitializeErrorMessages9(); + InitializeErrorMessages10(); + InitializeErrorMessages11(); + InitializeErrorMessages12(); + InitializeErrorMessages13(); + InitializeErrorMessages14(); + InitializeErrorMessages15(); +#endif // MOBILE + s_ErrorMessagesInitialized = true; + } + } + + public static void InitializeErrorMessages1() + { + s_ErrorMessage.Add(10036, "Operation in progress"); /* WSAEINPROGRESS */ + s_ErrorMessage.Add(10037, "Operation already in progress"); /* WSAEALREADY */ + s_ErrorMessage.Add(10038, "The descriptor is not a socket"); /* WSAENOTSOCK */ + s_ErrorMessage.Add(10039, "Destination address required"); /* WSAEDESTADDRREQ */ + s_ErrorMessage.Add(10040, "Message too long"); /* WSAEMSGSIZE */ + s_ErrorMessage.Add(10041, "Protocol wrong type for socket"); /* WSAEPROTOTYPE */ + s_ErrorMessage.Add(10042, "Protocol option not supported"); /* WSAENOPROTOOPT */ + s_ErrorMessage.Add(10043, "Protocol not supported"); /* WSAEPROTONOSUPPORT */ + s_ErrorMessage.Add(10044, "Socket not supported"); /* WSAESOCKTNOSUPPORT */ + s_ErrorMessage.Add(10045, "Operation not supported"); /* WSAEOPNOTSUPP */ + s_ErrorMessage.Add(10046, "Protocol family not supported"); /* WSAEPFNOSUPPORT */ + s_ErrorMessage.Add(10047, "An address incompatible with the requested protocol was used"); /* WSAEAFNOSUPPORT */ + s_ErrorMessage.Add(10048, "Address already in use"); /* WSAEADDRINUSE */ + s_ErrorMessage.Add(10049, "The requested address is not valid in this context"); /* WSAEADDRNOTAVAIL */ + s_ErrorMessage.Add(10050, "Network subsystem is down"); /* WSAENETDOWN */ + s_ErrorMessage.Add(10051, "Network is unreachable"); /* WSAENETUNREACH */ + s_ErrorMessage.Add(10052, "Connection broken, keep-alive detected a problem"); /* WSAENETRESET */ + s_ErrorMessage.Add(10053, "An established connection was aborted in your host machine."); /* WSAECONNABORTED */ + s_ErrorMessage.Add(10054, "Connection reset by peer"); /* WSAECONNRESET */ + s_ErrorMessage.Add(10055, "Not enough buffer space is available"); /* WSAENOBUFS */ + s_ErrorMessage.Add(10056, "Socket is already connected"); /* WSAEISCONN */ + s_ErrorMessage.Add(10057, "The socket is not connected"); /* WSAENOTCONN */ + s_ErrorMessage.Add(10058, "The socket has been shut down"); /* WSAESHUTDOWN */ + s_ErrorMessage.Add(10059, "Too many references: cannot splice"); /* WSAETOOMANYREFS */ + s_ErrorMessage.Add(10060, "Connection timed out"); /* WSAETIMEDOUT */ + s_ErrorMessage.Add(10061, "Connection refused"); /* WSAECONNREFUSED */ + s_ErrorMessage.Add(10062, "Too many symbolic links encountered"); /* WSAELOOP */ + s_ErrorMessage.Add(10063, "File name too long"); /* WSAENAMETOOLONG */ + s_ErrorMessage.Add(10064, "Host is down"); /* WSAEHOSTDOWN */ + s_ErrorMessage.Add(10065, "No route to host"); /* WSAEHOSTUNREACH */ + s_ErrorMessage.Add(10066, "Directory not empty"); /* WSAENOTEMPTY */ + s_ErrorMessage.Add(10067, "EPROCLIM"); /* WSAEPROCLIM */ + s_ErrorMessage.Add(10068, "Too many users"); /* WSAEUSERS */ + s_ErrorMessage.Add(10069, "Quota exceeded"); /* WSAEDQUOT */ + s_ErrorMessage.Add(10070, "Stale NFS file handle"); /* WSAESTALE */ + s_ErrorMessage.Add(10071, "Object is remote"); /* WSAEREMOTE */ + s_ErrorMessage.Add(10091, "SYSNOTREADY"); /* WSASYSNOTREADY */ + s_ErrorMessage.Add(10092, "VERNOTSUPPORTED"); /* WSAVERNOTSUPPORTED */ + s_ErrorMessage.Add(10093, "Winsock not initialised"); /* WSANOTINITIALISED */ + s_ErrorMessage.Add(10101, "EDISCON"); /* WSAEDISCON */ + s_ErrorMessage.Add(10102, "ENOMORE"); /* WSAENOMORE */ + s_ErrorMessage.Add(10103, "Operation canceled"); /* WSAECANCELLED */ + s_ErrorMessage.Add(10104, "EINVALIDPROCTABLE"); /* WSAEINVALIDPROCTABLE */ + s_ErrorMessage.Add(10105, "EINVALIDPROVIDER"); /* WSAEINVALIDPROVIDER */ + s_ErrorMessage.Add(10106, "EPROVIDERFAILEDINIT"); /* WSAEPROVIDERFAILEDINIT */ + s_ErrorMessage.Add(10107, "System call failed"); /* WSASYSCALLFAILURE */ + s_ErrorMessage.Add(10108, "SERVICE_NOT_FOUND"); /* WSASERVICE_NOT_FOUND */ + s_ErrorMessage.Add(10109, "TYPE_NOT_FOUND"); /* WSATYPE_NOT_FOUND */ + s_ErrorMessage.Add(10112, "EREFUSED"); /* WSAEREFUSED */ + s_ErrorMessage.Add(11001, "No such host is known"); /* WSAHOST_NOT_FOUND */ + s_ErrorMessage.Add(11002, "A temporary error occurred on an authoritative name server. Try again later."); /* WSATRY_AGAIN */ + s_ErrorMessage.Add(11003, "No recovery"); /* WSANO_RECOVERY */ + s_ErrorMessage.Add(11004, "No data"); /* WSANO_DATA */ + } + + public static void InitializeErrorMessages2() + { + s_ErrorMessage.Add(1, "Invalid function"); /* ERROR_INVALID_FUNCTION */ + s_ErrorMessage.Add(7, "Arena trashed"); /* ERROR_ARENA_TRASHED */ + s_ErrorMessage.Add(8, "Not enough memory"); /* ERROR_NOT_ENOUGH_MEMORY */ + s_ErrorMessage.Add(9, "Invalid block"); /* ERROR_INVALID_BLOCK */ + s_ErrorMessage.Add(10, "Bad environment"); /* ERROR_BAD_ENVIRONMENT */ + s_ErrorMessage.Add(11, "Bad format"); /* ERROR_BAD_FORMAT */ + s_ErrorMessage.Add(12, "Invalid access"); /* ERROR_INVALID_ACCESS */ + s_ErrorMessage.Add(15, "Invalid drive"); /* ERROR_INVALID_DRIVE */ + s_ErrorMessage.Add(16, "Current directory"); /* ERROR_CURRENT_DIRECTORY */ + s_ErrorMessage.Add(19, "Write protect"); /* ERROR_WRITE_PROTECT */ + s_ErrorMessage.Add(20, "Bad unit"); /* ERROR_BAD_UNIT */ + s_ErrorMessage.Add(21, "Not ready"); /* ERROR_NOT_READY */ + s_ErrorMessage.Add(22, "Bad command"); /* ERROR_BAD_COMMAND */ + s_ErrorMessage.Add(23, "CRC"); /* ERROR_CRC */ + s_ErrorMessage.Add(25, "Seek"); /* ERROR_SEEK */ + s_ErrorMessage.Add(26, "Not DOS disk"); /* ERROR_NOT_DOS_DISK */ + s_ErrorMessage.Add(27, "Sector not found"); /* ERROR_SECTOR_NOT_FOUND */ + s_ErrorMessage.Add(28, "Out of paper"); /* ERROR_OUT_OF_PAPER */ + s_ErrorMessage.Add(29, "Write fault"); /* ERROR_WRITE_FAULT */ + s_ErrorMessage.Add(30, "Read fault"); /* ERROR_READ_FAULT */ + s_ErrorMessage.Add(34, "Wrong disk"); /* ERROR_WRONG_DISK */ + s_ErrorMessage.Add(36, "Sharing buffer exceeded"); /* ERROR_SHARING_BUFFER_EXCEEDED */ + s_ErrorMessage.Add(38, "Handle EOF"); /* ERROR_HANDLE_EOF */ + s_ErrorMessage.Add(39, "Handle disk full"); /* ERROR_HANDLE_DISK_FULL */ + s_ErrorMessage.Add(51, "Rem not list"); /* ERROR_REM_NOT_LIST */ + s_ErrorMessage.Add(52, "Duplicate name"); /* ERROR_DUP_NAME */ + s_ErrorMessage.Add(53, "Bad netpath"); /* ERROR_BAD_NETPATH */ + s_ErrorMessage.Add(54, "Network busy"); /* ERROR_NETWORK_BUSY */ + s_ErrorMessage.Add(56, "Too many commands"); /* ERROR_TOO_MANY_CMDS */ + s_ErrorMessage.Add(57, "ADAP HDW error"); /* ERROR_ADAP_HDW_ERR */ + s_ErrorMessage.Add(58, "Bad net response"); /* ERROR_BAD_NET_RESP */ + s_ErrorMessage.Add(59, "Unexpected net error"); /* ERROR_UNEXP_NET_ERR */ + s_ErrorMessage.Add(60, "Bad rem adap"); /* ERROR_BAD_REM_ADAP */ + s_ErrorMessage.Add(61, "Print queue full"); /* ERROR_PRINTQ_FULL */ + s_ErrorMessage.Add(62, "No spool space"); /* ERROR_NO_SPOOL_SPACE */ + s_ErrorMessage.Add(63, "Print cancelled"); /* ERROR_PRINT_CANCELLED */ + s_ErrorMessage.Add(64, "Netname deleted"); /* ERROR_NETNAME_DELETED */ + s_ErrorMessage.Add(65, "Network access denied"); /* ERROR_NETWORK_ACCESS_DENIED */ + s_ErrorMessage.Add(66, "Bad device type"); /* ERROR_BAD_DEV_TYPE */ + s_ErrorMessage.Add(67, "Bad net name"); /* ERROR_BAD_NET_NAME */ + s_ErrorMessage.Add(68, "Too many names"); /* ERROR_TOO_MANY_NAMES */ + s_ErrorMessage.Add(69, "Too many sessions"); /* ERROR_TOO_MANY_SESS */ + s_ErrorMessage.Add(70, "Sharing paused"); /* ERROR_SHARING_PAUSED */ + s_ErrorMessage.Add(71, "Req not accep"); /* ERROR_REQ_NOT_ACCEP */ + s_ErrorMessage.Add(72, "Redir paused"); /* ERROR_REDIR_PAUSED */ + s_ErrorMessage.Add(80, "File exists"); /* ERROR_FILE_EXISTS */ + s_ErrorMessage.Add(82, "Cannot make"); /* ERROR_CANNOT_MAKE */ + s_ErrorMessage.Add(83, "Fail i24"); /* ERROR_FAIL_I24 */ + s_ErrorMessage.Add(84, "Out of structures"); /* ERROR_OUT_OF_STRUCTURES */ + s_ErrorMessage.Add(85, "Already assigned"); /* ERROR_ALREADY_ASSIGNED */ + s_ErrorMessage.Add(86, "Invalid password"); /* ERROR_INVALID_PASSWORD */ + s_ErrorMessage.Add(88, "Net write fault"); /* ERROR_NET_WRITE_FAULT */ + s_ErrorMessage.Add(89, "No proc slots"); /* ERROR_NO_PROC_SLOTS */ + s_ErrorMessage.Add(100, "Too many semaphores"); /* ERROR_TOO_MANY_SEMAPHORES */ + s_ErrorMessage.Add(101, "Exclusive semaphore already owned"); /* ERROR_EXCL_SEM_ALREADY_OWNED */ + s_ErrorMessage.Add(102, "Semaphore is set"); /* ERROR_SEM_IS_SET */ + s_ErrorMessage.Add(103, "Too many semaphore requests"); /* ERROR_TOO_MANY_SEM_REQUESTS */ + s_ErrorMessage.Add(104, "Invalid at interrupt time"); /* ERROR_INVALID_AT_INTERRUPT_TIME */ + s_ErrorMessage.Add(105, "Semaphore owner died"); /* ERROR_SEM_OWNER_DIED */ + s_ErrorMessage.Add(106, "Semaphore user limit"); /* ERROR_SEM_USER_LIMIT */ + s_ErrorMessage.Add(107, "Disk change"); /* ERROR_DISK_CHANGE */ + } + + public static void InitializeErrorMessages3() + { + s_ErrorMessage.Add(108, "Drive locked"); /* ERROR_DRIVE_LOCKED */ + s_ErrorMessage.Add(109, "Broken pipe"); /* ERROR_BROKEN_PIPE */ + s_ErrorMessage.Add(110, "Open failed"); /* ERROR_OPEN_FAILED */ + s_ErrorMessage.Add(111, "Buffer overflow"); /* ERROR_BUFFER_OVERFLOW */ + s_ErrorMessage.Add(112, "Disk full"); /* ERROR_DISK_FULL */ + s_ErrorMessage.Add(113, "No more search handles"); /* ERROR_NO_MORE_SEARCH_HANDLES */ + s_ErrorMessage.Add(114, "Invalid target handle"); /* ERROR_INVALID_TARGET_HANDLE */ + s_ErrorMessage.Add(117, "Invalid category"); /* ERROR_INVALID_CATEGORY */ + s_ErrorMessage.Add(118, "Invalid verify switch"); /* ERROR_INVALID_VERIFY_SWITCH */ + s_ErrorMessage.Add(119, "Bad driver level"); /* ERROR_BAD_DRIVER_LEVEL */ + s_ErrorMessage.Add(121, "Semaphore timeout"); /* ERROR_SEM_TIMEOUT */ + s_ErrorMessage.Add(122, "Insufficient buffer"); /* ERROR_INSUFFICIENT_BUFFER */ + s_ErrorMessage.Add(124, "Invalid level"); /* ERROR_INVALID_LEVEL */ + s_ErrorMessage.Add(125, "No volume label"); /* ERROR_NO_VOLUME_LABEL */ + s_ErrorMessage.Add(126, "Module not found"); /* ERROR_MOD_NOT_FOUND */ + s_ErrorMessage.Add(128, "Wait no children"); /* ERROR_WAIT_NO_CHILDREN */ + s_ErrorMessage.Add(129, "Child not complete"); /* ERROR_CHILD_NOT_COMPLETE */ + s_ErrorMessage.Add(130, "Direct access handle"); /* ERROR_DIRECT_ACCESS_HANDLE */ + s_ErrorMessage.Add(131, "Negative seek"); /* ERROR_NEGATIVE_SEEK */ + s_ErrorMessage.Add(132, "Seek on device"); /* ERROR_SEEK_ON_DEVICE */ + s_ErrorMessage.Add(133, "Is join target"); /* ERROR_IS_JOIN_TARGET */ + s_ErrorMessage.Add(134, "Is joined"); /* ERROR_IS_JOINED */ + s_ErrorMessage.Add(135, "Is substed"); /* ERROR_IS_SUBSTED */ + s_ErrorMessage.Add(136, "Not joined"); /* ERROR_NOT_JOINED */ + s_ErrorMessage.Add(137, "Not substed"); /* ERROR_NOT_SUBSTED */ + s_ErrorMessage.Add(138, "Join to join"); /* ERROR_JOIN_TO_JOIN */ + s_ErrorMessage.Add(139, "Subst to subst"); /* ERROR_SUBST_TO_SUBST */ + s_ErrorMessage.Add(140, "Join to subst"); /* ERROR_JOIN_TO_SUBST */ + s_ErrorMessage.Add(141, "Subst to join"); /* ERROR_SUBST_TO_JOIN */ + s_ErrorMessage.Add(142, "Busy drive"); /* ERROR_BUSY_DRIVE */ + s_ErrorMessage.Add(143, "Same drive"); /* ERROR_SAME_DRIVE */ + s_ErrorMessage.Add(144, "Directory not root"); /* ERROR_DIR_NOT_ROOT */ + s_ErrorMessage.Add(145, "Directory not empty"); /* ERROR_DIR_NOT_EMPTY */ + s_ErrorMessage.Add(146, "Is subst path"); /* ERROR_IS_SUBST_PATH */ + s_ErrorMessage.Add(147, "Is join path"); /* ERROR_IS_JOIN_PATH */ + s_ErrorMessage.Add(148, "Path busy"); /* ERROR_PATH_BUSY */ + s_ErrorMessage.Add(149, "Is subst target"); /* ERROR_IS_SUBST_TARGET */ + s_ErrorMessage.Add(150, "System trace"); /* ERROR_SYSTEM_TRACE */ + s_ErrorMessage.Add(151, "Invalid event count"); /* ERROR_INVALID_EVENT_COUNT */ + s_ErrorMessage.Add(152, "Too many muxwaiters"); /* ERROR_TOO_MANY_MUXWAITERS */ + s_ErrorMessage.Add(153, "Invalid list format"); /* ERROR_INVALID_LIST_FORMAT */ + s_ErrorMessage.Add(154, "Label too long"); /* ERROR_LABEL_TOO_LONG */ + s_ErrorMessage.Add(155, "Too many TCBs"); /* ERROR_TOO_MANY_TCBS */ + s_ErrorMessage.Add(156, "Signal refused"); /* ERROR_SIGNAL_REFUSED */ + s_ErrorMessage.Add(157, "Discarded"); /* ERROR_DISCARDED */ + s_ErrorMessage.Add(158, "Not locked"); /* ERROR_NOT_LOCKED */ + s_ErrorMessage.Add(159, "Bad thread ID addr"); /* ERROR_BAD_THREADID_ADDR */ + s_ErrorMessage.Add(160, "Bad arguments"); /* ERROR_BAD_ARGUMENTS */ + s_ErrorMessage.Add(161, "Bad pathname"); /* ERROR_BAD_PATHNAME */ + s_ErrorMessage.Add(162, "Signal pending"); /* ERROR_SIGNAL_PENDING */ + s_ErrorMessage.Add(164, "Max thrds reached"); /* ERROR_MAX_THRDS_REACHED */ + s_ErrorMessage.Add(167, "Lock failed"); /* ERROR_LOCK_FAILED */ + s_ErrorMessage.Add(170, "Busy"); /* ERROR_BUSY */ + s_ErrorMessage.Add(173, "Cancel violation"); /* ERROR_CANCEL_VIOLATION */ + s_ErrorMessage.Add(174, "Atomic locks not supported"); /* ERROR_ATOMIC_LOCKS_NOT_SUPPORTED */ + s_ErrorMessage.Add(180, "Invalid segment number"); /* ERROR_INVALID_SEGMENT_NUMBER */ + s_ErrorMessage.Add(182, "Invalid ordinal"); /* ERROR_INVALID_ORDINAL */ + s_ErrorMessage.Add(186, "Invalid flag number"); /* ERROR_INVALID_FLAG_NUMBER */ + s_ErrorMessage.Add(187, "Sem not found"); /* ERROR_SEM_NOT_FOUND */ + s_ErrorMessage.Add(188, "Invalid starting codeseg"); /* ERROR_INVALID_STARTING_CODESEG */ + s_ErrorMessage.Add(189, "Invalid stackseg"); /* ERROR_INVALID_STACKSEG */ + s_ErrorMessage.Add(190, "Invalid moduletype"); /* ERROR_INVALID_MODULETYPE */ + s_ErrorMessage.Add(191, "Invalid exe signature"); /* ERROR_INVALID_EXE_SIGNATURE */ + s_ErrorMessage.Add(192, "Exe marked invalid"); /* ERROR_EXE_MARKED_INVALID */ + s_ErrorMessage.Add(193, "Bad exe format"); /* ERROR_BAD_EXE_FORMAT */ + s_ErrorMessage.Add(194, "Iterated data exceeds 64k (and that should be enough for anybody!)"); /* ERROR_ITERATED_DATA_EXCEEDS_64k */ + s_ErrorMessage.Add(195, "Invalid minallocsize"); /* ERROR_INVALID_MINALLOCSIZE */ + s_ErrorMessage.Add(196, "Dynlink from invalid ring"); /* ERROR_DYNLINK_FROM_INVALID_RING */ + s_ErrorMessage.Add(197, "IOPL not enabled"); /* ERROR_IOPL_NOT_ENABLED */ + s_ErrorMessage.Add(198, "Invalid segdpl"); /* ERROR_INVALID_SEGDPL */ + s_ErrorMessage.Add(199, "Autodataseg exceeds 64k"); /* ERROR_AUTODATASEG_EXCEEDS_64k */ + s_ErrorMessage.Add(200, "Ring2seg must be movable"); /* ERROR_RING2SEG_MUST_BE_MOVABLE */ + s_ErrorMessage.Add(201, "Reloc chain exceeds seglim"); /* ERROR_RELOC_CHAIN_XEEDS_SEGLIM */ + s_ErrorMessage.Add(202, "Infloop in reloc chain"); /* ERROR_INFLOOP_IN_RELOC_CHAIN */ + s_ErrorMessage.Add(203, "Env var not found"); /* ERROR_ENVVAR_NOT_FOUND */ + s_ErrorMessage.Add(205, "No signal sent"); /* ERROR_NO_SIGNAL_SENT */ + s_ErrorMessage.Add(206, "Filename exceeds range"); /* ERROR_FILENAME_EXCED_RANGE */ + s_ErrorMessage.Add(207, "Ring2 stack in use"); /* ERROR_RING2_STACK_IN_USE */ + s_ErrorMessage.Add(208, "Meta expansion too long"); /* ERROR_META_EXPANSION_TOO_LONG */ + s_ErrorMessage.Add(209, "Invalid signal number"); /* ERROR_INVALID_SIGNAL_NUMBER */ + } + + public static void InitializeErrorMessages4() + { + s_ErrorMessage.Add(210, "Thread 1 inactive"); /* ERROR_THREAD_1_INACTIVE */ + s_ErrorMessage.Add(212, "Locked"); /* ERROR_LOCKED */ + s_ErrorMessage.Add(214, "Too many modules"); /* ERROR_TOO_MANY_MODULES */ + s_ErrorMessage.Add(215, "Nesting not allowed"); /* ERROR_NESTING_NOT_ALLOWED */ + s_ErrorMessage.Add(216, "Exe machine type mismatch"); /* ERROR_EXE_MACHINE_TYPE_MISMATCH */ + s_ErrorMessage.Add(230, "Bad pipe"); /* ERROR_BAD_PIPE */ + s_ErrorMessage.Add(231, "Pipe busy"); /* ERROR_PIPE_BUSY */ + s_ErrorMessage.Add(232, "No data"); /* ERROR_NO_DATA */ + s_ErrorMessage.Add(233, "Pipe not connected"); /* ERROR_PIPE_NOT_CONNECTED */ + s_ErrorMessage.Add(234, "More data"); /* ERROR_MORE_DATA */ + s_ErrorMessage.Add(240, "VC disconnected"); /* ERROR_VC_DISCONNECTED */ + s_ErrorMessage.Add(254, "Invalid EA name"); /* ERROR_INVALID_EA_NAME */ + s_ErrorMessage.Add(255, "EA list inconsistent"); /* ERROR_EA_LIST_INCONSISTENT */ + s_ErrorMessage.Add(258, "Wait timeout"); /* WAIT_TIMEOUT */ + s_ErrorMessage.Add(259, "No more items"); /* ERROR_NO_MORE_ITEMS */ + s_ErrorMessage.Add(266, "Cannot copy"); /* ERROR_CANNOT_COPY */ + s_ErrorMessage.Add(275, "EAS didnt fit"); /* ERROR_EAS_DIDNT_FIT */ + s_ErrorMessage.Add(276, "EA file corrupt"); /* ERROR_EA_FILE_CORRUPT */ + s_ErrorMessage.Add(277, "EA table full"); /* ERROR_EA_TABLE_FULL */ + s_ErrorMessage.Add(278, "Invalid EA handle"); /* ERROR_INVALID_EA_HANDLE */ + s_ErrorMessage.Add(282, "EAs not supported"); /* ERROR_EAS_NOT_SUPPORTED */ + s_ErrorMessage.Add(288, "Not owner"); /* ERROR_NOT_OWNER */ + s_ErrorMessage.Add(298, "Too many posts"); /* ERROR_TOO_MANY_POSTS */ + s_ErrorMessage.Add(299, "Partial copy"); /* ERROR_PARTIAL_COPY */ + s_ErrorMessage.Add(300, "Oplock not granted"); /* ERROR_OPLOCK_NOT_GRANTED */ + s_ErrorMessage.Add(301, "Invalid oplock protocol"); /* ERROR_INVALID_OPLOCK_PROTOCOL */ + s_ErrorMessage.Add(302, "Disk too fragmented"); /* ERROR_DISK_TOO_FRAGMENTED */ + s_ErrorMessage.Add(303, "Delete pending"); /* ERROR_DELETE_PENDING */ + s_ErrorMessage.Add(317, "Mr Mid not found"); /* ERROR_MR_MID_NOT_FOUND */ + s_ErrorMessage.Add(487, "Invalid address"); /* ERROR_INVALID_ADDRESS */ + s_ErrorMessage.Add(534, "Arithmetic overflow"); /* ERROR_ARITHMETIC_OVERFLOW */ + s_ErrorMessage.Add(535, "Pipe connected"); /* ERROR_PIPE_CONNECTED */ + s_ErrorMessage.Add(536, "Pipe listening"); /* ERROR_PIPE_LISTENING */ + s_ErrorMessage.Add(994, "EA access denied"); /* ERROR_EA_ACCESS_DENIED */ + s_ErrorMessage.Add(996, "IO incomplete"); /* ERROR_IO_INCOMPLETE */ + s_ErrorMessage.Add(997, "IO pending"); /* ERROR_IO_PENDING */ + s_ErrorMessage.Add(998, "No access"); /* ERROR_NOACCESS */ + s_ErrorMessage.Add(999, "Swap error"); /* ERROR_SWAPERROR */ + s_ErrorMessage.Add(1001, "Stack overflow"); /* ERROR_STACK_OVERFLOW */ + s_ErrorMessage.Add(1002, "Invalid message"); /* ERROR_INVALID_MESSAGE */ + s_ErrorMessage.Add(1003, "Can not complete"); /* ERROR_CAN_NOT_COMPLETE */ + s_ErrorMessage.Add(1004, "Invalid flags"); /* ERROR_INVALID_FLAGS */ + s_ErrorMessage.Add(1005, "Unrecognised volume"); /* ERROR_UNRECOGNIZED_VOLUME */ + s_ErrorMessage.Add(1006, "File invalid"); /* ERROR_FILE_INVALID */ + s_ErrorMessage.Add(1007, "Full screen mode"); /* ERROR_FULLSCREEN_MODE */ + s_ErrorMessage.Add(1008, "No token"); /* ERROR_NO_TOKEN */ + s_ErrorMessage.Add(1009, "Bad DB"); /* ERROR_BADDB */ + s_ErrorMessage.Add(1010, "Bad key"); /* ERROR_BADKEY */ + s_ErrorMessage.Add(1011, "Can't open"); /* ERROR_CANTOPEN */ + s_ErrorMessage.Add(1012, "Can't read"); /* ERROR_CANTREAD */ + s_ErrorMessage.Add(1013, "Can't write"); /* ERROR_CANTWRITE */ + s_ErrorMessage.Add(1014, "Registry recovered"); /* ERROR_REGISTRY_RECOVERED */ + s_ErrorMessage.Add(1015, "Registry corrupt"); /* ERROR_REGISTRY_CORRUPT */ + s_ErrorMessage.Add(1016, "Registry IO failed"); /* ERROR_REGISTRY_IO_FAILED */ + s_ErrorMessage.Add(1017, "Not registry file"); /* ERROR_NOT_REGISTRY_FILE */ + s_ErrorMessage.Add(1018, "Key deleted"); /* ERROR_KEY_DELETED */ + s_ErrorMessage.Add(1019, "No log space"); /* ERROR_NO_LOG_SPACE */ + s_ErrorMessage.Add(1020, "Key has children"); /* ERROR_KEY_HAS_CHILDREN */ + s_ErrorMessage.Add(1021, "Child must be volatile"); /* ERROR_CHILD_MUST_BE_VOLATILE */ + s_ErrorMessage.Add(1022, "Notify enum dir"); /* ERROR_NOTIFY_ENUM_DIR */ + s_ErrorMessage.Add(1051, "Dependent services running"); /* ERROR_DEPENDENT_SERVICES_RUNNING */ + s_ErrorMessage.Add(1052, "Invalid service control"); /* ERROR_INVALID_SERVICE_CONTROL */ + } + + public static void InitializeErrorMessages5() + { + s_ErrorMessage.Add(1053, "Service request timeout"); /* ERROR_SERVICE_REQUEST_TIMEOUT */ + s_ErrorMessage.Add(1054, "Service no thread"); /* ERROR_SERVICE_NO_THREAD */ + s_ErrorMessage.Add(1055, "Service database locked"); /* ERROR_SERVICE_DATABASE_LOCKED */ + s_ErrorMessage.Add(1056, "Service already running"); /* ERROR_SERVICE_ALREADY_RUNNING */ + s_ErrorMessage.Add(1057, "Invalid service account"); /* ERROR_INVALID_SERVICE_ACCOUNT */ + s_ErrorMessage.Add(1058, "Service disabled"); /* ERROR_SERVICE_DISABLED */ + s_ErrorMessage.Add(1059, "Circular dependency"); /* ERROR_CIRCULAR_DEPENDENCY */ + s_ErrorMessage.Add(1060, "Service does not exist"); /* ERROR_SERVICE_DOES_NOT_EXIST */ + s_ErrorMessage.Add(1061, "Service cannot accept ctrl"); /* ERROR_SERVICE_CANNOT_ACCEPT_CTRL */ + s_ErrorMessage.Add(1062, "Service not active"); /* ERROR_SERVICE_NOT_ACTIVE */ + s_ErrorMessage.Add(1063, "Failed service controller connect"); /* ERROR_FAILED_SERVICE_CONTROLLER_CONNECT */ + s_ErrorMessage.Add(1064, "Exception in service"); /* ERROR_EXCEPTION_IN_SERVICE */ + s_ErrorMessage.Add(1065, "Database does not exist"); /* ERROR_DATABASE_DOES_NOT_EXIST */ + s_ErrorMessage.Add(1066, "Service specific error"); /* ERROR_SERVICE_SPECIFIC_ERROR */ + s_ErrorMessage.Add(1067, "Process aborted"); /* ERROR_PROCESS_ABORTED */ + s_ErrorMessage.Add(1068, "Service dependency fail"); /* ERROR_SERVICE_DEPENDENCY_FAIL */ + s_ErrorMessage.Add(1069, "Service logon failed"); /* ERROR_SERVICE_LOGON_FAILED */ + s_ErrorMessage.Add(1070, "Service start hang"); /* ERROR_SERVICE_START_HANG */ + s_ErrorMessage.Add(1071, "Invalid service lock"); /* ERROR_INVALID_SERVICE_LOCK */ + s_ErrorMessage.Add(1072, "Service marked for delete"); /* ERROR_SERVICE_MARKED_FOR_DELETE */ + s_ErrorMessage.Add(1073, "Service exists"); /* ERROR_SERVICE_EXISTS */ + s_ErrorMessage.Add(1074, "Already running lkg"); /* ERROR_ALREADY_RUNNING_LKG */ + s_ErrorMessage.Add(1075, "Service dependency deleted"); /* ERROR_SERVICE_DEPENDENCY_DELETED */ + s_ErrorMessage.Add(1076, "Boot already accepted"); /* ERROR_BOOT_ALREADY_ACCEPTED */ + s_ErrorMessage.Add(1077, "Service never started"); /* ERROR_SERVICE_NEVER_STARTED */ + s_ErrorMessage.Add(1078, "Duplicate service name"); /* ERROR_DUPLICATE_SERVICE_NAME */ + s_ErrorMessage.Add(1079, "Different service account"); /* ERROR_DIFFERENT_SERVICE_ACCOUNT */ + s_ErrorMessage.Add(1080, "Cannot detect driver failure"); /* ERROR_CANNOT_DETECT_DRIVER_FAILURE */ + s_ErrorMessage.Add(1081, "Cannot detect process abort"); /* ERROR_CANNOT_DETECT_PROCESS_ABORT */ + s_ErrorMessage.Add(1082, "No recovery program"); /* ERROR_NO_RECOVERY_PROGRAM */ + s_ErrorMessage.Add(1083, "Service not in exe"); /* ERROR_SERVICE_NOT_IN_EXE */ + s_ErrorMessage.Add(1084, "Not safeboot service"); /* ERROR_NOT_SAFEBOOT_SERVICE */ + s_ErrorMessage.Add(1100, "End of media"); /* ERROR_END_OF_MEDIA */ + s_ErrorMessage.Add(1101, "Filemark detected"); /* ERROR_FILEMARK_DETECTED */ + s_ErrorMessage.Add(1102, "Beginning of media"); /* ERROR_BEGINNING_OF_MEDIA */ + s_ErrorMessage.Add(1103, "Setmark detected"); /* ERROR_SETMARK_DETECTED */ + s_ErrorMessage.Add(1104, "No data detected"); /* ERROR_NO_DATA_DETECTED */ + s_ErrorMessage.Add(1105, "Partition failure"); /* ERROR_PARTITION_FAILURE */ + s_ErrorMessage.Add(1106, "Invalid block length"); /* ERROR_INVALID_BLOCK_LENGTH */ + s_ErrorMessage.Add(1107, "Device not partitioned"); /* ERROR_DEVICE_NOT_PARTITIONED */ + s_ErrorMessage.Add(1108, "Unable to lock media"); /* ERROR_UNABLE_TO_LOCK_MEDIA */ + s_ErrorMessage.Add(1109, "Unable to unload media"); /* ERROR_UNABLE_TO_UNLOAD_MEDIA */ + s_ErrorMessage.Add(1110, "Media changed"); /* ERROR_MEDIA_CHANGED */ + s_ErrorMessage.Add(1111, "Bus reset"); /* ERROR_BUS_RESET */ + s_ErrorMessage.Add(1112, "No media in drive"); /* ERROR_NO_MEDIA_IN_DRIVE */ + s_ErrorMessage.Add(1113, "No unicode translation"); /* ERROR_NO_UNICODE_TRANSLATION */ + s_ErrorMessage.Add(1114, "DLL init failed"); /* ERROR_DLL_INIT_FAILED */ + s_ErrorMessage.Add(1115, "Shutdown in progress"); /* ERROR_SHUTDOWN_IN_PROGRESS */ + s_ErrorMessage.Add(1116, "No shutdown in progress"); /* ERROR_NO_SHUTDOWN_IN_PROGRESS */ + s_ErrorMessage.Add(1117, "IO device"); /* ERROR_IO_DEVICE */ + s_ErrorMessage.Add(1118, "Serial IO device"); /* ERROR_SERIAL_NO_DEVICE */ + s_ErrorMessage.Add(1119, "IRQ busy"); /* ERROR_IRQ_BUSY */ + s_ErrorMessage.Add(1120, "More writes"); /* ERROR_MORE_WRITES */ + s_ErrorMessage.Add(1121, "Counter timeout"); /* ERROR_COUNTER_TIMEOUT */ + s_ErrorMessage.Add(1122, "Floppy ID mark not found"); /* ERROR_FLOPPY_ID_MARK_NOT_FOUND */ + s_ErrorMessage.Add(1123, "Floppy wrong cylinder"); /* ERROR_FLOPPY_WRONG_CYLINDER */ + s_ErrorMessage.Add(1124, "Floppy unknown error"); /* ERROR_FLOPPY_UNKNOWN_ERROR */ + s_ErrorMessage.Add(1125, "Floppy bad registers"); /* ERROR_FLOPPY_BAD_REGISTERS */ + s_ErrorMessage.Add(1126, "Disk recalibrate failed"); /* ERROR_DISK_RECALIBRATE_FAILED */ + s_ErrorMessage.Add(1127, "Disk operation failed"); /* ERROR_DISK_OPERATION_FAILED */ + s_ErrorMessage.Add(1128, "Disk reset failed"); /* ERROR_DISK_RESET_FAILED */ + s_ErrorMessage.Add(1129, "EOM overflow"); /* ERROR_EOM_OVERFLOW */ + s_ErrorMessage.Add(1130, "Not enough server memory"); /* ERROR_NOT_ENOUGH_SERVER_MEMORY */ + s_ErrorMessage.Add(1131, "Possible deadlock"); /* ERROR_POSSIBLE_DEADLOCK */ + s_ErrorMessage.Add(1132, "Mapped alignment"); /* ERROR_MAPPED_ALIGNMENT */ + s_ErrorMessage.Add(1140, "Set power state vetoed"); /* ERROR_SET_POWER_STATE_VETOED */ + s_ErrorMessage.Add(1141, "Set power state failed"); /* ERROR_SET_POWER_STATE_FAILED */ + s_ErrorMessage.Add(1142, "Too many links"); /* ERROR_TOO_MANY_LINKS */ + s_ErrorMessage.Add(1150, "Old win version"); /* ERROR_OLD_WIN_VERSION */ + s_ErrorMessage.Add(1151, "App wrong OS"); /* ERROR_APP_WRONG_OS */ + s_ErrorMessage.Add(1152, "Single instance app"); /* ERROR_SINGLE_INSTANCE_APP */ + s_ErrorMessage.Add(1153, "Rmode app"); /* ERROR_RMODE_APP */ + s_ErrorMessage.Add(1154, "Invalid DLL"); /* ERROR_INVALID_DLL */ + s_ErrorMessage.Add(1155, "No association"); /* ERROR_NO_ASSOCIATION */ + s_ErrorMessage.Add(1156, "DDE fail"); /* ERROR_DDE_FAIL */ + s_ErrorMessage.Add(1157, "DLL not found"); /* ERROR_DLL_NOT_FOUND */ + s_ErrorMessage.Add(1158, "No more user handles"); /* ERROR_NO_MORE_USER_HANDLES */ + s_ErrorMessage.Add(1159, "Message sync only"); /* ERROR_MESSAGE_SYNC_ONLY */ + s_ErrorMessage.Add(1160, "Source element empty"); /* ERROR_SOURCE_ELEMENT_EMPTY */ + s_ErrorMessage.Add(1161, "Destination element full"); /* ERROR_DESTINATION_ELEMENT_FULL */ + s_ErrorMessage.Add(1162, "Illegal element address"); /* ERROR_ILLEGAL_ELEMENT_ADDRESS */ + s_ErrorMessage.Add(1163, "Magazine not present"); /* ERROR_MAGAZINE_NOT_PRESENT */ + s_ErrorMessage.Add(1164, "Device reinitialization needed"); /* ERROR_DEVICE_REINITIALIZATION_NEEDED */ + s_ErrorMessage.Add(1165, "Device requires cleaning"); /* ERROR_DEVICE_REQUIRES_CLEANING */ + s_ErrorMessage.Add(1166, "Device door open"); /* ERROR_DEVICE_DOOR_OPEN */ + s_ErrorMessage.Add(1167, "Device not connected"); /* ERROR_DEVICE_NOT_CONNECTED */ + s_ErrorMessage.Add(1168, "Not found"); /* ERROR_NOT_FOUND */ + s_ErrorMessage.Add(1169, "No match"); /* ERROR_NO_MATCH */ + s_ErrorMessage.Add(1170, "Set not found"); /* ERROR_SET_NOT_FOUND */ + s_ErrorMessage.Add(1171, "Point not found"); /* ERROR_POINT_NOT_FOUND */ + s_ErrorMessage.Add(1172, "No tracking service"); /* ERROR_NO_TRACKING_SERVICE */ + s_ErrorMessage.Add(1173, "No volume ID"); /* ERROR_NO_VOLUME_ID */ + s_ErrorMessage.Add(1175, "Unable to remove replaced"); /* ERROR_UNABLE_TO_REMOVE_REPLACED */ + s_ErrorMessage.Add(1176, "Unable to move replacement"); /* ERROR_UNABLE_TO_MOVE_REPLACEMENT */ + s_ErrorMessage.Add(1177, "Unable to move replacement 2"); /* ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 */ + s_ErrorMessage.Add(1178, "Journal delete in progress"); /* ERROR_JOURNAL_DELETE_IN_PROGRESS */ + s_ErrorMessage.Add(1179, "Journal not active"); /* ERROR_JOURNAL_NOT_ACTIVE */ + s_ErrorMessage.Add(1180, "Potential file found"); /* ERROR_POTENTIAL_FILE_FOUND */ + s_ErrorMessage.Add(1181, "Journal entry deleted"); /* ERROR_JOURNAL_ENTRY_DELETED */ + s_ErrorMessage.Add(1200, "Bad device"); /* ERROR_BAD_DEVICE */ + s_ErrorMessage.Add(1201, "Connection unavail"); /* ERROR_CONNECTION_UNAVAIL */ + s_ErrorMessage.Add(1202, "Device already remembered"); /* ERROR_DEVICE_ALREADY_REMEMBERED */ + s_ErrorMessage.Add(1203, "No net or bad path"); /* ERROR_NO_NET_OR_BAD_PATH */ + s_ErrorMessage.Add(1204, "Bad provider"); /* ERROR_BAD_PROVIDER */ + s_ErrorMessage.Add(1205, "Cannot open profile"); /* ERROR_CANNOT_OPEN_PROFILE */ + s_ErrorMessage.Add(1206, "Bad profile"); /* ERROR_BAD_PROFILE */ + s_ErrorMessage.Add(1207, "Not container"); /* ERROR_NOT_CONTAINER */ + s_ErrorMessage.Add(1208, "Extended error"); /* ERROR_EXTENDED_ERROR */ + s_ErrorMessage.Add(1209, "Invalid group name"); /* ERROR_INVALID_GROUPNAME */ + s_ErrorMessage.Add(1210, "Invalid computer name"); /* ERROR_INVALID_COMPUTERNAME */ + s_ErrorMessage.Add(1211, "Invalid event name"); /* ERROR_INVALID_EVENTNAME */ + s_ErrorMessage.Add(1212, "Invalid domain name"); /* ERROR_INVALID_DOMAINNAME */ + s_ErrorMessage.Add(1213, "Invalid service name"); /* ERROR_INVALID_SERVICENAME */ + s_ErrorMessage.Add(1214, "Invalid net name"); /* ERROR_INVALID_NETNAME */ + s_ErrorMessage.Add(1215, "Invalid share name"); /* ERROR_INVALID_SHARENAME */ + s_ErrorMessage.Add(1216, "Invalid password name"); /* ERROR_INVALID_PASSWORDNAME */ + s_ErrorMessage.Add(1217, "Invalid message name"); /* ERROR_INVALID_MESSAGENAME */ + s_ErrorMessage.Add(1218, "Invalid message dest"); /* ERROR_INVALID_MESSAGEDEST */ + s_ErrorMessage.Add(1219, "Session credential conflict"); /* ERROR_SESSION_CREDENTIAL_CONFLICT */ + s_ErrorMessage.Add(1220, "Remote session limit exceeded"); /* ERROR_REMOTE_SESSION_LIMIT_EXCEEDED */ + s_ErrorMessage.Add(1221, "Dup domain name"); /* ERROR_DUP_DOMAINNAME */ + s_ErrorMessage.Add(1222, "No network"); /* ERROR_NO_NETWORK */ + s_ErrorMessage.Add(1223, "Cancelled"); /* ERROR_CANCELLED */ + s_ErrorMessage.Add(1224, "User mapped file"); /* ERROR_USER_MAPPED_FILE */ + s_ErrorMessage.Add(1225, "Connection refused"); /* ERROR_CONNECTION_REFUSED */ + s_ErrorMessage.Add(1226, "Graceful disconnect"); /* ERROR_GRACEFUL_DISCONNECT */ + s_ErrorMessage.Add(1227, "Address already associated"); /* ERROR_ADDRESS_ALREADY_ASSOCIATED */ + s_ErrorMessage.Add(1228, "Address not associated"); /* ERROR_ADDRESS_NOT_ASSOCIATED */ + s_ErrorMessage.Add(1229, "Connected invalid"); /* ERROR_CONNECTION_INVALID */ + s_ErrorMessage.Add(1230, "Connection active"); /* ERROR_CONNECTION_ACTIVE */ + s_ErrorMessage.Add(1231, "Network unreachable"); /* ERROR_NETWORK_UNREACHABLE */ + s_ErrorMessage.Add(1232, "Host unreachable"); /* ERROR_HOST_UNREACHABLE */ + s_ErrorMessage.Add(1233, "Protocol unreachable"); /* ERROR_PROTOCOL_UNREACHABLE */ + s_ErrorMessage.Add(1234, "Port unreachable"); /* ERROR_PORT_UNREACHABLE */ + s_ErrorMessage.Add(1235, "Request aborted"); /* ERROR_REQUEST_ABORTED */ + } + + public static void InitializeErrorMessages6() + { + s_ErrorMessage.Add(1236, "Connection aborted"); /* ERROR_CONNECTION_ABORTED */ + s_ErrorMessage.Add(1237, "Retry"); /* ERROR_RETRY */ + s_ErrorMessage.Add(1238, "Connection count limit"); /* ERROR_CONNECTION_COUNT_LIMIT */ + s_ErrorMessage.Add(1239, "Login time restriction"); /* ERROR_LOGIN_TIME_RESTRICTION */ + s_ErrorMessage.Add(1240, "Login wksta restriction"); /* ERROR_LOGIN_WKSTA_RESTRICTION */ + s_ErrorMessage.Add(1241, "Incorrect address"); /* ERROR_INCORRECT_ADDRESS */ + s_ErrorMessage.Add(1242, "Already registered"); /* ERROR_ALREADY_REGISTERED */ + s_ErrorMessage.Add(1243, "Service not found"); /* ERROR_SERVICE_NOT_FOUND */ + s_ErrorMessage.Add(1244, "Not authenticated"); /* ERROR_NOT_AUTHENTICATED */ + s_ErrorMessage.Add(1245, "Not logged on"); /* ERROR_NOT_LOGGED_ON */ + s_ErrorMessage.Add(1246, "Continue"); /* ERROR_CONTINUE */ + s_ErrorMessage.Add(1247, "Already initialised"); /* ERROR_ALREADY_INITIALIZED */ + s_ErrorMessage.Add(1248, "No more devices"); /* ERROR_NO_MORE_DEVICES */ + s_ErrorMessage.Add(1249, "No such site"); /* ERROR_NO_SUCH_SITE */ + s_ErrorMessage.Add(1250, "Domain controller exists"); /* ERROR_DOMAIN_CONTROLLER_EXISTS */ + s_ErrorMessage.Add(1251, "Only if connected"); /* ERROR_ONLY_IF_CONNECTED */ + s_ErrorMessage.Add(1252, "Override no changes"); /* ERROR_OVERRIDE_NOCHANGES */ + s_ErrorMessage.Add(1253, "Bad user profile"); /* ERROR_BAD_USER_PROFILE */ + s_ErrorMessage.Add(1254, "Not supported on SBS"); /* ERROR_NOT_SUPPORTED_ON_SBS */ + s_ErrorMessage.Add(1255, "Server shutdown in progress"); /* ERROR_SERVER_SHUTDOWN_IN_PROGRESS */ + s_ErrorMessage.Add(1256, "Host down"); /* ERROR_HOST_DOWN */ + s_ErrorMessage.Add(1257, "Non account sid"); /* ERROR_NON_ACCOUNT_SID */ + s_ErrorMessage.Add(1258, "Non domain sid"); /* ERROR_NON_DOMAIN_SID */ + s_ErrorMessage.Add(1259, "Apphelp block"); /* ERROR_APPHELP_BLOCK */ + s_ErrorMessage.Add(1260, "Access disabled by policy"); /* ERROR_ACCESS_DISABLED_BY_POLICY */ + s_ErrorMessage.Add(1261, "Reg nat consumption"); /* ERROR_REG_NAT_CONSUMPTION */ + s_ErrorMessage.Add(1262, "CSC share offline"); /* ERROR_CSCSHARE_OFFLINE */ + s_ErrorMessage.Add(1263, "PK init failure"); /* ERROR_PKINIT_FAILURE */ + s_ErrorMessage.Add(1264, "Smartcard subsystem failure"); /* ERROR_SMARTCARD_SUBSYSTEM_FAILURE */ + s_ErrorMessage.Add(1265, "Downgrade detected"); /* ERROR_DOWNGRADE_DETECTED */ + s_ErrorMessage.Add(1266, "Smartcard cert revoked"); /* SEC_E_SMARTCARD_CERT_REVOKED */ + s_ErrorMessage.Add(1267, "Issuing CA untrusted"); /* SEC_E_ISSUING_CA_UNTRUSTED */ + s_ErrorMessage.Add(1268, "Revocation offline"); /* SEC_E_REVOCATION_OFFLINE_C */ + s_ErrorMessage.Add(1269, "PK init client failure"); /* SEC_E_PKINIT_CLIENT_FAILUR */ + s_ErrorMessage.Add(1270, "Smartcard cert expired"); /* SEC_E_SMARTCARD_CERT_EXPIRED */ + s_ErrorMessage.Add(1271, "Machine locked"); /* ERROR_MACHINE_LOCKED */ + s_ErrorMessage.Add(1273, "Callback supplied invalid data"); /* ERROR_CALLBACK_SUPPLIED_INVALID_DATA */ + s_ErrorMessage.Add(1274, "Sync foreground refresh required"); /* ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED */ + s_ErrorMessage.Add(1275, "Driver blocked"); /* ERROR_DRIVER_BLOCKED */ + s_ErrorMessage.Add(1276, "Invalid import of non DLL"); /* ERROR_INVALID_IMPORT_OF_NON_DLL */ + s_ErrorMessage.Add(1300, "Not all assigned"); /* ERROR_NOT_ALL_ASSIGNED */ + s_ErrorMessage.Add(1301, "Some not mapped"); /* ERROR_SOME_NOT_MAPPED */ + s_ErrorMessage.Add(1302, "No quotas for account"); /* ERROR_NO_QUOTAS_FOR_ACCOUNT */ + s_ErrorMessage.Add(1303, "Local user session key"); /* ERROR_LOCAL_USER_SESSION_KEY */ + s_ErrorMessage.Add(1304, "Null LM password"); /* ERROR_NULL_LM_PASSWORD */ + s_ErrorMessage.Add(1305, "Unknown revision"); /* ERROR_UNKNOWN_REVISION */ + s_ErrorMessage.Add(1306, "Revision mismatch"); /* ERROR_REVISION_MISMATCH */ + s_ErrorMessage.Add(1307, "Invalid owner"); /* ERROR_INVALID_OWNER */ + s_ErrorMessage.Add(1308, "Invalid primary group"); /* ERROR_INVALID_PRIMARY_GROUP */ + s_ErrorMessage.Add(1309, "No impersonation token"); /* ERROR_NO_IMPERSONATION_TOKEN */ + s_ErrorMessage.Add(1310, "Can't disable mandatory"); /* ERROR_CANT_DISABLE_MANDATORY */ + s_ErrorMessage.Add(1311, "No logon servers"); /* ERROR_NO_LOGON_SERVERS */ + s_ErrorMessage.Add(1312, "No such logon session"); /* ERROR_NO_SUCH_LOGON_SESSION */ + s_ErrorMessage.Add(1313, "No such privilege"); /* ERROR_NO_SUCH_PRIVILEGE */ + s_ErrorMessage.Add(1314, "Privilege not held"); /* ERROR_PRIVILEGE_NOT_HELD */ + s_ErrorMessage.Add(1315, "Invalid account name"); /* ERROR_INVALID_ACCOUNT_NAME */ + s_ErrorMessage.Add(1316, "User exists"); /* ERROR_USER_EXISTS */ + s_ErrorMessage.Add(1317, "No such user"); /* ERROR_NO_SUCH_USER */ + s_ErrorMessage.Add(1318, "Group exists"); /* ERROR_GROUP_EXISTS */ + s_ErrorMessage.Add(1319, "No such group"); /* ERROR_NO_SUCH_GROUP */ + s_ErrorMessage.Add(1320, "Member in group"); /* ERROR_MEMBER_IN_GROUP */ + s_ErrorMessage.Add(1321, "Member not in group"); /* ERROR_MEMBER_NOT_IN_GROUP */ + s_ErrorMessage.Add(1322, "Last admin"); /* ERROR_LAST_ADMIN */ + s_ErrorMessage.Add(1323, "Wrong password"); /* ERROR_WRONG_PASSWORD */ + s_ErrorMessage.Add(1324, "Ill formed password"); /* ERROR_ILL_FORMED_PASSWORD */ + s_ErrorMessage.Add(1325, "Password restriction"); /* ERROR_PASSWORD_RESTRICTION */ + s_ErrorMessage.Add(1326, "Logon failure"); /* ERROR_LOGON_FAILURE */ + s_ErrorMessage.Add(1327, "Account restriction"); /* ERROR_ACCOUNT_RESTRICTION */ + s_ErrorMessage.Add(1328, "Invalid logon hours"); /* ERROR_INVALID_LOGON_HOURS */ + s_ErrorMessage.Add(1329, "Invalid workstation"); /* ERROR_INVALID_WORKSTATION */ + s_ErrorMessage.Add(1330, "Password expired"); /* ERROR_PASSWORD_EXPIRED */ + s_ErrorMessage.Add(1331, "Account disabled"); /* ERROR_ACCOUNT_DISABLED */ + s_ErrorMessage.Add(1332, "None mapped"); /* ERROR_NONE_MAPPED */ + s_ErrorMessage.Add(1333, "Too many LUIDs requested"); /* ERROR_TOO_MANY_LUIDS_REQUESTED */ + s_ErrorMessage.Add(1334, "LUIDs exhausted"); /* ERROR_LUIDS_EXHAUSTED */ + s_ErrorMessage.Add(1335, "Invalid sub authority"); /* ERROR_INVALID_SUB_AUTHORITY */ + s_ErrorMessage.Add(1336, "Invalid ACL"); /* ERROR_INVALID_ACL */ + s_ErrorMessage.Add(1337, "Invalid SID"); /* ERROR_INVALID_SID */ + s_ErrorMessage.Add(1338, "Invalid security descr"); /* ERROR_INVALID_SECURITY_DESCR */ + s_ErrorMessage.Add(1340, "Bad inheritance ACL"); /* ERROR_BAD_INHERITANCE_ACL */ + s_ErrorMessage.Add(1341, "Server disabled"); /* ERROR_SERVER_DISABLED */ + s_ErrorMessage.Add(1342, "Server not disabled"); /* ERROR_SERVER_NOT_DISABLED */ + s_ErrorMessage.Add(1343, "Invalid ID authority"); /* ERROR_INVALID_ID_AUTHORITY */ + s_ErrorMessage.Add(1344, "Allotted space exceeded"); /* ERROR_ALLOTTED_SPACE_EXCEEDED */ + s_ErrorMessage.Add(1345, "Invalid group attributes"); /* ERROR_INVALID_GROUP_ATTRIBUTES */ + s_ErrorMessage.Add(1346, "Bad impersonation level"); /* ERROR_BAD_IMPERSONATION_LEVEL */ + s_ErrorMessage.Add(1347, "Can't open anonymous"); /* ERROR_CANT_OPEN_ANONYMOUS */ + s_ErrorMessage.Add(1348, "Bad validation class"); /* ERROR_BAD_VALIDATION_CLASS */ + s_ErrorMessage.Add(1349, "Bad token type"); /* ERROR_BAD_TOKEN_TYPE */ + s_ErrorMessage.Add(1350, "No security on object"); /* ERROR_NO_SECURITY_ON_OBJECT */ + s_ErrorMessage.Add(1351, "Can't access domain info"); /* ERROR_CANT_ACCESS_DOMAIN_INFO */ + s_ErrorMessage.Add(1352, "Invalid server state"); /* ERROR_INVALID_SERVER_STATE */ + s_ErrorMessage.Add(1353, "Invalid domain state"); /* ERROR_INVALID_DOMAIN_STATE */ + s_ErrorMessage.Add(1354, "Invalid domain role"); /* ERROR_INVALID_DOMAIN_ROLE */ + s_ErrorMessage.Add(1355, "No such domain"); /* ERROR_NO_SUCH_DOMAIN */ + s_ErrorMessage.Add(1356, "Domain exists"); /* ERROR_DOMAIN_EXISTS */ + s_ErrorMessage.Add(1357, "Domain limit exceeded"); /* ERROR_DOMAIN_LIMIT_EXCEEDED */ + s_ErrorMessage.Add(1358, "Internal DB corruption"); /* ERROR_INTERNAL_DB_CORRUPTION */ + s_ErrorMessage.Add(1359, "Internal error"); /* ERROR_INTERNAL_ERROR */ + s_ErrorMessage.Add(1360, "Generic not mapped"); /* ERROR_GENERIC_NOT_MAPPED */ + s_ErrorMessage.Add(1361, "Bad descriptor format"); /* ERROR_BAD_DESCRIPTOR_FORMAT */ + s_ErrorMessage.Add(1362, "Not logon process"); /* ERROR_NOT_LOGON_PROCESS */ + s_ErrorMessage.Add(1363, "Logon session exists"); /* ERROR_LOGON_SESSION_EXISTS */ + s_ErrorMessage.Add(1364, "No such package"); /* ERROR_NO_SUCH_PACKAGE */ + s_ErrorMessage.Add(1365, "Bad logon session state"); /* ERROR_BAD_LOGON_SESSION_STATE */ + s_ErrorMessage.Add(1366, "Logon session collision"); /* ERROR_LOGON_SESSION_COLLISION */ + s_ErrorMessage.Add(1367, "Invalid logon type"); /* ERROR_INVALID_LOGON_TYPE */ + } + + public static void InitializeErrorMessages7() + { + s_ErrorMessage.Add(1368, "Cannot impersonate"); /* ERROR_CANNOT_IMPERSONATE */ + s_ErrorMessage.Add(1369, "Rxact invalid state"); /* ERROR_RXACT_INVALID_STATE */ + s_ErrorMessage.Add(1370, "Rxact commit failure"); /* ERROR_RXACT_COMMIT_FAILURE */ + s_ErrorMessage.Add(1371, "Special account"); /* ERROR_SPECIAL_ACCOUNT */ + s_ErrorMessage.Add(1372, "Special group"); /* ERROR_SPECIAL_GROUP */ + s_ErrorMessage.Add(1373, "Special user"); /* ERROR_SPECIAL_USER */ + s_ErrorMessage.Add(1374, "Members primary group"); /* ERROR_MEMBERS_PRIMARY_GROUP */ + s_ErrorMessage.Add(1375, "Token already in use"); /* ERROR_TOKEN_ALREADY_IN_USE */ + s_ErrorMessage.Add(1376, "No such alias"); /* ERROR_NO_SUCH_ALIAS */ + s_ErrorMessage.Add(1377, "Member not in alias"); /* ERROR_MEMBER_NOT_IN_ALIAS */ + s_ErrorMessage.Add(1378, "Member in alias"); /* ERROR_MEMBER_IN_ALIAS */ + s_ErrorMessage.Add(1379, "Alias exists"); /* ERROR_ALIAS_EXISTS */ + s_ErrorMessage.Add(1380, "Logon not granted"); /* ERROR_LOGON_NOT_GRANTED */ + s_ErrorMessage.Add(1381, "Too many secrets"); /* ERROR_TOO_MANY_SECRETS */ + s_ErrorMessage.Add(1382, "Secret too long"); /* ERROR_SECRET_TOO_LONG */ + s_ErrorMessage.Add(1383, "Internal DB error"); /* ERROR_INTERNAL_DB_ERROR */ + s_ErrorMessage.Add(1384, "Too many context IDs"); /* ERROR_TOO_MANY_CONTEXT_IDS */ + s_ErrorMessage.Add(1385, "Logon type not granted"); /* ERROR_LOGON_TYPE_NOT_GRANTED */ + s_ErrorMessage.Add(1386, "NT cross encryption required"); /* ERROR_NT_CROSS_ENCRYPTION_REQUIRED */ + s_ErrorMessage.Add(1387, "No such member"); /* ERROR_NO_SUCH_MEMBER */ + s_ErrorMessage.Add(1388, "Invalid member"); /* ERROR_INVALID_MEMBER */ + s_ErrorMessage.Add(1389, "Too many SIDs"); /* ERROR_TOO_MANY_SIDS */ + s_ErrorMessage.Add(1390, "LM cross encryption required"); /* ERROR_LM_CROSS_ENCRYPTION_REQUIRED */ + s_ErrorMessage.Add(1391, "No inheritance"); /* ERROR_NO_INHERITANCE */ + s_ErrorMessage.Add(1392, "File corrupt"); /* ERROR_FILE_CORRUPT */ + s_ErrorMessage.Add(1393, "Disk corrupt"); /* ERROR_DISK_CORRUPT */ + s_ErrorMessage.Add(1394, "No user session key"); /* ERROR_NO_USER_SESSION_KEY */ + s_ErrorMessage.Add(1395, "Licence quota exceeded"); /* ERROR_LICENSE_QUOTA_EXCEEDED */ + s_ErrorMessage.Add(1396, "Wrong target name"); /* ERROR_WRONG_TARGET_NAME */ + s_ErrorMessage.Add(1397, "Mutual auth failed"); /* ERROR_MUTUAL_AUTH_FAILED */ + s_ErrorMessage.Add(1398, "Time skew"); /* ERROR_TIME_SKEW */ + s_ErrorMessage.Add(1399, "Current domain not allowed"); /* ERROR_CURRENT_DOMAIN_NOT_ALLOWED */ + s_ErrorMessage.Add(1400, "Invalid window handle"); /* ERROR_INVALID_WINDOW_HANDLE */ + s_ErrorMessage.Add(1401, "Invalid menu handle"); /* ERROR_INVALID_MENU_HANDLE */ + s_ErrorMessage.Add(1402, "Invalid cursor handle"); /* ERROR_INVALID_CURSOR_HANDLE */ + s_ErrorMessage.Add(1403, "Invalid accel handle"); /* ERROR_INVALID_ACCEL_HANDLE */ + s_ErrorMessage.Add(1404, "Invalid hook handle"); /* ERROR_INVALID_HOOK_HANDLE */ + s_ErrorMessage.Add(1405, "Invalid DWP handle"); /* ERROR_INVALID_DWP_HANDLE */ + s_ErrorMessage.Add(1406, "TLW with wschild"); /* ERROR_TLW_WITH_WSCHILD */ + s_ErrorMessage.Add(1407, "Cannot find WND class"); /* ERROR_CANNOT_FIND_WND_CLASS */ + s_ErrorMessage.Add(1408, "Window of other thread"); /* ERROR_WINDOW_OF_OTHER_THREAD */ + s_ErrorMessage.Add(1409, "Hotkey already registered"); /* ERROR_HOTKEY_ALREADY_REGISTERED */ + s_ErrorMessage.Add(1410, "Class already exists"); /* ERROR_CLASS_ALREADY_EXISTS */ + s_ErrorMessage.Add(1411, "Class does not exist"); /* ERROR_CLASS_DOES_NOT_EXIST */ + s_ErrorMessage.Add(1412, "Class has windows"); /* ERROR_CLASS_HAS_WINDOWS */ + s_ErrorMessage.Add(1413, "Invalid index"); /* ERROR_INVALID_INDEX */ + s_ErrorMessage.Add(1414, "Invalid icon handle"); /* ERROR_INVALID_ICON_HANDLE */ + s_ErrorMessage.Add(1415, "Private dialog index"); /* ERROR_PRIVATE_DIALOG_INDEX */ + s_ErrorMessage.Add(1416, "Listbox ID not found"); /* ERROR_LISTBOX_ID_NOT_FOUND */ + s_ErrorMessage.Add(1417, "No wildcard characters"); /* ERROR_NO_WILDCARD_CHARACTERS */ + s_ErrorMessage.Add(1418, "Clipboard not open"); /* ERROR_CLIPBOARD_NOT_OPEN */ + s_ErrorMessage.Add(1419, "Hotkey not registered"); /* ERROR_HOTKEY_NOT_REGISTERED */ + s_ErrorMessage.Add(1420, "Window not dialog"); /* ERROR_WINDOW_NOT_DIALOG */ + s_ErrorMessage.Add(1421, "Control ID not found"); /* ERROR_CONTROL_ID_NOT_FOUND */ + s_ErrorMessage.Add(1422, "Invalid combobox message"); /* ERROR_INVALID_COMBOBOX_MESSAGE */ + s_ErrorMessage.Add(1423, "Window not combobox"); /* ERROR_WINDOW_NOT_COMBOBOX */ + s_ErrorMessage.Add(1424, "Invalid edit height"); /* ERROR_INVALID_EDIT_HEIGHT */ + s_ErrorMessage.Add(1425, "DC not found"); /* ERROR_DC_NOT_FOUND */ + s_ErrorMessage.Add(1426, "Invalid hook filter"); /* ERROR_INVALID_HOOK_FILTER */ + s_ErrorMessage.Add(1427, "Invalid filter proc"); /* ERROR_INVALID_FILTER_PROC */ + s_ErrorMessage.Add(1428, "Hook needs HMOD"); /* ERROR_HOOK_NEEDS_HMOD */ + s_ErrorMessage.Add(1429, "Global only hook"); /* ERROR_GLOBAL_ONLY_HOOK */ + s_ErrorMessage.Add(1430, "Journal hook set"); /* ERROR_JOURNAL_HOOK_SET */ + s_ErrorMessage.Add(1431, "Hook not installed"); /* ERROR_HOOK_NOT_INSTALLED */ + s_ErrorMessage.Add(1432, "Invalid LB message"); /* ERROR_INVALID_LB_MESSAGE */ + s_ErrorMessage.Add(1433, "Setcount on bad LB"); /* ERROR_SETCOUNT_ON_BAD_LB */ + s_ErrorMessage.Add(1434, "LB without tabstops"); /* ERROR_LB_WITHOUT_TABSTOPS */ + s_ErrorMessage.Add(1435, "Destroy object of other thread"); /* ERROR_DESTROY_OBJECT_OF_OTHER_THREAD */ + s_ErrorMessage.Add(1436, "Child window menu"); /* ERROR_CHILD_WINDOW_MENU */ + s_ErrorMessage.Add(1437, "No system menu"); /* ERROR_NO_SYSTEM_MENU */ + s_ErrorMessage.Add(1438, "Invalid msgbox style"); /* ERROR_INVALID_MSGBOX_STYLE */ + s_ErrorMessage.Add(1439, "Invalid SPI value"); /* ERROR_INVALID_SPI_VALUE */ + s_ErrorMessage.Add(1440, "Screen already locked"); /* ERROR_SCREEN_ALREADY_LOCKED */ + s_ErrorMessage.Add(1441, "HWNDs have different parent"); /* ERROR_HWNDS_HAVE_DIFF_PARENT */ + s_ErrorMessage.Add(1442, "Not child window"); /* ERROR_NOT_CHILD_WINDOW */ + s_ErrorMessage.Add(1443, "Invalid GW command"); /* ERROR_INVALID_GW_COMMAND */ + s_ErrorMessage.Add(1444, "Invalid thread ID"); /* ERROR_INVALID_THREAD_ID */ + s_ErrorMessage.Add(1445, "Non MDI child window"); /* ERROR_NON_MDICHILD_WINDOW */ + s_ErrorMessage.Add(1446, "Popup already active"); /* ERROR_POPUP_ALREADY_ACTIVE */ + s_ErrorMessage.Add(1447, "No scrollbars"); /* ERROR_NO_SCROLLBARS */ + s_ErrorMessage.Add(1448, "Invalid scrollbar range"); /* ERROR_INVALID_SCROLLBAR_RANGE */ + s_ErrorMessage.Add(1449, "Invalid showwin command"); /* ERROR_INVALID_SHOWWIN_COMMAND */ + s_ErrorMessage.Add(1450, "No system resources"); /* ERROR_NO_SYSTEM_RESOURCES */ + s_ErrorMessage.Add(1451, "Nonpaged system resources"); /* ERROR_NONPAGED_SYSTEM_RESOURCES */ + s_ErrorMessage.Add(1452, "Paged system resources"); /* ERROR_PAGED_SYSTEM_RESOURCES */ + s_ErrorMessage.Add(1453, "Working set quota"); /* ERROR_WORKING_SET_QUOTA */ + s_ErrorMessage.Add(1454, "Pagefile quota"); /* ERROR_PAGEFILE_QUOTA */ + s_ErrorMessage.Add(1455, "Commitment limit"); /* ERROR_COMMITMENT_LIMIT */ + s_ErrorMessage.Add(1456, "Menu item not found"); /* ERROR_MENU_ITEM_NOT_FOUND */ + s_ErrorMessage.Add(1457, "Invalid keyboard handle"); /* ERROR_INVALID_KEYBOARD_HANDLE */ + s_ErrorMessage.Add(1458, "Hook type not allowed"); /* ERROR_HOOK_TYPE_NOT_ALLOWED */ + s_ErrorMessage.Add(1459, "Requires interactive windowstation"); /* ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION */ + s_ErrorMessage.Add(1460, "Timeout"); /* ERROR_TIMEOUT */ + s_ErrorMessage.Add(1461, "Invalid monitor handle"); /* ERROR_INVALID_MONITOR_HANDLE */ + s_ErrorMessage.Add(1500, "Eventlog file corrupt"); /* ERROR_EVENTLOG_FILE_CORRUPT */ + s_ErrorMessage.Add(1501, "Eventlog can't start"); /* ERROR_EVENTLOG_CANT_START */ + s_ErrorMessage.Add(1502, "Log file full"); /* ERROR_LOG_FILE_FULL */ + s_ErrorMessage.Add(1503, "Eventlog file changed"); /* ERROR_EVENTLOG_FILE_CHANGED */ + s_ErrorMessage.Add(1601, "Install service failure"); /* ERROR_INSTALL_SERVICE_FAILURE */ + s_ErrorMessage.Add(1602, "Install userexit"); /* ERROR_INSTALL_USEREXIT */ + s_ErrorMessage.Add(1603, "Install failure"); /* ERROR_INSTALL_FAILURE */ + s_ErrorMessage.Add(1604, "Install suspend"); /* ERROR_INSTALL_SUSPEND */ + s_ErrorMessage.Add(1605, "Unknown product"); /* ERROR_UNKNOWN_PRODUCT */ + s_ErrorMessage.Add(1606, "Unknown feature"); /* ERROR_UNKNOWN_FEATURE */ + s_ErrorMessage.Add(1607, "Unknown component"); /* ERROR_UNKNOWN_COMPONENT */ + s_ErrorMessage.Add(1608, "Unknown property"); /* ERROR_UNKNOWN_PROPERTY */ + s_ErrorMessage.Add(1609, "Invalid handle state"); /* ERROR_INVALID_HANDLE_STATE */ + s_ErrorMessage.Add(1610, "Bad configuration"); /* ERROR_BAD_CONFIGURATION */ + s_ErrorMessage.Add(1611, "Index absent"); /* ERROR_INDEX_ABSENT */ + s_ErrorMessage.Add(1612, "Install source absent"); /* ERROR_INSTALL_SOURCE_ABSENT */ + s_ErrorMessage.Add(1613, "Install package version"); /* ERROR_INSTALL_PACKAGE_VERSION */ + s_ErrorMessage.Add(1614, "Product uninstalled"); /* ERROR_PRODUCT_UNINSTALLED */ + s_ErrorMessage.Add(1615, "Bad query syntax"); /* ERROR_BAD_QUERY_SYNTAX */ + s_ErrorMessage.Add(1616, "Invalid field"); /* ERROR_INVALID_FIELD */ + s_ErrorMessage.Add(1617, "Device removed"); /* ERROR_DEVICE_REMOVED */ + s_ErrorMessage.Add(1618, "Install already running"); /* ERROR_INSTALL_ALREADY_RUNNING */ + s_ErrorMessage.Add(1619, "Install package open failed"); /* ERROR_INSTALL_PACKAGE_OPEN_FAILED */ + s_ErrorMessage.Add(1620, "Install package invalid"); /* ERROR_INSTALL_PACKAGE_INVALID */ + s_ErrorMessage.Add(1621, "Install UI failure"); /* ERROR_INSTALL_UI_FAILURE */ + s_ErrorMessage.Add(1622, "Install log failure"); /* ERROR_INSTALL_LOG_FAILURE */ + s_ErrorMessage.Add(1623, "Install language unsupported"); /* ERROR_INSTALL_LANGUAGE_UNSUPPORTED */ + s_ErrorMessage.Add(1624, "Install transform failure"); /* ERROR_INSTALL_TRANSFORM_FAILURE */ + s_ErrorMessage.Add(1625, "Install package rejected"); /* ERROR_INSTALL_PACKAGE_REJECTED */ + s_ErrorMessage.Add(1626, "Function not called"); /* ERROR_FUNCTION_NOT_CALLED */ + s_ErrorMessage.Add(1627, "Function failed"); /* ERROR_FUNCTION_FAILED */ + s_ErrorMessage.Add(1628, "Invalid table"); /* ERROR_INVALID_TABLE */ + s_ErrorMessage.Add(1629, "Datatype mismatch"); /* ERROR_DATATYPE_MISMATCH */ + s_ErrorMessage.Add(1630, "Unsupported type"); /* ERROR_UNSUPPORTED_TYPE */ + s_ErrorMessage.Add(1631, "Create failed"); /* ERROR_CREATE_FAILED */ + s_ErrorMessage.Add(1632, "Install temp unwritable"); /* ERROR_INSTALL_TEMP_UNWRITABLE */ + s_ErrorMessage.Add(1633, "Install platform unsupported"); /* ERROR_INSTALL_PLATFORM_UNSUPPORTED */ + s_ErrorMessage.Add(1634, "Install notused"); /* ERROR_INSTALL_NOTUSED */ + s_ErrorMessage.Add(1635, "Patch package open failed"); /* ERROR_PATCH_PACKAGE_OPEN_FAILED */ + s_ErrorMessage.Add(1636, "Patch package invalid"); /* ERROR_PATCH_PACKAGE_INVALID */ + s_ErrorMessage.Add(1637, "Patch package unsupported"); /* ERROR_PATCH_PACKAGE_UNSUPPORTED */ + s_ErrorMessage.Add(1638, "Product version"); /* ERROR_PRODUCT_VERSION */ + s_ErrorMessage.Add(1639, "Invalid command line"); /* ERROR_INVALID_COMMAND_LINE */ + s_ErrorMessage.Add(1640, "Install remote disallowed"); /* ERROR_INSTALL_REMOTE_DISALLOWED */ + s_ErrorMessage.Add(1641, "Success reboot initiated"); /* ERROR_SUCCESS_REBOOT_INITIATED */ + s_ErrorMessage.Add(1642, "Patch target not found"); /* ERROR_PATCH_TARGET_NOT_FOUND */ + s_ErrorMessage.Add(1643, "Patch package rejected"); /* ERROR_PATCH_PACKAGE_REJECTED */ + s_ErrorMessage.Add(1644, "Install transform rejected"); /* ERROR_INSTALL_TRANSFORM_REJECTED */ + s_ErrorMessage.Add(1700, "RPC S Invalid string binding"); /* RPC_S_INVALID_STRING_BINDING */ + s_ErrorMessage.Add(1701, "RPC S Wrong kind of binding"); /* RPC_S_WRONG_KIND_OF_BINDING */ + s_ErrorMessage.Add(1702, "RPC S Invalid binding"); /* RPC_S_INVALID_BINDING */ + s_ErrorMessage.Add(1703, "RPC S Protseq not supported"); /* RPC_S_PROTSEQ_NOT_SUPPORTED */ + } + + public static void InitializeErrorMessages8() + { + s_ErrorMessage.Add(1704, "RPC S Invalid RPC protseq"); /* RPC_S_INVALID_RPC_PROTSEQ */ + s_ErrorMessage.Add(1705, "RPC S Invalid string UUID"); /* RPC_S_INVALID_STRING_UUID */ + s_ErrorMessage.Add(1706, "RPC S Invalid endpoint format"); /* RPC_S_INVALID_ENDPOINT_FORMAT */ + s_ErrorMessage.Add(1707, "RPC S Invalid net addr"); /* RPC_S_INVALID_NET_ADDR */ + s_ErrorMessage.Add(1708, "RPC S No endpoint found"); /* RPC_S_NO_ENDPOINT_FOUND */ + s_ErrorMessage.Add(1709, "RPC S Invalid timeout"); /* RPC_S_INVALID_TIMEOUT */ + s_ErrorMessage.Add(1710, "RPC S Object not found"); /* RPC_S_OBJECT_NOT_FOUND */ + s_ErrorMessage.Add(1711, "RPC S Already registered"); /* RPC_S_ALREADY_REGISTERED */ + s_ErrorMessage.Add(1712, "RPC S Type already registered"); /* RPC_S_TYPE_ALREADY_REGISTERED */ + s_ErrorMessage.Add(1713, "RPC S Already listening"); /* RPC_S_ALREADY_LISTENING */ + s_ErrorMessage.Add(1714, "RPC S Not protseqs registered"); /* RPC_S_NO_PROTSEQS_REGISTERED */ + s_ErrorMessage.Add(1715, "RPC S Not listening"); /* RPC_S_NOT_LISTENING */ + s_ErrorMessage.Add(1716, "RPC S Unknown mgr type"); /* RPC_S_UNKNOWN_MGR_TYPE */ + s_ErrorMessage.Add(1717, "RPC S Unknown IF"); /* RPC_S_UNKNOWN_IF */ + s_ErrorMessage.Add(1718, "RPC S No bindings"); /* RPC_S_NO_BINDINGS */ + s_ErrorMessage.Add(1719, "RPC S Not protseqs"); /* RPC_S_NO_PROTSEQS */ + s_ErrorMessage.Add(1720, "RPC S Can't create endpoint"); /* RPC_S_CANT_CREATE_ENDPOINT */ + s_ErrorMessage.Add(1721, "RPC S Out of resources"); /* RPC_S_OUT_OF_RESOURCES */ + s_ErrorMessage.Add(1722, "RPC S Server unavailable"); /* RPC_S_SERVER_UNAVAILABLE */ + s_ErrorMessage.Add(1723, "RPC S Server too busy"); /* RPC_S_SERVER_TOO_BUSY */ + s_ErrorMessage.Add(1724, "RPC S Invalid network options"); /* RPC_S_INVALID_NETWORK_OPTIONS */ + s_ErrorMessage.Add(1725, "RPC S No call active"); /* RPC_S_NO_CALL_ACTIVE */ + s_ErrorMessage.Add(1726, "RPC S Call failed"); /* RPC_S_CALL_FAILED */ + s_ErrorMessage.Add(1727, "RPC S Call failed DNE"); /* RPC_S_CALL_FAILED_DNE */ + s_ErrorMessage.Add(1728, "RPC S Protocol error"); /* RPC_S_PROTOCOL_ERROR */ + s_ErrorMessage.Add(1730, "RPC S Unsupported trans syn"); /* RPC_S_UNSUPPORTED_TRANS_SYN */ + s_ErrorMessage.Add(1732, "RPC S Unsupported type"); /* RPC_S_UNSUPPORTED_TYPE */ + s_ErrorMessage.Add(1733, "RPC S Invalid tag"); /* RPC_S_INVALID_TAG */ + s_ErrorMessage.Add(1734, "RPC S Invalid bound"); /* RPC_S_INVALID_BOUND */ + s_ErrorMessage.Add(1735, "RPC S No entry name"); /* RPC_S_NO_ENTRY_NAME */ + s_ErrorMessage.Add(1736, "RPC S Invalid name syntax"); /* RPC_S_INVALID_NAME_SYNTAX */ + s_ErrorMessage.Add(1737, "RPC S Unsupported name syntax"); /* RPC_S_UNSUPPORTED_NAME_SYNTAX */ + s_ErrorMessage.Add(1739, "RPC S UUID no address"); /* RPC_S_UUID_NO_ADDRESS */ + s_ErrorMessage.Add(1740, "RPC S Duplicate endpoint"); /* RPC_S_DUPLICATE_ENDPOINT */ + s_ErrorMessage.Add(1741, "RPC S Unknown authn type"); /* RPC_S_UNKNOWN_AUTHN_TYPE */ + s_ErrorMessage.Add(1742, "RPC S Max calls too small"); /* RPC_S_MAX_CALLS_TOO_SMALL */ + s_ErrorMessage.Add(1743, "RPC S String too long"); /* RPC_S_STRING_TOO_LONG */ + s_ErrorMessage.Add(1744, "RPC S Protseq not found"); /* RPC_S_PROTSEQ_NOT_FOUND */ + s_ErrorMessage.Add(1745, "RPC S Procnum out of range"); /* RPC_S_PROCNUM_OUT_OF_RANGE */ + s_ErrorMessage.Add(1746, "RPC S Binding has no auth"); /* RPC_S_BINDING_HAS_NO_AUTH */ + s_ErrorMessage.Add(1747, "RPC S Unknown authn service"); /* RPC_S_UNKNOWN_AUTHN_SERVICE */ + s_ErrorMessage.Add(1748, "RPC S Unknown authn level"); /* RPC_S_UNKNOWN_AUTHN_LEVEL */ + s_ErrorMessage.Add(1749, "RPC S Invalid auth identity"); /* RPC_S_INVALID_AUTH_IDENTITY */ + s_ErrorMessage.Add(1750, "RPC S Unknown authz service"); /* RPC_S_UNKNOWN_AUTHZ_SERVICE */ + s_ErrorMessage.Add(1751, "EPT S Invalid entry"); /* EPT_S_INVALID_ENTRY */ + s_ErrorMessage.Add(1752, "EPT S Can't perform op"); /* EPT_S_CANT_PERFORM_OP */ + s_ErrorMessage.Add(1753, "EPT S Not registered"); /* EPT_S_NOT_REGISTERED */ + s_ErrorMessage.Add(1754, "RPC S Nothing to export"); /* RPC_S_NOTHING_TO_EXPORT */ + s_ErrorMessage.Add(1755, "RPC S Incomplete name"); /* RPC_S_INCOMPLETE_NAME */ + s_ErrorMessage.Add(1756, "RPC S Invalid vers option"); /* RPC_S_INVALID_VERS_OPTION */ + s_ErrorMessage.Add(1757, "RPC S No more members"); /* RPC_S_NO_MORE_MEMBERS */ + s_ErrorMessage.Add(1758, "RPC S Not all objs unexported"); /* RPC_S_NOT_ALL_OBJS_UNEXPORTED */ + s_ErrorMessage.Add(1759, "RPC S Interface not found"); /* RPC_S_INTERFACE_NOT_FOUND */ + s_ErrorMessage.Add(1760, "RPC S Entry already exists"); /* RPC_S_ENTRY_ALREADY_EXISTS */ + s_ErrorMessage.Add(1761, "RPC S Entry not found"); /* RPC_S_ENTRY_NOT_FOUND */ + s_ErrorMessage.Add(1762, "RPC S Name service unavailable"); /* RPC_S_NAME_SERVICE_UNAVAILABLE */ + s_ErrorMessage.Add(1763, "RPC S Invalid naf ID"); /* RPC_S_INVALID_NAF_ID */ + s_ErrorMessage.Add(1764, "RPC S Cannot support"); /* RPC_S_CANNOT_SUPPORT */ + s_ErrorMessage.Add(1765, "RPC S No context available"); /* RPC_S_NO_CONTEXT_AVAILABLE */ + s_ErrorMessage.Add(1766, "RPC S Internal error"); /* RPC_S_INTERNAL_ERROR */ + s_ErrorMessage.Add(1767, "RPC S Zero divide"); /* RPC_S_ZERO_DIVIDE */ + s_ErrorMessage.Add(1768, "RPC S Address error"); /* RPC_S_ADDRESS_ERROR */ + s_ErrorMessage.Add(1769, "RPC S FP div zero"); /* RPC_S_FP_DIV_ZERO */ + s_ErrorMessage.Add(1770, "RPC S FP Underflow"); /* RPC_S_FP_UNDERFLOW */ + s_ErrorMessage.Add(1771, "RPC S Overflow"); /* RPC_S_FP_OVERFLOW */ + s_ErrorMessage.Add(1772, "RPC X No more entries"); /* RPC_X_NO_MORE_ENTRIES */ + s_ErrorMessage.Add(1773, "RPC X SS char trans open fail"); /* RPC_X_SS_CHAR_TRANS_OPEN_FAIL */ + s_ErrorMessage.Add(1774, "RPC X SS char trans short file"); /* RPC_X_SS_CHAR_TRANS_SHORT_FILE */ + s_ErrorMessage.Add(1775, "RPC S SS in null context"); /* RPC_X_SS_IN_NULL_CONTEXT */ + s_ErrorMessage.Add(1777, "RPC X SS context damaged"); /* RPC_X_SS_CONTEXT_DAMAGED */ + s_ErrorMessage.Add(1778, "RPC X SS handles mismatch"); /* RPC_X_SS_HANDLES_MISMATCH */ + s_ErrorMessage.Add(1779, "RPC X SS cannot get call handle"); /* RPC_X_SS_CANNOT_GET_CALL_HANDLE */ + s_ErrorMessage.Add(1780, "RPC X Null ref pointer"); /* RPC_X_NULL_REF_POINTER */ + s_ErrorMessage.Add(1781, "RPC X enum value out of range"); /* RPC_X_ENUM_VALUE_OUT_OF_RANGE */ + s_ErrorMessage.Add(1782, "RPC X byte count too small"); /* RPC_X_BYTE_COUNT_TOO_SMALL */ + s_ErrorMessage.Add(1783, "RPC X bad stub data"); /* RPC_X_BAD_STUB_DATA */ + s_ErrorMessage.Add(1784, "Invalid user buffer"); /* ERROR_INVALID_USER_BUFFER */ + s_ErrorMessage.Add(1785, "Unrecognised media"); /* ERROR_UNRECOGNIZED_MEDIA */ + s_ErrorMessage.Add(1786, "No trust lsa secret"); /* ERROR_NO_TRUST_LSA_SECRET */ + s_ErrorMessage.Add(1787, "No trust sam account"); /* ERROR_NO_TRUST_SAM_ACCOUNT */ + s_ErrorMessage.Add(1788, "Trusted domain failure"); /* ERROR_TRUSTED_DOMAIN_FAILURE */ + s_ErrorMessage.Add(1789, "Trusted relationship failure"); /* ERROR_TRUSTED_RELATIONSHIP_FAILURE */ + s_ErrorMessage.Add(1790, "Trust failure"); /* ERROR_TRUST_FAILURE */ + s_ErrorMessage.Add(1791, "RPC S call in progress"); /* RPC_S_CALL_IN_PROGRESS */ + s_ErrorMessage.Add(1792, "Error netlogon not started"); /* ERROR_NETLOGON_NOT_STARTED */ + s_ErrorMessage.Add(1793, "Account expired"); /* ERROR_ACCOUNT_EXPIRED */ + s_ErrorMessage.Add(1794, "Redirector has open handles"); /* ERROR_REDIRECTOR_HAS_OPEN_HANDLES */ + s_ErrorMessage.Add(1795, "Printer driver already installed"); /* ERROR_PRINTER_DRIVER_ALREADY_INSTALLED */ + s_ErrorMessage.Add(1796, "Unknown port"); /* ERROR_UNKNOWN_PORT */ + s_ErrorMessage.Add(1797, "Unknown printer driver"); /* ERROR_UNKNOWN_PRINTER_DRIVER */ + s_ErrorMessage.Add(1798, "Unknown printprocessor"); /* ERROR_UNKNOWN_PRINTPROCESSOR */ + s_ErrorMessage.Add(1799, "Invalid separator file"); /* ERROR_INVALID_SEPARATOR_FILE */ + s_ErrorMessage.Add(1800, "Invalid priority"); /* ERROR_INVALID_PRIORITY */ + s_ErrorMessage.Add(1801, "Invalid printer name"); /* ERROR_INVALID_PRINTER_NAME */ + s_ErrorMessage.Add(1802, "Printer already exists"); /* ERROR_PRINTER_ALREADY_EXISTS */ + s_ErrorMessage.Add(1803, "Invalid printer command"); /* ERROR_INVALID_PRINTER_COMMAND */ + s_ErrorMessage.Add(1804, "Invalid datatype"); /* ERROR_INVALID_DATATYPE */ + s_ErrorMessage.Add(1805, "Invalid environment"); /* ERROR_INVALID_ENVIRONMENT */ + s_ErrorMessage.Add(1806, "RPC S no more bindings"); /* RPC_S_NO_MORE_BINDINGS */ + s_ErrorMessage.Add(1807, "Nologon interdomain trust account"); /* ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT */ + s_ErrorMessage.Add(1808, "Nologon workstation trust account"); /* ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT */ + s_ErrorMessage.Add(1809, "Nologon server trust account"); /* ERROR_NOLOGON_SERVER_TRUST_ACCOUNT */ + s_ErrorMessage.Add(1810, "Domain trust inconsistent"); /* ERROR_DOMAIN_TRUST_INCONSISTENT */ + s_ErrorMessage.Add(1811, "Server has open handles"); /* ERROR_SERVER_HAS_OPEN_HANDLES */ + s_ErrorMessage.Add(1812, "Resource data not found"); /* ERROR_RESOURCE_DATA_NOT_FOUND */ + s_ErrorMessage.Add(1813, "Resource type not found"); /* ERROR_RESOURCE_TYPE_NOT_FOUND */ + s_ErrorMessage.Add(1814, "Resource name not found"); /* ERROR_RESOURCE_NAME_NOT_FOUND */ + s_ErrorMessage.Add(1815, "Resource lang not found"); /* ERROR_RESOURCE_LANG_NOT_FOUND */ + s_ErrorMessage.Add(1816, "Not enough quota"); /* ERROR_NOT_ENOUGH_QUOTA */ + s_ErrorMessage.Add(1817, "RPC S no interfaces"); /* RPC_S_NO_INTERFACES */ + s_ErrorMessage.Add(1818, "RPC S Call cancelled"); /* RPC_S_CALL_CANCELLED */ + s_ErrorMessage.Add(1819, "RPC S Binding incomplete"); /* RPC_S_BINDING_INCOMPLETE */ + s_ErrorMessage.Add(1820, "RPC S Comm failure"); /* RPC_S_COMM_FAILURE */ + s_ErrorMessage.Add(1821, "RPC S Unsupported authn level"); /* RPC_S_UNSUPPORTED_AUTHN_LEVEL */ + s_ErrorMessage.Add(1822, "RPC S No princ name"); /* RPC_S_NO_PRINC_NAME */ + s_ErrorMessage.Add(1823, "RPC S Not RPC error"); /* RPC_S_NOT_RPC_ERROR */ + s_ErrorMessage.Add(1824, "RPC U UUID local only"); /* RPC_S_UUID_LOCAL_ONLY */ + s_ErrorMessage.Add(1825, "RPC S Sec pkg error"); /* RPC_S_SEC_PKG_ERROR */ + s_ErrorMessage.Add(1826, "RPC S Not cancelled"); /* RPC_S_NOT_CANCELLED */ + s_ErrorMessage.Add(1827, "RPC X Invalid ES action"); /* RPC_X_INVALID_ES_ACTION */ + s_ErrorMessage.Add(1828, "RPC X Wrong ES version"); /* RPC_X_WRONG_ES_VERSION */ + s_ErrorMessage.Add(1829, "RPC X Wrong stub version"); /* RPC_X_WRONG_STUB_VERSION */ + s_ErrorMessage.Add(1830, "RPC X Invalid pipe object"); /* RPC_X_INVALID_PIPE_OBJECT */ + s_ErrorMessage.Add(1831, "RPC X Wrong pipe order"); /* RPC_X_WRONG_PIPE_ORDER */ + s_ErrorMessage.Add(1832, "RPC X Wrong pipe version"); /* RPC_X_WRONG_PIPE_VERSION */ + s_ErrorMessage.Add(1898, "RPC S group member not found"); /* RPC_S_GROUP_MEMBER_NOT_FOUND */ + s_ErrorMessage.Add(1899, "EPT S Can't create"); /* EPT_S_CANT_CREATE */ + s_ErrorMessage.Add(1900, "RPC S Invalid object"); /* RPC_S_INVALID_OBJECT */ + s_ErrorMessage.Add(1901, "Invalid time"); /* ERROR_INVALID_TIME */ + s_ErrorMessage.Add(1902, "Invalid form name"); /* ERROR_INVALID_FORM_NAME */ + s_ErrorMessage.Add(1903, "Invalid form size"); /* ERROR_INVALID_FORM_SIZE */ + s_ErrorMessage.Add(1904, "Already waiting"); /* ERROR_ALREADY_WAITING */ + s_ErrorMessage.Add(1905, "Printer deleted"); /* ERROR_PRINTER_DELETED */ + s_ErrorMessage.Add(1906, "Invalid printer state"); /* ERROR_INVALID_PRINTER_STATE */ + } + + public static void InitializeErrorMessages9() + { + s_ErrorMessage.Add(1907, "Password must change"); /* ERROR_PASSWORD_MUST_CHANGE */ + s_ErrorMessage.Add(1908, "Domain controller not found"); /* ERROR_DOMAIN_CONTROLLER_NOT_FOUND */ + s_ErrorMessage.Add(1909, "Account locked out"); /* ERROR_ACCOUNT_LOCKED_OUT */ + s_ErrorMessage.Add(1910, "OR Invalid OXID"); /* OR_INVALID_OXID */ + s_ErrorMessage.Add(1911, "OR Invalid OID"); /* OR_INVALID_OID */ + s_ErrorMessage.Add(1912, "OR Invalid set"); /* OR_INVALID_SET */ + s_ErrorMessage.Add(1913, "RPC S Send incomplete"); /* RPC_S_SEND_INCOMPLETE */ + s_ErrorMessage.Add(1914, "RPC S Invalid async handle"); /* RPC_S_INVALID_ASYNC_HANDLE */ + s_ErrorMessage.Add(1915, "RPC S Invalid async call"); /* RPC_S_INVALID_ASYNC_CALL */ + s_ErrorMessage.Add(1916, "RPC X Pipe closed"); /* RPC_X_PIPE_CLOSED */ + s_ErrorMessage.Add(1917, "RPC X Pipe discipline error"); /* RPC_X_PIPE_DISCIPLINE_ERROR */ + s_ErrorMessage.Add(1918, "RPC X Pipe empty"); /* RPC_X_PIPE_EMPTY */ + s_ErrorMessage.Add(1919, "No sitename"); /* ERROR_NO_SITENAME */ + s_ErrorMessage.Add(1920, "Can't access file"); /* ERROR_CANT_ACCESS_FILE */ + s_ErrorMessage.Add(1921, "Can't resolve filename"); /* ERROR_CANT_RESOLVE_FILENAME */ + s_ErrorMessage.Add(1922, "RPC S Entry type mismatch"); /* RPC_S_ENTRY_TYPE_MISMATCH */ + s_ErrorMessage.Add(1923, "RPC S Not all objs exported"); /* RPC_S_NOT_ALL_OBJS_EXPORTED */ + s_ErrorMessage.Add(1924, "RPC S Interface not exported"); /* RPC_S_INTERFACE_NOT_EXPORTED */ + s_ErrorMessage.Add(1925, "RPC S Profile not added"); /* RPC_S_PROFILE_NOT_ADDED */ + s_ErrorMessage.Add(1926, "RPC S PRF ELT not added"); /* RPC_S_PRF_ELT_NOT_ADDED */ + s_ErrorMessage.Add(1927, "RPC S PRF ELT not removed"); /* RPC_S_PRF_ELT_NOT_REMOVED */ + s_ErrorMessage.Add(1928, "RPC S GRP ELT not added"); /* RPC_S_GRP_ELT_NOT_ADDED */ + s_ErrorMessage.Add(1929, "RPC S GRP ELT not removed"); /* RPC_S_GRP_ELT_NOT_REMOVED */ + s_ErrorMessage.Add(1930, "KM driver blocked"); /* ERROR_KM_DRIVER_BLOCKED */ + s_ErrorMessage.Add(1931, "Context expired"); /* ERROR_CONTEXT_EXPIRED */ + s_ErrorMessage.Add(2000, "Invalid pixel format"); /* ERROR_INVALID_PIXEL_FORMAT */ + s_ErrorMessage.Add(2001, "Bad driver"); /* ERROR_BAD_DRIVER */ + s_ErrorMessage.Add(2002, "Invalid window style"); /* ERROR_INVALID_WINDOW_STYLE */ + s_ErrorMessage.Add(2003, "Metafile not supported"); /* ERROR_METAFILE_NOT_SUPPORTED */ + s_ErrorMessage.Add(2004, "Transform not supported"); /* ERROR_TRANSFORM_NOT_SUPPORTED */ + s_ErrorMessage.Add(2005, "Clipping not supported"); /* ERROR_CLIPPING_NOT_SUPPORTED */ + s_ErrorMessage.Add(2010, "Invalid CMM"); /* ERROR_INVALID_CMM */ + s_ErrorMessage.Add(2011, "Invalid profile"); /* ERROR_INVALID_PROFILE */ + s_ErrorMessage.Add(2012, "Tag not found"); /* ERROR_TAG_NOT_FOUND */ + s_ErrorMessage.Add(2013, "Tag not present"); /* ERROR_TAG_NOT_PRESENT */ + s_ErrorMessage.Add(2014, "Duplicate tag"); /* ERROR_DUPLICATE_TAG */ + s_ErrorMessage.Add(2015, "Profile not associated with device"); /* ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE */ + s_ErrorMessage.Add(2016, "Profile not found"); /* ERROR_PROFILE_NOT_FOUND */ + s_ErrorMessage.Add(2017, "Invalid colorspace"); /* ERROR_INVALID_COLORSPACE */ + s_ErrorMessage.Add(2018, "ICM not enabled"); /* ERROR_ICM_NOT_ENABLED */ + s_ErrorMessage.Add(2019, "Deleting ICM xform"); /* ERROR_DELETING_ICM_XFORM */ + s_ErrorMessage.Add(2020, "Invalid transform"); /* ERROR_INVALID_TRANSFORM */ + s_ErrorMessage.Add(2021, "Colorspace mismatch"); /* ERROR_COLORSPACE_MISMATCH */ + s_ErrorMessage.Add(2022, "Invalid colorindex"); /* ERROR_INVALID_COLORINDEX */ + s_ErrorMessage.Add(2108, "Connected other password"); /* ERROR_CONNECTED_OTHER_PASSWORD */ + s_ErrorMessage.Add(2109, "Connected other password default"); /* ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT */ + s_ErrorMessage.Add(2202, "Bad username"); /* ERROR_BAD_USERNAME */ + s_ErrorMessage.Add(2250, "Not connected"); /* ERROR_NOT_CONNECTED */ + s_ErrorMessage.Add(2401, "Open files"); /* ERROR_OPEN_FILES */ + s_ErrorMessage.Add(2402, "Active connections"); /* ERROR_ACTIVE_CONNECTIONS */ + s_ErrorMessage.Add(2404, "Device in use"); /* ERROR_DEVICE_IN_USE */ + s_ErrorMessage.Add(3000, "Unknown print monitor"); /* ERROR_UNKNOWN_PRINT_MONITOR */ + s_ErrorMessage.Add(3001, "Printer driver in use"); /* ERROR_PRINTER_DRIVER_IN_USE */ + s_ErrorMessage.Add(3002, "Spool file not found"); /* ERROR_SPOOL_FILE_NOT_FOUND */ + s_ErrorMessage.Add(3003, "SPL no startdoc"); /* ERROR_SPL_NO_STARTDOC */ + s_ErrorMessage.Add(3004, "SPL no addjob"); /* ERROR_SPL_NO_ADDJOB */ + s_ErrorMessage.Add(3005, "Print processor already installed"); /* ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED */ + s_ErrorMessage.Add(3006, "Print monitor already installed"); /* ERROR_PRINT_MONITOR_ALREADY_INSTALLED */ + s_ErrorMessage.Add(3007, "Invalid print monitor"); /* ERROR_INVALID_PRINT_MONITOR */ + s_ErrorMessage.Add(3008, "Print monitor in use"); /* ERROR_PRINT_MONITOR_IN_USE */ + s_ErrorMessage.Add(3009, "Printer has jobs queued"); /* ERROR_PRINTER_HAS_JOBS_QUEUED */ + s_ErrorMessage.Add(3010, "Success reboot required"); /* ERROR_SUCCESS_REBOOT_REQUIRED */ + s_ErrorMessage.Add(3011, "Success restart required"); /* ERROR_SUCCESS_RESTART_REQUIRED */ + s_ErrorMessage.Add(3012, "Printer not found"); /* ERROR_PRINTER_NOT_FOUND */ + s_ErrorMessage.Add(3013, "Printer driver warned"); /* ERROR_PRINTER_DRIVER_WARNED */ + s_ErrorMessage.Add(3014, "Printer driver blocked"); /* ERROR_PRINTER_DRIVER_BLOCKED */ + s_ErrorMessage.Add(4000, "Wins internal"); /* ERROR_WINS_INTERNAL */ + s_ErrorMessage.Add(4001, "Can not del local wins"); /* ERROR_CAN_NOT_DEL_LOCAL_WINS */ + s_ErrorMessage.Add(4002, "Static init"); /* ERROR_STATIC_INIT */ + s_ErrorMessage.Add(4003, "Inc backup"); /* ERROR_INC_BACKUP */ + s_ErrorMessage.Add(4004, "Full backup"); /* ERROR_FULL_BACKUP */ + s_ErrorMessage.Add(4005, "Rec not existent"); /* ERROR_REC_NON_EXISTENT */ + s_ErrorMessage.Add(4006, "RPL not allowed"); /* ERROR_RPL_NOT_ALLOWED */ + s_ErrorMessage.Add(4100, "DHCP address conflict"); /* ERROR_DHCP_ADDRESS_CONFLICT */ + s_ErrorMessage.Add(4200, "WMU GUID not found"); /* ERROR_WMI_GUID_NOT_FOUND */ + s_ErrorMessage.Add(4201, "WMI instance not found"); /* ERROR_WMI_INSTANCE_NOT_FOUND */ + s_ErrorMessage.Add(4202, "WMI ItemID not found"); /* ERROR_WMI_ITEMID_NOT_FOUND */ + s_ErrorMessage.Add(4203, "WMI try again"); /* ERROR_WMI_TRY_AGAIN */ + s_ErrorMessage.Add(4204, "WMI DP not found"); /* ERROR_WMI_DP_NOT_FOUND */ + s_ErrorMessage.Add(4205, "WMI unresolved instance ref"); /* ERROR_WMI_UNRESOLVED_INSTANCE_REF */ + s_ErrorMessage.Add(4206, "WMU already enabled"); /* ERROR_WMI_ALREADY_ENABLED */ + s_ErrorMessage.Add(4207, "WMU GUID disconnected"); /* ERROR_WMI_GUID_DISCONNECTED */ + s_ErrorMessage.Add(4208, "WMI server unavailable"); /* ERROR_WMI_SERVER_UNAVAILABLE */ + s_ErrorMessage.Add(4209, "WMI DP failed"); /* ERROR_WMI_DP_FAILED */ + s_ErrorMessage.Add(4210, "WMI invalid MOF"); /* ERROR_WMI_INVALID_MOF */ + s_ErrorMessage.Add(4211, "WMI invalid reginfo"); /* ERROR_WMI_INVALID_REGINFO */ + s_ErrorMessage.Add(4212, "WMI already disabled"); /* ERROR_WMI_ALREADY_DISABLED */ + s_ErrorMessage.Add(4213, "WMI read only"); /* ERROR_WMI_READ_ONLY */ + s_ErrorMessage.Add(4214, "WMI set failure"); /* ERROR_WMI_SET_FAILURE */ + s_ErrorMessage.Add(4300, "Invalid media"); /* ERROR_INVALID_MEDIA */ + s_ErrorMessage.Add(4301, "Invalid library"); /* ERROR_INVALID_LIBRARY */ + s_ErrorMessage.Add(4302, "Invalid media pool"); /* ERROR_INVALID_MEDIA_POOL */ + s_ErrorMessage.Add(4303, "Drive media mismatch"); /* ERROR_DRIVE_MEDIA_MISMATCH */ + s_ErrorMessage.Add(4304, "Media offline"); /* ERROR_MEDIA_OFFLINE */ + s_ErrorMessage.Add(4305, "Library offline"); /* ERROR_LIBRARY_OFFLINE */ + s_ErrorMessage.Add(4306, "Empty"); /* ERROR_EMPTY */ + s_ErrorMessage.Add(4307, "Not empty"); /* ERROR_NOT_EMPTY */ + s_ErrorMessage.Add(4308, "Media unavailable"); /* ERROR_MEDIA_UNAVAILABLE */ + s_ErrorMessage.Add(4309, "Resource disabled"); /* ERROR_RESOURCE_DISABLED */ + s_ErrorMessage.Add(4310, "Invalid cleaner"); /* ERROR_INVALID_CLEANER */ + s_ErrorMessage.Add(4311, "Unable to clean"); /* ERROR_UNABLE_TO_CLEAN */ + s_ErrorMessage.Add(4312, "Object not found"); /* ERROR_OBJECT_NOT_FOUND */ + s_ErrorMessage.Add(4313, "Database failure"); /* ERROR_DATABASE_FAILURE */ + s_ErrorMessage.Add(4314, "Database full"); /* ERROR_DATABASE_FULL */ + s_ErrorMessage.Add(4315, "Media incompatible"); /* ERROR_MEDIA_INCOMPATIBLE */ + s_ErrorMessage.Add(4316, "Resource not present"); /* ERROR_RESOURCE_NOT_PRESENT */ + s_ErrorMessage.Add(4317, "Invalid operation"); /* ERROR_INVALID_OPERATION */ + s_ErrorMessage.Add(4318, "Media not available"); /* ERROR_MEDIA_NOT_AVAILABLE */ + s_ErrorMessage.Add(4319, "Device not available"); /* ERROR_DEVICE_NOT_AVAILABLE */ + s_ErrorMessage.Add(4320, "Request refused"); /* ERROR_REQUEST_REFUSED */ + s_ErrorMessage.Add(4321, "Invalid drive object"); /* ERROR_INVALID_DRIVE_OBJECT */ + s_ErrorMessage.Add(4322, "Library full"); /* ERROR_LIBRARY_FULL */ + s_ErrorMessage.Add(4323, "Medium not accessible"); /* ERROR_MEDIUM_NOT_ACCESSIBLE */ + s_ErrorMessage.Add(4324, "Unable to load medium"); /* ERROR_UNABLE_TO_LOAD_MEDIUM */ + s_ErrorMessage.Add(4325, "Unable to inventory drive"); /* ERROR_UNABLE_TO_INVENTORY_DRIVE */ + s_ErrorMessage.Add(4326, "Unable to inventory slot"); /* ERROR_UNABLE_TO_INVENTORY_SLOT */ + s_ErrorMessage.Add(4327, "Unable to inventory transport"); /* ERROR_UNABLE_TO_INVENTORY_TRANSPORT */ + s_ErrorMessage.Add(4328, "Transport full"); /* ERROR_TRANSPORT_FULL */ + s_ErrorMessage.Add(4329, "Controlling ieport"); /* ERROR_CONTROLLING_IEPORT */ + s_ErrorMessage.Add(4330, "Unable to eject mounted media"); /* ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA */ + s_ErrorMessage.Add(4331, "Cleaner slot set"); /* ERROR_CLEANER_SLOT_SET */ + s_ErrorMessage.Add(4332, "Cleaner slot not set"); /* ERROR_CLEANER_SLOT_NOT_SET */ + s_ErrorMessage.Add(4333, "Cleaner cartridge spent"); /* ERROR_CLEANER_CARTRIDGE_SPENT */ + s_ErrorMessage.Add(4334, "Unexpected omid"); /* ERROR_UNEXPECTED_OMID */ + s_ErrorMessage.Add(4335, "Can't delete last item"); /* ERROR_CANT_DELETE_LAST_ITEM */ + s_ErrorMessage.Add(4336, "Message exceeds max size"); /* ERROR_MESSAGE_EXCEEDS_MAX_SIZE */ + s_ErrorMessage.Add(4337, "Volume contains sys files"); /* ERROR_VOLUME_CONTAINS_SYS_FILES */ + s_ErrorMessage.Add(4338, "Indigenous type"); /* ERROR_INDIGENOUS_TYPE */ + s_ErrorMessage.Add(4339, "No supporting drives"); /* ERROR_NO_SUPPORTING_DRIVES */ + s_ErrorMessage.Add(4340, "Cleaner cartridge installed"); /* ERROR_CLEANER_CARTRIDGE_INSTALLED */ + s_ErrorMessage.Add(4350, "Fill offline"); /* ERROR_FILE_OFFLINE */ + s_ErrorMessage.Add(4351, "Remote storage not active"); /* ERROR_REMOTE_STORAGE_NOT_ACTIVE */ + } + + public static void InitializeErrorMessages10() + { + s_ErrorMessage.Add(4352, "Remote storage media error"); /* ERROR_REMOTE_STORAGE_MEDIA_ERROR */ + s_ErrorMessage.Add(4390, "Not a reparse point"); /* ERROR_NOT_A_REPARSE_POINT */ + s_ErrorMessage.Add(4391, "Reparse attribute conflict"); /* ERROR_REPARSE_ATTRIBUTE_CONFLICT */ + s_ErrorMessage.Add(4392, "Invalid reparse data"); /* ERROR_INVALID_REPARSE_DATA */ + s_ErrorMessage.Add(4393, "Reparse tag invalid"); /* ERROR_REPARSE_TAG_INVALID */ + s_ErrorMessage.Add(4394, "Reparse tag mismatch"); /* ERROR_REPARSE_TAG_MISMATCH */ + s_ErrorMessage.Add(4500, "Volume not sis enabled"); /* ERROR_VOLUME_NOT_SIS_ENABLED */ + s_ErrorMessage.Add(5001, "Dependent resource exists"); /* ERROR_DEPENDENT_RESOURCE_EXISTS */ + s_ErrorMessage.Add(5002, "Dependency not found"); /* ERROR_DEPENDENCY_NOT_FOUND */ + s_ErrorMessage.Add(5003, "Dependency already exists"); /* ERROR_DEPENDENCY_ALREADY_EXISTS */ + s_ErrorMessage.Add(5004, "Resource not online"); /* ERROR_RESOURCE_NOT_ONLINE */ + s_ErrorMessage.Add(5005, "Host node not available"); /* ERROR_HOST_NODE_NOT_AVAILABLE */ + s_ErrorMessage.Add(5006, "Resource not available"); /* ERROR_RESOURCE_NOT_AVAILABLE */ + s_ErrorMessage.Add(5007, "Resource not found"); /* ERROR_RESOURCE_NOT_FOUND */ + s_ErrorMessage.Add(5008, "Shutdown cluster"); /* ERROR_SHUTDOWN_CLUSTER */ + s_ErrorMessage.Add(5009, "Can't evict active node"); /* ERROR_CANT_EVICT_ACTIVE_NODE */ + s_ErrorMessage.Add(5010, "Object already exists"); /* ERROR_OBJECT_ALREADY_EXISTS */ + s_ErrorMessage.Add(5011, "Object in list"); /* ERROR_OBJECT_IN_LIST */ + s_ErrorMessage.Add(5012, "Group not available"); /* ERROR_GROUP_NOT_AVAILABLE */ + s_ErrorMessage.Add(5013, "Group not found"); /* ERROR_GROUP_NOT_FOUND */ + s_ErrorMessage.Add(5014, "Group not online"); /* ERROR_GROUP_NOT_ONLINE */ + s_ErrorMessage.Add(5015, "Host node not resource owner"); /* ERROR_HOST_NODE_NOT_RESOURCE_OWNER */ + s_ErrorMessage.Add(5016, "Host node not group owner"); /* ERROR_HOST_NODE_NOT_GROUP_OWNER */ + s_ErrorMessage.Add(5017, "Resmon create failed"); /* ERROR_RESMON_CREATE_FAILED */ + s_ErrorMessage.Add(5018, "Resmon online failed"); /* ERROR_RESMON_ONLINE_FAILED */ + s_ErrorMessage.Add(5019, "Resource online"); /* ERROR_RESOURCE_ONLINE */ + s_ErrorMessage.Add(5020, "Quorum resource"); /* ERROR_QUORUM_RESOURCE */ + s_ErrorMessage.Add(5021, "Not quorum capable"); /* ERROR_NOT_QUORUM_CAPABLE */ + s_ErrorMessage.Add(5022, "Cluster shutting down"); /* ERROR_CLUSTER_SHUTTING_DOWN */ + s_ErrorMessage.Add(5023, "Invalid state"); /* ERROR_INVALID_STATE */ + s_ErrorMessage.Add(5024, "Resource properties stored"); /* ERROR_RESOURCE_PROPERTIES_STORED */ + s_ErrorMessage.Add(5025, "Not quorum class"); /* ERROR_NOT_QUORUM_CLASS */ + s_ErrorMessage.Add(5026, "Core resource"); /* ERROR_CORE_RESOURCE */ + s_ErrorMessage.Add(5027, "Quorum resource online failed"); /* ERROR_QUORUM_RESOURCE_ONLINE_FAILED */ + s_ErrorMessage.Add(5028, "Quorumlog open failed"); /* ERROR_QUORUMLOG_OPEN_FAILED */ + s_ErrorMessage.Add(5029, "Clusterlog corrupt"); /* ERROR_CLUSTERLOG_CORRUPT */ + s_ErrorMessage.Add(5030, "Clusterlog record exceeds maxsize"); /* ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE */ + s_ErrorMessage.Add(5031, "Clusterlog exceeds maxsize"); /* ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE */ + s_ErrorMessage.Add(5032, "Clusterlog chkpoint not found"); /* ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND */ + s_ErrorMessage.Add(5033, "Clusterlog not enough space"); /* ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE */ + s_ErrorMessage.Add(5034, "Quorum owner alive"); /* ERROR_QUORUM_OWNER_ALIVE */ + s_ErrorMessage.Add(5035, "Network not available"); /* ERROR_NETWORK_NOT_AVAILABLE */ + s_ErrorMessage.Add(5036, "Node not available"); /* ERROR_NODE_NOT_AVAILABLE */ + s_ErrorMessage.Add(5037, "All nodes not available"); /* ERROR_ALL_NODES_NOT_AVAILABLE */ + s_ErrorMessage.Add(5038, "Resource failed"); /* ERROR_RESOURCE_FAILED */ + s_ErrorMessage.Add(5039, "Cluster invalid node"); /* ERROR_CLUSTER_INVALID_NODE */ + s_ErrorMessage.Add(5040, "Cluster node exists"); /* ERROR_CLUSTER_NODE_EXISTS */ + s_ErrorMessage.Add(5041, "Cluster join in progress"); /* ERROR_CLUSTER_JOIN_IN_PROGRESS */ + s_ErrorMessage.Add(5042, "Cluster node not found"); /* ERROR_CLUSTER_NODE_NOT_FOUND */ + s_ErrorMessage.Add(5043, "Cluster local node not found"); /* ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND */ + s_ErrorMessage.Add(5044, "Cluster network exists"); /* ERROR_CLUSTER_NETWORK_EXISTS */ + s_ErrorMessage.Add(5045, "Cluster network not found"); /* ERROR_CLUSTER_NETWORK_NOT_FOUND */ + s_ErrorMessage.Add(5046, "Cluster netinterface exists"); /* ERROR_CLUSTER_NETINTERFACE_EXISTS */ + s_ErrorMessage.Add(5047, "Cluster netinterface not found"); /* ERROR_CLUSTER_NETINTERFACE_NOT_FOUND */ + s_ErrorMessage.Add(5048, "Cluster invalid request"); /* ERROR_CLUSTER_INVALID_REQUEST */ + s_ErrorMessage.Add(5049, "Cluster invalid network provider"); /* ERROR_CLUSTER_INVALID_NETWORK_PROVIDER */ + s_ErrorMessage.Add(5050, "Cluster node down"); /* ERROR_CLUSTER_NODE_DOWN */ + s_ErrorMessage.Add(5051, "Cluster node unreachable"); /* ERROR_CLUSTER_NODE_UNREACHABLE */ + s_ErrorMessage.Add(5052, "Cluster node not member"); /* ERROR_CLUSTER_NODE_NOT_MEMBER */ + s_ErrorMessage.Add(5053, "Cluster join not in progress"); /* ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS */ + s_ErrorMessage.Add(5054, "Cluster invalid network"); /* ERROR_CLUSTER_INVALID_NETWORK */ + s_ErrorMessage.Add(5056, "Cluster node up"); /* ERROR_CLUSTER_NODE_UP */ + s_ErrorMessage.Add(5057, "Cluster ipaddr in use"); /* ERROR_CLUSTER_IPADDR_IN_USE */ + s_ErrorMessage.Add(5058, "Cluster node not paused"); /* ERROR_CLUSTER_NODE_NOT_PAUSED */ + s_ErrorMessage.Add(5059, "Cluster no security context"); /* ERROR_CLUSTER_NO_SECURITY_CONTEXT */ + s_ErrorMessage.Add(5060, "Cluster network not internal"); /* ERROR_CLUSTER_NETWORK_NOT_INTERNAL */ + s_ErrorMessage.Add(5061, "Cluster node already up"); /* ERROR_CLUSTER_NODE_ALREADY_UP */ + s_ErrorMessage.Add(5062, "Cluster node already down"); /* ERROR_CLUSTER_NODE_ALREADY_DOWN */ + s_ErrorMessage.Add(5063, "Cluster network already online"); /* ERROR_CLUSTER_NETWORK_ALREADY_ONLINE */ + s_ErrorMessage.Add(5064, "Cluster network already offline"); /* ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE */ + s_ErrorMessage.Add(5065, "Cluster node already member"); /* ERROR_CLUSTER_NODE_ALREADY_MEMBER */ + s_ErrorMessage.Add(5066, "Cluster last internal network"); /* ERROR_CLUSTER_LAST_INTERNAL_NETWORK */ + s_ErrorMessage.Add(5067, "Cluster network has dependents"); /* ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS */ + s_ErrorMessage.Add(5068, "Invalid operation on quorum"); /* ERROR_INVALID_OPERATION_ON_QUORUM */ + s_ErrorMessage.Add(5069, "Dependency not allowed"); /* ERROR_DEPENDENCY_NOT_ALLOWED */ + s_ErrorMessage.Add(5070, "Cluster node paused"); /* ERROR_CLUSTER_NODE_PAUSED */ + s_ErrorMessage.Add(5071, "Node can't host resource"); /* ERROR_NODE_CANT_HOST_RESOURCE */ + s_ErrorMessage.Add(5072, "Cluster node not ready"); /* ERROR_CLUSTER_NODE_NOT_READY */ + s_ErrorMessage.Add(5073, "Cluster node shutting down"); /* ERROR_CLUSTER_NODE_SHUTTING_DOWN */ + s_ErrorMessage.Add(5074, "Cluster join aborted"); /* ERROR_CLUSTER_JOIN_ABORTED */ + s_ErrorMessage.Add(5075, "Cluster incompatible versions"); /* ERROR_CLUSTER_INCOMPATIBLE_VERSIONS */ + s_ErrorMessage.Add(5076, "Cluster maxnum of resources exceeded"); /* ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED */ + s_ErrorMessage.Add(5077, "Cluster system config changed"); /* ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED */ + s_ErrorMessage.Add(5078, "Cluster resource type not found"); /* ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND */ + s_ErrorMessage.Add(5079, "Cluster restype not supported"); /* ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED */ + s_ErrorMessage.Add(5080, "Cluster resname not found"); /* ERROR_CLUSTER_RESNAME_NOT_FOUND */ + s_ErrorMessage.Add(5081, "Cluster no RPC packages registered"); /* ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED */ + s_ErrorMessage.Add(5082, "Cluster owner not in preflist"); /* ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST */ + s_ErrorMessage.Add(5083, "Cluster database seqmismatch"); /* ERROR_CLUSTER_DATABASE_SEQMISMATCH */ + s_ErrorMessage.Add(5084, "Resmon invalid state"); /* ERROR_RESMON_INVALID_STATE */ + s_ErrorMessage.Add(5085, "Cluster gum not locker"); /* ERROR_CLUSTER_GUM_NOT_LOCKER */ + s_ErrorMessage.Add(5086, "Quorum disk not found"); /* ERROR_QUORUM_DISK_NOT_FOUND */ + s_ErrorMessage.Add(5087, "Database backup corrupt"); /* ERROR_DATABASE_BACKUP_CORRUPT */ + s_ErrorMessage.Add(5088, "Cluster node already has DFS root"); /* ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT */ + s_ErrorMessage.Add(5089, "Resource property unchangeable"); /* ERROR_RESOURCE_PROPERTY_UNCHANGEABLE */ + s_ErrorMessage.Add(5890, "Cluster membership invalid state"); /* ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE */ + s_ErrorMessage.Add(5891, "Cluster quorumlog not found"); /* ERROR_CLUSTER_QUORUMLOG_NOT_FOUND */ + s_ErrorMessage.Add(5892, "Cluster membership halt"); /* ERROR_CLUSTER_MEMBERSHIP_HALT */ + s_ErrorMessage.Add(5893, "Cluster instance ID mismatch"); /* ERROR_CLUSTER_INSTANCE_ID_MISMATCH */ + s_ErrorMessage.Add(5894, "Cluster network not found for IP"); /* ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP */ + s_ErrorMessage.Add(5895, "Cluster property data type mismatch"); /* ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH */ + s_ErrorMessage.Add(5896, "Cluster evict without cleanup"); /* ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP */ + s_ErrorMessage.Add(5897, "Cluster parameter mismatch"); /* ERROR_CLUSTER_PARAMETER_MISMATCH */ + s_ErrorMessage.Add(5898, "Node cannot be clustered"); /* ERROR_NODE_CANNOT_BE_CLUSTERED */ + s_ErrorMessage.Add(5899, "Cluster wrong OS version"); /* ERROR_CLUSTER_WRONG_OS_VERSION */ + s_ErrorMessage.Add(5900, "Cluster can't create dup cluster name"); /* ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME */ + s_ErrorMessage.Add(6001, "Decryption failed"); /* ERROR_DECRYPTION_FAILED */ + s_ErrorMessage.Add(6002, "File encrypted"); /* ERROR_FILE_ENCRYPTED */ + s_ErrorMessage.Add(6003, "No recovery policy"); /* ERROR_NO_RECOVERY_POLICY */ + s_ErrorMessage.Add(6004, "No EFS"); /* ERROR_NO_EFS */ + s_ErrorMessage.Add(6005, "Wrong EFS"); /* ERROR_WRONG_EFS */ + s_ErrorMessage.Add(6006, "No user keys"); /* ERROR_NO_USER_KEYS */ + s_ErrorMessage.Add(6007, "File not encryped"); /* ERROR_FILE_NOT_ENCRYPTED */ + s_ErrorMessage.Add(6008, "Not export format"); /* ERROR_NOT_EXPORT_FORMAT */ + s_ErrorMessage.Add(6009, "File read only"); /* ERROR_FILE_READ_ONLY */ + s_ErrorMessage.Add(6010, "Dir EFS disallowed"); /* ERROR_DIR_EFS_DISALLOWED */ + s_ErrorMessage.Add(6011, "EFS server not trusted"); /* ERROR_EFS_SERVER_NOT_TRUSTED */ + s_ErrorMessage.Add(6012, "Bad recovery policy"); /* ERROR_BAD_RECOVERY_POLICY */ + s_ErrorMessage.Add(6013, "ETS alg blob too big"); /* ERROR_EFS_ALG_BLOB_TOO_BIG */ + s_ErrorMessage.Add(6014, "Volume not support EFS"); /* ERROR_VOLUME_NOT_SUPPORT_EFS */ + s_ErrorMessage.Add(6015, "EFS disabled"); /* ERROR_EFS_DISABLED */ + s_ErrorMessage.Add(6016, "EFS version not support"); /* ERROR_EFS_VERSION_NOT_SUPPORT */ + } + + public static void InitializeErrorMessages11() + { + s_ErrorMessage.Add(6118, "No browser servers found"); /* ERROR_NO_BROWSER_SERVERS_FOUND */ + s_ErrorMessage.Add(6200, "Sched E service not localsystem"); /* SCHED_E_SERVICE_NOT_LOCALSYSTEM */ + s_ErrorMessage.Add(7001, "Ctx winstation name invalid"); /* ERROR_CTX_WINSTATION_NAME_INVALID */ + s_ErrorMessage.Add(7002, "Ctx invalid PD"); /* ERROR_CTX_INVALID_PD */ + s_ErrorMessage.Add(7003, "Ctx PD not found"); /* ERROR_CTX_PD_NOT_FOUND */ + s_ErrorMessage.Add(7004, "Ctx WD not found"); /* ERROR_CTX_WD_NOT_FOUND */ + s_ErrorMessage.Add(7005, "Ctx cannot make eventlog entry"); /* ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY */ + s_ErrorMessage.Add(7006, "Ctx service name collision"); /* ERROR_CTX_SERVICE_NAME_COLLISION */ + s_ErrorMessage.Add(7007, "Ctx close pending"); /* ERROR_CTX_CLOSE_PENDING */ + s_ErrorMessage.Add(7008, "Ctx no outbuf"); /* ERROR_CTX_NO_OUTBUF */ + s_ErrorMessage.Add(7009, "Ctx modem inf not found"); /* ERROR_CTX_MODEM_INF_NOT_FOUND */ + s_ErrorMessage.Add(7010, "Ctx invalid modemname"); /* ERROR_CTX_INVALID_MODEMNAME */ + s_ErrorMessage.Add(7011, "Ctx modem response error"); /* ERROR_CTX_MODEM_RESPONSE_ERROR */ + s_ErrorMessage.Add(7012, "Ctx modem response timeout"); /* ERROR_CTX_MODEM_RESPONSE_TIMEOUT */ + s_ErrorMessage.Add(7013, "Ctx modem response no carrier"); /* ERROR_CTX_MODEM_RESPONSE_NO_CARRIER */ + s_ErrorMessage.Add(7014, "Ctx modem response no dial tone"); /* ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE */ + s_ErrorMessage.Add(7015, "Ctx modem response busy"); /* ERROR_CTX_MODEM_RESPONSE_BUSY */ + s_ErrorMessage.Add(7016, "Ctx modem response voice"); /* ERROR_CTX_MODEM_RESPONSE_VOICE */ + s_ErrorMessage.Add(7017, "Ctx TD error"); /* ERROR_CTX_TD_ERROR */ + s_ErrorMessage.Add(7022, "Ctx winstation not found"); /* ERROR_CTX_WINSTATION_NOT_FOUND */ + s_ErrorMessage.Add(7023, "Ctx winstation already exists"); /* ERROR_CTX_WINSTATION_ALREADY_EXISTS */ + s_ErrorMessage.Add(7024, "Ctx winstation busy"); /* ERROR_CTX_WINSTATION_BUSY */ + s_ErrorMessage.Add(7025, "Ctx bad video mode"); /* ERROR_CTX_BAD_VIDEO_MODE */ + s_ErrorMessage.Add(7035, "Ctx graphics invalid"); /* ERROR_CTX_GRAPHICS_INVALID */ + s_ErrorMessage.Add(7037, "Ctx logon disabled"); /* ERROR_CTX_LOGON_DISABLED */ + s_ErrorMessage.Add(7038, "Ctx not console"); /* ERROR_CTX_NOT_CONSOLE */ + s_ErrorMessage.Add(7040, "Ctx client query timeout"); /* ERROR_CTX_CLIENT_QUERY_TIMEOUT */ + s_ErrorMessage.Add(7041, "Ctx console disconnect"); /* ERROR_CTX_CONSOLE_DISCONNECT */ + s_ErrorMessage.Add(7042, "Ctx console connect"); /* ERROR_CTX_CONSOLE_CONNECT */ + s_ErrorMessage.Add(7044, "Ctx shadow denied"); /* ERROR_CTX_SHADOW_DENIED */ + s_ErrorMessage.Add(7045, "Ctx winstation access denied"); /* ERROR_CTX_WINSTATION_ACCESS_DENIED */ + s_ErrorMessage.Add(7049, "Ctx invalid WD"); /* ERROR_CTX_INVALID_WD */ + s_ErrorMessage.Add(7050, "Ctx shadow invalid"); /* ERROR_CTX_SHADOW_INVALID */ + s_ErrorMessage.Add(7051, "Ctx shadow disabled"); /* ERROR_CTX_SHADOW_DISABLED */ + s_ErrorMessage.Add(7052, "Ctx client licence in use"); /* ERROR_CTX_CLIENT_LICENSE_IN_USE */ + s_ErrorMessage.Add(7053, "Ctx client licence not set"); /* ERROR_CTX_CLIENT_LICENSE_NOT_SET */ + s_ErrorMessage.Add(7054, "Ctx licence not available"); /* ERROR_CTX_LICENSE_NOT_AVAILABLE */ + s_ErrorMessage.Add(7055, "Ctx licence client invalid"); /* ERROR_CTX_LICENSE_CLIENT_INVALID */ + s_ErrorMessage.Add(7056, "Ctx licence expired"); /* ERROR_CTX_LICENSE_EXPIRED */ + s_ErrorMessage.Add(7057, "Ctx shadow not running"); /* ERROR_CTX_SHADOW_NOT_RUNNING */ + s_ErrorMessage.Add(7058, "Ctx shadow ended by mode change"); /* ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE */ + s_ErrorMessage.Add(8001, "FRS err invalid API sequence"); /* FRS_ERR_INVALID_API_SEQUENCE */ + s_ErrorMessage.Add(8002, "FRS err starting service"); /* FRS_ERR_STARTING_SERVICE */ + s_ErrorMessage.Add(8003, "FRS err stopping service"); /* FRS_ERR_STOPPING_SERVICE */ + s_ErrorMessage.Add(8004, "FRS err internal API"); /* FRS_ERR_INTERNAL_API */ + s_ErrorMessage.Add(8005, "FRS err internal"); /* FRS_ERR_INTERNAL */ + s_ErrorMessage.Add(8006, "FRS err service comm"); /* FRS_ERR_SERVICE_COMM */ + s_ErrorMessage.Add(8007, "FRS err insufficient priv"); /* FRS_ERR_INSUFFICIENT_PRIV */ + s_ErrorMessage.Add(8008, "FRS err authentication"); /* FRS_ERR_AUTHENTICATION */ + s_ErrorMessage.Add(8009, "FRS err parent insufficient priv"); /* FRS_ERR_PARENT_INSUFFICIENT_PRIV */ + s_ErrorMessage.Add(8010, "FRS err parent authentication"); /* FRS_ERR_PARENT_AUTHENTICATION */ + s_ErrorMessage.Add(8011, "FRS err child to parent comm"); /* FRS_ERR_CHILD_TO_PARENT_COMM */ + s_ErrorMessage.Add(8012, "FRS err parent to child comm"); /* FRS_ERR_PARENT_TO_CHILD_COMM */ + s_ErrorMessage.Add(8013, "FRS err sysvol populate"); /* FRS_ERR_SYSVOL_POPULATE */ + s_ErrorMessage.Add(8014, "FRS err sysvol populate timeout"); /* FRS_ERR_SYSVOL_POPULATE_TIMEOUT */ + s_ErrorMessage.Add(8015, "FRS err sysvol is busy"); /* FRS_ERR_SYSVOL_IS_BUSY */ + s_ErrorMessage.Add(8016, "FRS err sysvol demote"); /* FRS_ERR_SYSVOL_DEMOTE */ + s_ErrorMessage.Add(8017, "FRS err invalid service parameter"); /* FRS_ERR_INVALID_SERVICE_PARAMETER */ + s_ErrorMessage.Add(8200, "DS not installed"); /* ERROR_DS_NOT_INSTALLED */ + s_ErrorMessage.Add(8201, "DS membership evaluated locally"); /* ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY */ + s_ErrorMessage.Add(8202, "DS no attribute or value"); /* ERROR_DS_NO_ATTRIBUTE_OR_VALUE */ + s_ErrorMessage.Add(8203, "DS invalid attribute syntax"); /* ERROR_DS_INVALID_ATTRIBUTE_SYNTAX */ + s_ErrorMessage.Add(8204, "DS attribute type undefined"); /* ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED */ + s_ErrorMessage.Add(8205, "DS attribute or value exists"); /* ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS */ + s_ErrorMessage.Add(8206, "DS busy"); /* ERROR_DS_BUSY */ + s_ErrorMessage.Add(8207, "DS unavailable"); /* ERROR_DS_UNAVAILABLE */ + s_ErrorMessage.Add(8208, "DS no rids allocated"); /* ERROR_DS_NO_RIDS_ALLOCATED */ + s_ErrorMessage.Add(8209, "DS no more rids"); /* ERROR_DS_NO_MORE_RIDS */ + s_ErrorMessage.Add(8210, "DS incorrect role owner"); /* ERROR_DS_INCORRECT_ROLE_OWNER */ + s_ErrorMessage.Add(8211, "DS ridmgr init error"); /* ERROR_DS_RIDMGR_INIT_ERROR */ + s_ErrorMessage.Add(8212, "DS obj class violation"); /* ERROR_DS_OBJ_CLASS_VIOLATION */ + s_ErrorMessage.Add(8213, "DS can't on non leaf"); /* ERROR_DS_CANT_ON_NON_LEAF */ + s_ErrorMessage.Add(8214, "DS can't on rnd"); /* ERROR_DS_CANT_ON_RDN */ + s_ErrorMessage.Add(8215, "DS can't mod obj class"); /* ERROR_DS_CANT_MOD_OBJ_CLASS */ + s_ErrorMessage.Add(8216, "DS cross dom move error"); /* ERROR_DS_CROSS_DOM_MOVE_ERROR */ + s_ErrorMessage.Add(8217, "DS GC not available"); /* ERROR_DS_GC_NOT_AVAILABLE */ + s_ErrorMessage.Add(8218, "Shared policy"); /* ERROR_SHARED_POLICY */ + s_ErrorMessage.Add(8219, "Policy object not found"); /* ERROR_POLICY_OBJECT_NOT_FOUND */ + s_ErrorMessage.Add(8220, "Policy only in DS"); /* ERROR_POLICY_ONLY_IN_DS */ + s_ErrorMessage.Add(8221, "Promotion active"); /* ERROR_PROMOTION_ACTIVE */ + s_ErrorMessage.Add(8222, "No promotion active"); /* ERROR_NO_PROMOTION_ACTIVE */ + s_ErrorMessage.Add(8224, "DS operations error"); /* ERROR_DS_OPERATIONS_ERROR */ + s_ErrorMessage.Add(8225, "DS protocol error"); /* ERROR_DS_PROTOCOL_ERROR */ + s_ErrorMessage.Add(8226, "DS timelimit exceeded"); /* ERROR_DS_TIMELIMIT_EXCEEDED */ + s_ErrorMessage.Add(8227, "DS sizelimit exceeded"); /* ERROR_DS_SIZELIMIT_EXCEEDED */ + s_ErrorMessage.Add(8228, "DS admin limit exceeded"); /* ERROR_DS_ADMIN_LIMIT_EXCEEDED */ + s_ErrorMessage.Add(8229, "DS compare false"); /* ERROR_DS_COMPARE_FALSE */ + s_ErrorMessage.Add(8230, "DS compare true"); /* ERROR_DS_COMPARE_TRUE */ + s_ErrorMessage.Add(8231, "DS auth method not supported"); /* ERROR_DS_AUTH_METHOD_NOT_SUPPORTED */ + s_ErrorMessage.Add(8232, "DS strong auth required"); /* ERROR_DS_STRONG_AUTH_REQUIRED */ + s_ErrorMessage.Add(8233, "DS inappropriate auth"); /* ERROR_DS_INAPPROPRIATE_AUTH */ + s_ErrorMessage.Add(8234, "DS auth unknown"); /* ERROR_DS_AUTH_UNKNOWN */ + s_ErrorMessage.Add(8235, "DS referral"); /* ERROR_DS_REFERRAL */ + s_ErrorMessage.Add(8236, "DS unavailable crit extension"); /* ERROR_DS_UNAVAILABLE_CRIT_EXTENSION */ + s_ErrorMessage.Add(8237, "DS confidentiality required"); /* ERROR_DS_CONFIDENTIALITY_REQUIRED */ + s_ErrorMessage.Add(8238, "DS inappropriate matching"); /* ERROR_DS_INAPPROPRIATE_MATCHING */ + s_ErrorMessage.Add(8239, "DS constraint violation"); /* ERROR_DS_CONSTRAINT_VIOLATION */ + s_ErrorMessage.Add(8240, "DS no such object"); /* ERROR_DS_NO_SUCH_OBJECT */ + s_ErrorMessage.Add(8241, "DS alias problem"); /* ERROR_DS_ALIAS_PROBLEM */ + s_ErrorMessage.Add(8242, "DS invalid dn syntax"); /* ERROR_DS_INVALID_DN_SYNTAX */ + s_ErrorMessage.Add(8243, "DS is leaf"); /* ERROR_DS_IS_LEAF */ + s_ErrorMessage.Add(8244, "DS alias deref problem"); /* ERROR_DS_ALIAS_DEREF_PROBLEM */ + s_ErrorMessage.Add(8245, "DS unwilling to perform"); /* ERROR_DS_UNWILLING_TO_PERFORM */ + s_ErrorMessage.Add(8246, "DS loop detect"); /* ERROR_DS_LOOP_DETECT */ + s_ErrorMessage.Add(8247, "DS naming violation"); /* ERROR_DS_NAMING_VIOLATION */ + s_ErrorMessage.Add(8248, "DS object results too large"); /* ERROR_DS_OBJECT_RESULTS_TOO_LARGE */ + s_ErrorMessage.Add(8249, "DS affects multiple dsas"); /* ERROR_DS_AFFECTS_MULTIPLE_DSAS */ + s_ErrorMessage.Add(8250, "DS server down"); /* ERROR_DS_SERVER_DOWN */ + s_ErrorMessage.Add(8251, "DS local error"); /* ERROR_DS_LOCAL_ERROR */ + s_ErrorMessage.Add(8252, "DS encoding error"); /* ERROR_DS_ENCODING_ERROR */ + s_ErrorMessage.Add(8253, "DS decoding error"); /* ERROR_DS_DECODING_ERROR */ + s_ErrorMessage.Add(8254, "DS filter unknown"); /* ERROR_DS_FILTER_UNKNOWN */ + s_ErrorMessage.Add(8255, "DS param error"); /* ERROR_DS_PARAM_ERROR */ + s_ErrorMessage.Add(8256, "DS not supported"); /* ERROR_DS_NOT_SUPPORTED */ + s_ErrorMessage.Add(8257, "DS no results returned"); /* ERROR_DS_NO_RESULTS_RETURNED */ + s_ErrorMessage.Add(8258, "DS control not found"); /* ERROR_DS_CONTROL_NOT_FOUND */ + s_ErrorMessage.Add(8259, "DS client loop"); /* ERROR_DS_CLIENT_LOOP */ + s_ErrorMessage.Add(8260, "DS referral limit exceeded"); /* ERROR_DS_REFERRAL_LIMIT_EXCEEDED */ + s_ErrorMessage.Add(8261, "DS sort control missing"); /* ERROR_DS_SORT_CONTROL_MISSING */ + s_ErrorMessage.Add(8262, "DS offset range error"); /* ERROR_DS_OFFSET_RANGE_ERROR */ + s_ErrorMessage.Add(8301, "DS root must be nc"); /* ERROR_DS_ROOT_MUST_BE_NC */ + s_ErrorMessage.Add(8302, "DS and replica inhibited"); /* ERROR_DS_ADD_REPLICA_INHIBITED */ + s_ErrorMessage.Add(8303, "DS att not def in schema"); /* ERROR_DS_ATT_NOT_DEF_IN_SCHEMA */ + } + + public static void InitializeErrorMessages12() + { + s_ErrorMessage.Add(8304, "DS max obj size exceeded"); /* ERROR_DS_MAX_OBJ_SIZE_EXCEEDED */ + s_ErrorMessage.Add(8305, "DS obj string name exists"); /* ERROR_DS_OBJ_STRING_NAME_EXISTS */ + s_ErrorMessage.Add(8306, "DS no rdn defined in schema"); /* ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA */ + s_ErrorMessage.Add(8307, "DS rdn doesn't match schema"); /* ERROR_DS_RDN_DOESNT_MATCH_SCHEMA */ + s_ErrorMessage.Add(8308, "DS no requested atts found"); /* ERROR_DS_NO_REQUESTED_ATTS_FOUND */ + s_ErrorMessage.Add(8309, "DS user buffer too small"); /* ERROR_DS_USER_BUFFER_TO_SMALL */ + s_ErrorMessage.Add(8310, "DS att is not on obj"); /* ERROR_DS_ATT_IS_NOT_ON_OBJ */ + s_ErrorMessage.Add(8311, "DS illegal mod operation"); /* ERROR_DS_ILLEGAL_MOD_OPERATION */ + s_ErrorMessage.Add(8312, "DS obj too large"); /* ERROR_DS_OBJ_TOO_LARGE */ + s_ErrorMessage.Add(8313, "DS bad instance type"); /* ERROR_DS_BAD_INSTANCE_TYPE */ + s_ErrorMessage.Add(8314, "DS masterdsa required"); /* ERROR_DS_MASTERDSA_REQUIRED */ + s_ErrorMessage.Add(8315, "DS object class required"); /* ERROR_DS_OBJECT_CLASS_REQUIRED */ + s_ErrorMessage.Add(8316, "DS missing required att"); /* ERROR_DS_MISSING_REQUIRED_ATT */ + s_ErrorMessage.Add(8317, "DS att not def for class"); /* ERROR_DS_ATT_NOT_DEF_FOR_CLASS */ + s_ErrorMessage.Add(8318, "DS att already exists"); /* ERROR_DS_ATT_ALREADY_EXISTS */ + s_ErrorMessage.Add(8320, "DS can't add att values"); /* ERROR_DS_CANT_ADD_ATT_VALUES */ + s_ErrorMessage.Add(8321, "DS single value constraint"); /* ERROR_DS_SINGLE_VALUE_CONSTRAINT */ + s_ErrorMessage.Add(8322, "DS range constraint"); /* ERROR_DS_RANGE_CONSTRAINT */ + s_ErrorMessage.Add(8323, "DS att val already exists"); /* ERROR_DS_ATT_VAL_ALREADY_EXISTS */ + s_ErrorMessage.Add(8324, "DS can't rem missing att"); /* ERROR_DS_CANT_REM_MISSING_ATT */ + s_ErrorMessage.Add(8325, "DS can't rem missing att val"); /* ERROR_DS_CANT_REM_MISSING_ATT_VAL */ + s_ErrorMessage.Add(8326, "DS root can't be subref"); /* ERROR_DS_ROOT_CANT_BE_SUBREF */ + s_ErrorMessage.Add(8327, "DS no chaining"); /* ERROR_DS_NO_CHAINING */ + s_ErrorMessage.Add(8328, "DS no chained eval"); /* ERROR_DS_NO_CHAINED_EVAL */ + s_ErrorMessage.Add(8329, "DS no parent object"); /* ERROR_DS_NO_PARENT_OBJECT */ + s_ErrorMessage.Add(8330, "DS parent is an alias"); /* ERROR_DS_PARENT_IS_AN_ALIAS */ + s_ErrorMessage.Add(8331, "DS can't mix master and reps"); /* ERROR_DS_CANT_MIX_MASTER_AND_REPS */ + s_ErrorMessage.Add(8332, "DS children exist"); /* ERROR_DS_CHILDREN_EXIST */ + s_ErrorMessage.Add(8333, "DS obj not found"); /* ERROR_DS_OBJ_NOT_FOUND */ + s_ErrorMessage.Add(8334, "DS aliased obj missing"); /* ERROR_DS_ALIASED_OBJ_MISSING */ + s_ErrorMessage.Add(8335, "DS bad name syntax"); /* ERROR_DS_BAD_NAME_SYNTAX */ + s_ErrorMessage.Add(8336, "DS alias points to alias"); /* ERROR_DS_ALIAS_POINTS_TO_ALIAS */ + s_ErrorMessage.Add(8337, "DS can't redef alias"); /* ERROR_DS_CANT_DEREF_ALIAS */ + s_ErrorMessage.Add(8338, "DS out of scope"); /* ERROR_DS_OUT_OF_SCOPE */ + s_ErrorMessage.Add(8339, "DS object being removed"); /* ERROR_DS_OBJECT_BEING_REMOVED */ + s_ErrorMessage.Add(8340, "DS can't delete dsa obj"); /* ERROR_DS_CANT_DELETE_DSA_OBJ */ + s_ErrorMessage.Add(8341, "DS generic error"); /* ERROR_DS_GENERIC_ERROR */ + s_ErrorMessage.Add(8342, "DS dsa must be int master"); /* ERROR_DS_DSA_MUST_BE_INT_MASTER */ + s_ErrorMessage.Add(8343, "DS class not dsa"); /* ERROR_DS_CLASS_NOT_DSA */ + s_ErrorMessage.Add(8344, "DS insuff access rights"); /* ERROR_DS_INSUFF_ACCESS_RIGHTS */ + s_ErrorMessage.Add(8345, "DS illegal superior"); /* ERROR_DS_ILLEGAL_SUPERIOR */ + s_ErrorMessage.Add(8346, "DS attribute owned by sam"); /* ERROR_DS_ATTRIBUTE_OWNED_BY_SAM */ + s_ErrorMessage.Add(8347, "DS name too many parts"); /* ERROR_DS_NAME_TOO_MANY_PARTS */ + s_ErrorMessage.Add(8348, "DS name too long"); /* ERROR_DS_NAME_TOO_LONG */ + s_ErrorMessage.Add(8349, "DS name value too long"); /* ERROR_DS_NAME_VALUE_TOO_LONG */ + s_ErrorMessage.Add(8350, "DS name unparseable"); /* ERROR_DS_NAME_UNPARSEABLE */ + s_ErrorMessage.Add(8351, "DS name type unknown"); /* ERROR_DS_NAME_TYPE_UNKNOWN */ + s_ErrorMessage.Add(8352, "DS not an object"); /* ERROR_DS_NOT_AN_OBJECT */ + s_ErrorMessage.Add(8353, "DS sec desc too short"); /* ERROR_DS_SEC_DESC_TOO_SHORT */ + s_ErrorMessage.Add(8354, "DS sec desc invalid"); /* ERROR_DS_SEC_DESC_INVALID */ + s_ErrorMessage.Add(8355, "DS no deleted name"); /* ERROR_DS_NO_DELETED_NAME */ + s_ErrorMessage.Add(8356, "DS subref must have parent"); /* ERROR_DS_SUBREF_MUST_HAVE_PARENT */ + s_ErrorMessage.Add(8357, "DS ncname must be nc"); /* ERROR_DS_NCNAME_MUST_BE_NC */ + s_ErrorMessage.Add(8358, "DS can't add system only"); /* ERROR_DS_CANT_ADD_SYSTEM_ONLY */ + s_ErrorMessage.Add(8359, "DS class must be concrete"); /* ERROR_DS_CLASS_MUST_BE_CONCRETE */ + s_ErrorMessage.Add(8360, "DS invalid dmd"); /* ERROR_DS_INVALID_DMD */ + s_ErrorMessage.Add(8361, "DS obj GUID exists"); /* ERROR_DS_OBJ_GUID_EXISTS */ + s_ErrorMessage.Add(8362, "DS not on backlink"); /* ERROR_DS_NOT_ON_BACKLINK */ + s_ErrorMessage.Add(8363, "DS no crossref for nc"); /* ERROR_DS_NO_CROSSREF_FOR_NC */ + s_ErrorMessage.Add(8364, "DS shutting down"); /* ERROR_DS_SHUTTING_DOWN */ + s_ErrorMessage.Add(8365, "DS unknown operation"); /* ERROR_DS_UNKNOWN_OPERATION */ + s_ErrorMessage.Add(8366, "DS invalid role owner"); /* ERROR_DS_INVALID_ROLE_OWNER */ + s_ErrorMessage.Add(8367, "DS couldn't contact fsmo"); /* ERROR_DS_COULDNT_CONTACT_FSMO */ + s_ErrorMessage.Add(8368, "DS cross nc dn rename"); /* ERROR_DS_CROSS_NC_DN_RENAME */ + s_ErrorMessage.Add(8369, "DS can't mod system only"); /* ERROR_DS_CANT_MOD_SYSTEM_ONLY */ + s_ErrorMessage.Add(8370, "DS replicator only"); /* ERROR_DS_REPLICATOR_ONLY */ + s_ErrorMessage.Add(8371, "DS obj class not defined"); /* ERROR_DS_OBJ_CLASS_NOT_DEFINED */ + s_ErrorMessage.Add(8372, "DS obj class not subclass"); /* ERROR_DS_OBJ_CLASS_NOT_SUBCLASS */ + s_ErrorMessage.Add(8373, "DS name reference invalid"); /* ERROR_DS_NAME_REFERENCE_INVALID */ + s_ErrorMessage.Add(8374, "DS cross ref exists"); /* ERROR_DS_CROSS_REF_EXISTS */ + s_ErrorMessage.Add(8375, "DS can't del master crossref"); /* ERROR_DS_CANT_DEL_MASTER_CROSSREF */ + s_ErrorMessage.Add(8376, "DS subtree notify not nc head"); /* ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD */ + s_ErrorMessage.Add(8377, "DS notify filter too complex"); /* ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX */ + s_ErrorMessage.Add(8378, "DS dup rdn"); /* ERROR_DS_DUP_RDN */ + s_ErrorMessage.Add(8379, "DS dup oid"); /* ERROR_DS_DUP_OID */ + s_ErrorMessage.Add(8380, "DS dup mapi ID"); /* ERROR_DS_DUP_MAPI_ID */ + s_ErrorMessage.Add(8381, "DS dup schema ID GUID"); /* ERROR_DS_DUP_SCHEMA_ID_GUID */ + s_ErrorMessage.Add(8382, "DS dup LDAP display name"); /* ERROR_DS_DUP_LDAP_DISPLAY_NAME */ + s_ErrorMessage.Add(8383, "DS semantic att test"); /* ERROR_DS_SEMANTIC_ATT_TEST */ + s_ErrorMessage.Add(8384, "DS syntax mismatch"); /* ERROR_DS_SYNTAX_MISMATCH */ + s_ErrorMessage.Add(8385, "DS exists in must have"); /* ERROR_DS_EXISTS_IN_MUST_HAVE */ + s_ErrorMessage.Add(8386, "DS exists in may have"); /* ERROR_DS_EXISTS_IN_MAY_HAVE */ + s_ErrorMessage.Add(8387, "DS nonexistent may have"); /* ERROR_DS_NONEXISTENT_MAY_HAVE */ + s_ErrorMessage.Add(8388, "DS nonexistent must have"); /* ERROR_DS_NONEXISTENT_MUST_HAVE */ + s_ErrorMessage.Add(8389, "DS aux cls test fail"); /* ERROR_DS_AUX_CLS_TEST_FAIL */ + s_ErrorMessage.Add(8390, "DS nonexistent poss sup"); /* ERROR_DS_NONEXISTENT_POSS_SUP */ + s_ErrorMessage.Add(8391, "DS sub cls test fail"); /* ERROR_DS_SUB_CLS_TEST_FAIL */ + s_ErrorMessage.Add(8392, "DS bad rdn att ID syntax"); /* ERROR_DS_BAD_RDN_ATT_ID_SYNTAX */ + s_ErrorMessage.Add(8393, "DS exists in aux cls"); /* ERROR_DS_EXISTS_IN_AUX_CLS */ + s_ErrorMessage.Add(8394, "DS exists in sub cls"); /* ERROR_DS_EXISTS_IN_SUB_CLS */ + s_ErrorMessage.Add(8395, "DS exists in poss sup"); /* ERROR_DS_EXISTS_IN_POSS_SUP */ + s_ErrorMessage.Add(8396, "DS recalcschema failed"); /* ERROR_DS_RECALCSCHEMA_FAILED */ + s_ErrorMessage.Add(8397, "DS tree delete not finished"); /* ERROR_DS_TREE_DELETE_NOT_FINISHED */ + s_ErrorMessage.Add(8398, "DS can't delete"); /* ERROR_DS_CANT_DELETE */ + s_ErrorMessage.Add(8399, "DS att schema req ID"); /* ERROR_DS_ATT_SCHEMA_REQ_ID */ + s_ErrorMessage.Add(8400, "DS bad att schema syntax"); /* ERROR_DS_BAD_ATT_SCHEMA_SYNTAX */ + s_ErrorMessage.Add(8401, "DS can't cache att"); /* ERROR_DS_CANT_CACHE_ATT */ + s_ErrorMessage.Add(8402, "DS can't cache class"); /* ERROR_DS_CANT_CACHE_CLASS */ + s_ErrorMessage.Add(8403, "DS can't remove att cache"); /* ERROR_DS_CANT_REMOVE_ATT_CACHE */ + s_ErrorMessage.Add(8404, "DS can't remove class cache"); /* ERROR_DS_CANT_REMOVE_CLASS_CACHE */ + s_ErrorMessage.Add(8405, "DS can't retrieve DN"); /* ERROR_DS_CANT_RETRIEVE_DN */ + s_ErrorMessage.Add(8406, "DS missing supref"); /* ERROR_DS_MISSING_SUPREF */ + s_ErrorMessage.Add(8407, "DS can't retrieve instance"); /* ERROR_DS_CANT_RETRIEVE_INSTANCE */ + s_ErrorMessage.Add(8408, "DS code inconsistency"); /* ERROR_DS_CODE_INCONSISTENCY */ + s_ErrorMessage.Add(8409, "DS database error"); /* ERROR_DS_DATABASE_ERROR */ + s_ErrorMessage.Add(8410, "DS governsid missing"); /* ERROR_DS_GOVERNSID_MISSING */ + s_ErrorMessage.Add(8411, "DS missing expected att"); /* ERROR_DS_MISSING_EXPECTED_ATT */ + s_ErrorMessage.Add(8412, "DS ncname missing cr ref"); /* ERROR_DS_NCNAME_MISSING_CR_REF */ + s_ErrorMessage.Add(8413, "DS security checking error"); /* ERROR_DS_SECURITY_CHECKING_ERROR */ + s_ErrorMessage.Add(8414, "DS schema not loaded"); /* ERROR_DS_SCHEMA_NOT_LOADED */ + s_ErrorMessage.Add(8415, "DS schema alloc failed"); /* ERROR_DS_SCHEMA_ALLOC_FAILED */ + s_ErrorMessage.Add(8416, "DS att schema req syntax"); /* ERROR_DS_ATT_SCHEMA_REQ_SYNTAX */ + s_ErrorMessage.Add(8417, "DS gcverify error"); /* ERROR_DS_GCVERIFY_ERROR */ + s_ErrorMessage.Add(8418, "DS dra schema mismatch"); /* ERROR_DS_DRA_SCHEMA_MISMATCH */ + s_ErrorMessage.Add(8419, "DS can't find dsa obj"); /* ERROR_DS_CANT_FIND_DSA_OBJ */ + s_ErrorMessage.Add(8420, "DS can't find expected nc"); /* ERROR_DS_CANT_FIND_EXPECTED_NC */ + s_ErrorMessage.Add(8421, "DS can't find nc in cache"); /* ERROR_DS_CANT_FIND_NC_IN_CACHE */ + s_ErrorMessage.Add(8422, "DS can't retrieve child"); /* ERROR_DS_CANT_RETRIEVE_CHILD */ + s_ErrorMessage.Add(8423, "DS security illegal modify"); /* ERROR_DS_SECURITY_ILLEGAL_MODIFY */ + s_ErrorMessage.Add(8424, "DS can't replace hidden rec"); /* ERROR_DS_CANT_REPLACE_HIDDEN_REC */ + s_ErrorMessage.Add(8425, "DS bad hierarchy file"); /* ERROR_DS_BAD_HIERARCHY_FILE */ + s_ErrorMessage.Add(8426, "DS build hierarchy table failed"); /* ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED */ + s_ErrorMessage.Add(8427, "DS config param missing"); /* ERROR_DS_CONFIG_PARAM_MISSING */ + s_ErrorMessage.Add(8428, "DS counting ab indices failed"); /* ERROR_DS_COUNTING_AB_INDICES_FAILED */ + s_ErrorMessage.Add(8429, "DS hierarchy table malloc failed"); /* ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED */ + s_ErrorMessage.Add(8430, "DS internal failure"); /* ERROR_DS_INTERNAL_FAILURE */ + s_ErrorMessage.Add(8431, "DS unknown error"); /* ERROR_DS_UNKNOWN_ERROR */ + s_ErrorMessage.Add(8432, "DS root requires class top"); /* ERROR_DS_ROOT_REQUIRES_CLASS_TOP */ + s_ErrorMessage.Add(8433, "DS refusing fmso roles"); /* ERROR_DS_REFUSING_FSMO_ROLES */ + s_ErrorMessage.Add(8434, "DS missing fmso settings"); /* ERROR_DS_MISSING_FSMO_SETTINGS */ + s_ErrorMessage.Add(8435, "DS unable to surrender roles"); /* ERROR_DS_UNABLE_TO_SURRENDER_ROLES */ + } + + public static void InitializeErrorMessages13() + { + s_ErrorMessage.Add(8436, "DS dra generic"); /* ERROR_DS_DRA_GENERIC */ + s_ErrorMessage.Add(8437, "DS dra invalid parameter"); /* ERROR_DS_DRA_INVALID_PARAMETER */ + s_ErrorMessage.Add(8438, "DS dra busy"); /* ERROR_DS_DRA_BUSY */ + s_ErrorMessage.Add(8439, "DS dra bad dn"); /* ERROR_DS_DRA_BAD_DN */ + s_ErrorMessage.Add(8440, "DS dra bad nc"); /* ERROR_DS_DRA_BAD_NC */ + s_ErrorMessage.Add(8441, "DS dra dn exists"); /* ERROR_DS_DRA_DN_EXISTS */ + s_ErrorMessage.Add(8442, "DS dra internal error"); /* ERROR_DS_DRA_INTERNAL_ERROR */ + s_ErrorMessage.Add(8443, "DS dra inconsistent dit"); /* ERROR_DS_DRA_INCONSISTENT_DIT */ + s_ErrorMessage.Add(8444, "DS dra connection failed"); /* ERROR_DS_DRA_CONNECTION_FAILED */ + s_ErrorMessage.Add(8445, "DS dra bad instance type"); /* ERROR_DS_DRA_BAD_INSTANCE_TYPE */ + s_ErrorMessage.Add(8446, "DS dra out of mem"); /* ERROR_DS_DRA_OUT_OF_MEM */ + s_ErrorMessage.Add(8447, "DS dra mail problem"); /* ERROR_DS_DRA_MAIL_PROBLEM */ + s_ErrorMessage.Add(8448, "DS dra ref already exists"); /* ERROR_DS_DRA_REF_ALREADY_EXISTS */ + s_ErrorMessage.Add(8449, "DS dra ref not found"); /* ERROR_DS_DRA_REF_NOT_FOUND */ + s_ErrorMessage.Add(8450, "DS dra obj is rep source"); /* ERROR_DS_DRA_OBJ_IS_REP_SOURCE */ + s_ErrorMessage.Add(8451, "DS dra db error"); /* ERROR_DS_DRA_DB_ERROR */ + s_ErrorMessage.Add(8452, "DS dra no replica"); /* ERROR_DS_DRA_NO_REPLICA */ + s_ErrorMessage.Add(8453, "DS dra access denied"); /* ERROR_DS_DRA_ACCESS_DENIED */ + s_ErrorMessage.Add(8454, "DS dra not supported"); /* ERROR_DS_DRA_NOT_SUPPORTED */ + s_ErrorMessage.Add(8455, "DS dra RPC cancelled"); /* ERROR_DS_DRA_RPC_CANCELLED */ + s_ErrorMessage.Add(8456, "DS dra source disabled"); /* ERROR_DS_DRA_SOURCE_DISABLED */ + s_ErrorMessage.Add(8457, "DS dra sink disabled"); /* ERROR_DS_DRA_SINK_DISABLED */ + s_ErrorMessage.Add(8458, "DS dra name collision"); /* ERROR_DS_DRA_NAME_COLLISION */ + s_ErrorMessage.Add(8459, "DS dra source reinstalled"); /* ERROR_DS_DRA_SOURCE_REINSTALLED */ + s_ErrorMessage.Add(8460, "DS dra missing parent"); /* ERROR_DS_DRA_MISSING_PARENT */ + s_ErrorMessage.Add(8461, "DS dra preempted"); /* ERROR_DS_DRA_PREEMPTED */ + s_ErrorMessage.Add(8462, "DS dra abandon sync"); /* ERROR_DS_DRA_ABANDON_SYNC */ + s_ErrorMessage.Add(8463, "DS dra shutdown"); /* ERROR_DS_DRA_SHUTDOWN */ + s_ErrorMessage.Add(8464, "DS dra incompatible partial set"); /* ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET */ + s_ErrorMessage.Add(8465, "DS dra source is partial replica"); /* ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA */ + s_ErrorMessage.Add(8466, "DS dra extn connection failed"); /* ERROR_DS_DRA_EXTN_CONNECTION_FAILED */ + s_ErrorMessage.Add(8467, "DS install schema mismatch"); /* ERROR_DS_INSTALL_SCHEMA_MISMATCH */ + s_ErrorMessage.Add(8468, "DS dup link ID"); /* ERROR_DS_DUP_LINK_ID */ + s_ErrorMessage.Add(8469, "DS name error resolving"); /* ERROR_DS_NAME_ERROR_RESOLVING */ + s_ErrorMessage.Add(8470, "DS name error not found"); /* ERROR_DS_NAME_ERROR_NOT_FOUND */ + s_ErrorMessage.Add(8471, "DS name error not unique"); /* ERROR_DS_NAME_ERROR_NOT_UNIQUE */ + s_ErrorMessage.Add(8472, "DS name error no mapping"); /* ERROR_DS_NAME_ERROR_NO_MAPPING */ + s_ErrorMessage.Add(8473, "DS name error domain only"); /* ERROR_DS_NAME_ERROR_DOMAIN_ONLY */ + s_ErrorMessage.Add(8474, "DS name error no syntactical mapping"); /* ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING */ + s_ErrorMessage.Add(8475, "DS constructed att mod"); /* ERROR_DS_CONSTRUCTED_ATT_MOD */ + s_ErrorMessage.Add(8476, "DS wrong om obj class"); /* ERROR_DS_WRONG_OM_OBJ_CLASS */ + s_ErrorMessage.Add(8477, "DS dra repl pending"); /* ERROR_DS_DRA_REPL_PENDING */ + s_ErrorMessage.Add(8478, "DS ds required"); /* ERROR_DS_DS_REQUIRED */ + s_ErrorMessage.Add(8479, "DS invalid LDAP display name"); /* ERROR_DS_INVALID_LDAP_DISPLAY_NAME */ + s_ErrorMessage.Add(8480, "DS non base search"); /* ERROR_DS_NON_BASE_SEARCH */ + s_ErrorMessage.Add(8481, "DS can't retrieve atts"); /* ERROR_DS_CANT_RETRIEVE_ATTS */ + s_ErrorMessage.Add(8482, "DS backlink without link"); /* ERROR_DS_BACKLINK_WITHOUT_LINK */ + s_ErrorMessage.Add(8483, "DS epoch mismatch"); /* ERROR_DS_EPOCH_MISMATCH */ + s_ErrorMessage.Add(8484, "DS src name mismatch"); /* ERROR_DS_SRC_NAME_MISMATCH */ + s_ErrorMessage.Add(8485, "DS src and dst nc identical"); /* ERROR_DS_SRC_AND_DST_NC_IDENTICAL */ + s_ErrorMessage.Add(8486, "DS dst nc mismatch"); /* ERROR_DS_DST_NC_MISMATCH */ + s_ErrorMessage.Add(8487, "DS not authoritive for dst nc"); /* ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC */ + s_ErrorMessage.Add(8488, "DS src GUID mismatch"); /* ERROR_DS_SRC_GUID_MISMATCH */ + s_ErrorMessage.Add(8489, "DS can't move deleted object"); /* ERROR_DS_CANT_MOVE_DELETED_OBJECT */ + s_ErrorMessage.Add(8490, "DS pdc operation in progress"); /* ERROR_DS_PDC_OPERATION_IN_PROGRESS */ + s_ErrorMessage.Add(8491, "DS cross domain cleanup reqd"); /* ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD */ + s_ErrorMessage.Add(8492, "DS illegal xdom move operation"); /* ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION */ + s_ErrorMessage.Add(8493, "DS can't with acct group membershps"); /* ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS */ + s_ErrorMessage.Add(8494, "DS nc must have nc parent"); /* ERROR_DS_NC_MUST_HAVE_NC_PARENT */ + s_ErrorMessage.Add(8496, "DS dst domain not native"); /* ERROR_DS_DST_DOMAIN_NOT_NATIVE */ + s_ErrorMessage.Add(8497, "DS missing infrastructure container"); /* ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER */ + s_ErrorMessage.Add(8498, "DS can't move account group"); /* ERROR_DS_CANT_MOVE_ACCOUNT_GROUP */ + s_ErrorMessage.Add(8499, "DS can't move resource group"); /* ERROR_DS_CANT_MOVE_RESOURCE_GROUP */ + s_ErrorMessage.Add(8500, "DS invalid search flag"); /* ERROR_DS_INVALID_SEARCH_FLAG */ + s_ErrorMessage.Add(8501, "DS no tree delete above nc"); /* ERROR_DS_NO_TREE_DELETE_ABOVE_NC */ + s_ErrorMessage.Add(8502, "DS couldn't lock tree for delete"); /* ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE */ + s_ErrorMessage.Add(8503, "DS couldn't identify objects for tree delete"); /* ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE */ + s_ErrorMessage.Add(8504, "DS sam init failure"); /* ERROR_DS_SAM_INIT_FAILURE */ + s_ErrorMessage.Add(8505, "DS sensitive group violation"); /* ERROR_DS_SENSITIVE_GROUP_VIOLATION */ + s_ErrorMessage.Add(8506, "DS can't mod primarygroupid"); /* ERROR_DS_CANT_MOD_PRIMARYGROUPID */ + s_ErrorMessage.Add(8507, "DS illegal base schema mod"); /* ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD */ + s_ErrorMessage.Add(8508, "DS nonsafe schema change"); /* ERROR_DS_NONSAFE_SCHEMA_CHANGE */ + s_ErrorMessage.Add(8509, "DS schema update disallowed"); /* ERROR_DS_SCHEMA_UPDATE_DISALLOWED */ + s_ErrorMessage.Add(8510, "DS can't create under schema"); /* ERROR_DS_CANT_CREATE_UNDER_SCHEMA */ + s_ErrorMessage.Add(8511, "DS install no src sch version"); /* ERROR_DS_INSTALL_NO_SRC_SCH_VERSION */ + s_ErrorMessage.Add(8512, "DS install no sch version in inifile"); /* ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE */ + s_ErrorMessage.Add(8513, "DS invalid group type"); /* ERROR_DS_INVALID_GROUP_TYPE */ + s_ErrorMessage.Add(8514, "DS no nest globalgroup in mixeddomain"); /* ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN */ + s_ErrorMessage.Add(8515, "DS no nest localgroup in mixeddomain"); /* ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN */ + s_ErrorMessage.Add(8516, "DS global can't have local member"); /* ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER */ + s_ErrorMessage.Add(8517, "DS global can't have universal member"); /* ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER */ + s_ErrorMessage.Add(8518, "DS universal can't have local member"); /* ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER */ + s_ErrorMessage.Add(8519, "DS global can't have crossdomain member"); /* ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER */ + s_ErrorMessage.Add(8520, "DS local can't have crossdomain local member"); /* ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER */ + s_ErrorMessage.Add(8521, "DS have primary members"); /* ERROR_DS_HAVE_PRIMARY_MEMBERS */ + s_ErrorMessage.Add(8522, "DS string sd conversion failed"); /* ERROR_DS_STRING_SD_CONVERSION_FAILED */ + s_ErrorMessage.Add(8523, "DS naming master gc"); /* ERROR_DS_NAMING_MASTER_GC */ + s_ErrorMessage.Add(8524, "DS lookup failure"); /* ERROR_DS_LOOKUP_FAILURE */ + s_ErrorMessage.Add(8525, "DS couldn't update spns"); /* ERROR_DS_COULDNT_UPDATE_SPNS */ + s_ErrorMessage.Add(8526, "DS can't retrieve sd"); /* ERROR_DS_CANT_RETRIEVE_SD */ + s_ErrorMessage.Add(8527, "DS key not unique"); /* ERROR_DS_KEY_NOT_UNIQUE */ + s_ErrorMessage.Add(8528, "DS wrong linked att syntax"); /* ERROR_DS_WRONG_LINKED_ATT_SYNTAX */ + s_ErrorMessage.Add(8529, "DS sam need bootkey password"); /* ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD */ + s_ErrorMessage.Add(8530, "DS sam need bootkey floppy"); /* ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY */ + s_ErrorMessage.Add(8531, "DS can't start"); /* ERROR_DS_CANT_START */ + s_ErrorMessage.Add(8532, "DS init failure"); /* ERROR_DS_INIT_FAILURE */ + s_ErrorMessage.Add(8533, "DS no pkt privacy on connection"); /* ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION */ + s_ErrorMessage.Add(8534, "DS source domain in forest"); /* ERROR_DS_SOURCE_DOMAIN_IN_FOREST */ + s_ErrorMessage.Add(8535, "DS destination domain not in forest"); /* ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST */ + s_ErrorMessage.Add(8536, "DS destination auditing not enabled"); /* ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED */ + s_ErrorMessage.Add(8537, "DS can't find dc for src domain"); /* ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN */ + s_ErrorMessage.Add(8538, "DS src obj not group or user"); /* ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER */ + s_ErrorMessage.Add(8539, "DS src sid exists in forest"); /* ERROR_DS_SRC_SID_EXISTS_IN_FOREST */ + s_ErrorMessage.Add(8540, "DS src and dst object class mismatch"); /* ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH */ + s_ErrorMessage.Add(8541, "Sam init failure"); /* ERROR_SAM_INIT_FAILURE */ + s_ErrorMessage.Add(8542, "DS dra schema info ship"); /* ERROR_DS_DRA_SCHEMA_INFO_SHIP */ + s_ErrorMessage.Add(8543, "DS dra schema conflict"); /* ERROR_DS_DRA_SCHEMA_CONFLICT */ + s_ErrorMessage.Add(8544, "DS dra earlier schema conflict"); /* ERROR_DS_DRA_EARLIER_SCHEMA_CONLICT */ + s_ErrorMessage.Add(8545, "DS dra obj nc mismatch"); /* ERROR_DS_DRA_OBJ_NC_MISMATCH */ + s_ErrorMessage.Add(8546, "DS nc still has dsas"); /* ERROR_DS_NC_STILL_HAS_DSAS */ + s_ErrorMessage.Add(8547, "DS gc required"); /* ERROR_DS_GC_REQUIRED */ + s_ErrorMessage.Add(8548, "DS local member of local only"); /* ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY */ + s_ErrorMessage.Add(8549, "DS no fpo in universal groups"); /* ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS */ + s_ErrorMessage.Add(8550, "DS can't add to gc"); /* ERROR_DS_CANT_ADD_TO_GC */ + s_ErrorMessage.Add(8551, "DS no checkpoint with pdc"); /* ERROR_DS_NO_CHECKPOINT_WITH_PDC */ + s_ErrorMessage.Add(8552, "DS source auditing not enabled"); /* ERROR_DS_SOURCE_AUDITING_NOT_ENABLED */ + s_ErrorMessage.Add(8553, "DS can't create in nondomain nc"); /* ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC */ + s_ErrorMessage.Add(8554, "DS invalid name for spn"); /* ERROR_DS_INVALID_NAME_FOR_SPN */ + s_ErrorMessage.Add(8555, "DS filter uses constructed attrs"); /* ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS */ + s_ErrorMessage.Add(8556, "DS unicodepwd not in quotes"); /* ERROR_DS_UNICODEPWD_NOT_IN_QUOTES */ + s_ErrorMessage.Add(8557, "DS machine account quota exceeded"); /* ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED */ + s_ErrorMessage.Add(8558, "DS must be run on dst dc"); /* ERROR_DS_MUST_BE_RUN_ON_DST_DC */ + s_ErrorMessage.Add(8559, "DS src dc must be sp4 or greater"); /* ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER */ + s_ErrorMessage.Add(8560, "DS can't tree delete critical obj"); /* ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ */ + s_ErrorMessage.Add(8561, "DS init failure console"); /* ERROR_DS_INIT_FAILURE_CONSOLE */ + s_ErrorMessage.Add(8562, "DS sam init failure console"); /* ERROR_DS_SAM_INIT_FAILURE_CONSOLE */ + s_ErrorMessage.Add(8563, "DS forest version too high"); /* ERROR_DS_FOREST_VERSION_TOO_HIGH */ + s_ErrorMessage.Add(8564, "DS domain version too high"); /* ERROR_DS_DOMAIN_VERSION_TOO_HIGH */ + s_ErrorMessage.Add(8565, "DS forest version too low"); /* ERROR_DS_FOREST_VERSION_TOO_LOW */ + s_ErrorMessage.Add(8566, "DS domain version too low"); /* ERROR_DS_DOMAIN_VERSION_TOO_LOW */ + s_ErrorMessage.Add(8567, "DS incompatible version"); /* ERROR_DS_INCOMPATIBLE_VERSION */ + s_ErrorMessage.Add(8568, "DS low dsa version"); /* ERROR_DS_LOW_DSA_VERSION */ + s_ErrorMessage.Add(8569, "DS no behaviour version in mixeddomain"); /* ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN */ + s_ErrorMessage.Add(8570, "DS not supported sort order"); /* ERROR_DS_NOT_SUPPORTED_SORT_ORDER */ + s_ErrorMessage.Add(8571, "DS name not unique"); /* ERROR_DS_NAME_NOT_UNIQUE */ + s_ErrorMessage.Add(8572, "DS machine account created prent4"); /* ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 */ + s_ErrorMessage.Add(8573, "DS out of version store"); /* ERROR_DS_OUT_OF_VERSION_STORE */ + s_ErrorMessage.Add(8574, "DS incompatible controls used"); /* ERROR_DS_INCOMPATIBLE_CONTROLS_USED */ + s_ErrorMessage.Add(8575, "DS no ref domain"); /* ERROR_DS_NO_REF_DOMAIN */ + s_ErrorMessage.Add(8576, "DS reserved link ID"); /* ERROR_DS_RESERVED_LINK_ID */ + s_ErrorMessage.Add(8577, "DS link ID not available"); /* ERROR_DS_LINK_ID_NOT_AVAILABLE */ + s_ErrorMessage.Add(8578, "DS ag can't have universal member"); /* ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER */ + s_ErrorMessage.Add(8579, "DS modifydn disallowed by instance type"); /* ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE */ + s_ErrorMessage.Add(8580, "DS no object move in schema nc"); /* ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC */ + s_ErrorMessage.Add(8581, "DS modifydn disallowed by flag"); /* ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG */ + s_ErrorMessage.Add(8582, "DS modifydn wrong grandparent"); /* ERROR_DS_MODIFYDN_WRONG_GRANDPARENT */ + s_ErrorMessage.Add(8583, "DS name error trust referral"); /* ERROR_DS_NAME_ERROR_TRUST_REFERRAL */ + s_ErrorMessage.Add(8584, "Not supported on standard server"); /* ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER */ + s_ErrorMessage.Add(8585, "DS can't access remote part of ad"); /* ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD */ + s_ErrorMessage.Add(8586, "DS cr impossible to validate"); /* ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE */ + s_ErrorMessage.Add(8587, "DS thread limit exceeded"); /* ERROR_DS_THREAD_LIMIT_EXCEEDED */ + s_ErrorMessage.Add(8588, "DS not closest"); /* ERROR_DS_NOT_CLOSEST */ + s_ErrorMessage.Add(8589, "DS can't derive spn without server ref"); /* ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF */ + s_ErrorMessage.Add(8590, "DS single user mode failed"); /* ERROR_DS_SINGLE_USER_MODE_FAILED */ + s_ErrorMessage.Add(8591, "DS ntdscript syntax error"); /* ERROR_DS_NTDSCRIPT_SYNTAX_ERROR */ + s_ErrorMessage.Add(8592, "DS ntdscript process error"); /* ERROR_DS_NTDSCRIPT_PROCESS_ERROR */ + s_ErrorMessage.Add(8593, "DS different repl epochs"); /* ERROR_DS_DIFFERENT_REPL_EPOCHS */ + s_ErrorMessage.Add(8594, "DS drs extensions changed"); /* ERROR_DS_DRS_EXTENSIONS_CHANGED */ + s_ErrorMessage.Add(8595, "DS replica set change not allowed on disabled cr"); /* ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR */ + s_ErrorMessage.Add(8596, "DS no msds intid"); /* ERROR_DS_NO_MSDS_INTID */ + s_ErrorMessage.Add(8597, "DS dup msds intid"); /* ERROR_DS_DUP_MSDS_INTID */ + s_ErrorMessage.Add(8598, "DS exists in rdnattid"); /* ERROR_DS_EXISTS_IN_RDNATTID */ + s_ErrorMessage.Add(8599, "DS authorisation failed"); /* ERROR_DS_AUTHORIZATION_FAILED */ + s_ErrorMessage.Add(8600, "DS invalid script"); /* ERROR_DS_INVALID_SCRIPT */ + s_ErrorMessage.Add(8601, "DS remote crossref op failed"); /* ERROR_DS_REMOTE_CROSSREF_OP_FAILED */ + s_ErrorMessage.Add(9001, "DNS error rcode format error"); /* DNS_ERROR_RCODE_FORMAT_ERROR */ + s_ErrorMessage.Add(9002, "DNS error rcode server failure"); /* DNS_ERROR_RCODE_SERVER_FAILURE */ + s_ErrorMessage.Add(9003, "DNS error rcode name error"); /* DNS_ERROR_RCODE_NAME_ERROR */ + s_ErrorMessage.Add(9004, "DNS error rcode not implemented"); /* DNS_ERROR_RCODE_NOT_IMPLEMENTED */ + s_ErrorMessage.Add(9005, "DNS error rcode refused"); /* DNS_ERROR_RCODE_REFUSED */ + s_ErrorMessage.Add(9006, "DNS error rcode yxdomain"); /* DNS_ERROR_RCODE_YXDOMAIN */ + s_ErrorMessage.Add(9007, "DNS error rcode yxrrset"); /* DNS_ERROR_RCODE_YXRRSET */ + s_ErrorMessage.Add(9008, "DNS error rcode nxrrset"); /* DNS_ERROR_RCODE_NXRRSET */ + } + + public static void InitializeErrorMessages14() + { + s_ErrorMessage.Add(9009, "DNS error rcode notauth"); /* DNS_ERROR_RCODE_NOTAUTH */ + s_ErrorMessage.Add(9010, "DNS error rcode notzone"); /* DNS_ERROR_RCODE_NOTZONE */ + s_ErrorMessage.Add(9016, "DNS error rcode badsig"); /* DNS_ERROR_RCODE_BADSIG */ + s_ErrorMessage.Add(9017, "DNS error rcode badkey"); /* DNS_ERROR_RCODE_BADKEY */ + s_ErrorMessage.Add(9018, "DNS error rcode badtime"); /* DNS_ERROR_RCODE_BADTIME */ + s_ErrorMessage.Add(9501, "DNS info no records"); /* DNS_INFO_NO_RECORDS */ + s_ErrorMessage.Add(9502, "DNS error bad packet"); /* DNS_ERROR_BAD_PACKET */ + s_ErrorMessage.Add(9503, "DNS error no packet"); /* DNS_ERROR_NO_PACKET */ + s_ErrorMessage.Add(9504, "DNS error rcode"); /* DNS_ERROR_RCODE */ + s_ErrorMessage.Add(9505, "DNS error unsecure packet"); /* DNS_ERROR_UNSECURE_PACKET */ + s_ErrorMessage.Add(9551, "DNS error invalid type"); /* DNS_ERROR_INVALID_TYPE */ + s_ErrorMessage.Add(9552, "DNS error invalid IP address"); /* DNS_ERROR_INVALID_IP_ADDRESS */ + s_ErrorMessage.Add(9553, "DNS error invalid property"); /* DNS_ERROR_INVALID_PROPERTY */ + s_ErrorMessage.Add(9554, "DNS error try again later"); /* DNS_ERROR_TRY_AGAIN_LATER */ + s_ErrorMessage.Add(9555, "DNS error not unique"); /* DNS_ERROR_NOT_UNIQUE */ + s_ErrorMessage.Add(9556, "DNS error non RFC name"); /* DNS_ERROR_NON_RFC_NAME */ + s_ErrorMessage.Add(9557, "DNS status FQDN"); /* DNS_STATUS_FQDN */ + s_ErrorMessage.Add(9558, "DNS status dotted name"); /* DNS_STATUS_DOTTED_NAME */ + s_ErrorMessage.Add(9559, "DNS status single part name"); /* DNS_STATUS_SINGLE_PART_NAME */ + s_ErrorMessage.Add(9560, "DNS error invalid name char"); /* DNS_ERROR_INVALID_NAME_CHAR */ + s_ErrorMessage.Add(9561, "DNS error numeric name"); /* DNS_ERROR_NUMERIC_NAME */ + s_ErrorMessage.Add(9562, "DNS error not allowed on root server"); /* DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER */ + s_ErrorMessage.Add(9601, "DNS error zone does not exist"); /* DNS_ERROR_ZONE_DOES_NOT_EXIST */ + s_ErrorMessage.Add(9602, "DNS error not zone info"); /* DNS_ERROR_NO_ZONE_INFO */ + s_ErrorMessage.Add(9603, "DNS error invalid zone operation"); /* DNS_ERROR_INVALID_ZONE_OPERATION */ + s_ErrorMessage.Add(9604, "DNS error zone configuration error"); /* DNS_ERROR_ZONE_CONFIGURATION_ERROR */ + s_ErrorMessage.Add(9605, "DNS error zone has not SOA record"); /* DNS_ERROR_ZONE_HAS_NO_SOA_RECORD */ + s_ErrorMessage.Add(9606, "DNS error zone has no NS records"); /* DNS_ERROR_ZONE_HAS_NO_NS_RECORDS */ + s_ErrorMessage.Add(9607, "DNS error zone locked"); /* DNS_ERROR_ZONE_LOCKED */ + s_ErrorMessage.Add(9608, "DNS error zone creation failed"); /* DNS_ERROR_ZONE_CREATION_FAILED */ + s_ErrorMessage.Add(9609, "DNS error zone already exists"); /* DNS_ERROR_ZONE_ALREADY_EXISTS */ + s_ErrorMessage.Add(9610, "DNS error autozone already exists"); /* DNS_ERROR_AUTOZONE_ALREADY_EXISTS */ + s_ErrorMessage.Add(9611, "DNS error invalid zone type"); /* DNS_ERROR_INVALID_ZONE_TYPE */ + s_ErrorMessage.Add(9612, "DNS error secondary requires master IP"); /* DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP */ + s_ErrorMessage.Add(9613, "DNS error zone not secondary"); /* DNS_ERROR_ZONE_NOT_SECONDARY */ + s_ErrorMessage.Add(9614, "DNS error need secondary addresses"); /* DNS_ERROR_NEED_SECONDARY_ADDRESSES */ + s_ErrorMessage.Add(9615, "DNS error wins init failed"); /* DNS_ERROR_WINS_INIT_FAILED */ + s_ErrorMessage.Add(9616, "DNS error need wins servers"); /* DNS_ERROR_NEED_WINS_SERVERS */ + s_ErrorMessage.Add(9617, "DNS error nbstat init failed"); /* DNS_ERROR_NBSTAT_INIT_FAILED */ + s_ErrorMessage.Add(9618, "DNS error SOA delete invalid"); /* DNS_ERROR_SOA_DELETE_INVALID */ + s_ErrorMessage.Add(9619, "DNS error forwarder already exists"); /* DNS_ERROR_FORWARDER_ALREADY_EXISTS */ + s_ErrorMessage.Add(9620, "DNS error zone requires master IP"); /* DNS_ERROR_ZONE_REQUIRES_MASTER_IP */ + s_ErrorMessage.Add(9621, "DNS error zone is shutdown"); /* DNS_ERROR_ZONE_IS_SHUTDOWN */ + s_ErrorMessage.Add(9651, "DNS error primary requires datafile"); /* DNS_ERROR_PRIMARY_REQUIRES_DATAFILE */ + s_ErrorMessage.Add(9652, "DNS error invalid datafile name"); /* DNS_ERROR_INVALID_DATAFILE_NAME */ + s_ErrorMessage.Add(9653, "DNS error datafile open failure"); /* DNS_ERROR_DATAFILE_OPEN_FAILURE */ + s_ErrorMessage.Add(9654, "DNS error file writeback failed"); /* DNS_ERROR_FILE_WRITEBACK_FAILED */ + s_ErrorMessage.Add(9655, "DNS error datafile parsing"); /* DNS_ERROR_DATAFILE_PARSING */ + s_ErrorMessage.Add(9701, "DNS error record does not exist"); /* DNS_ERROR_RECORD_DOES_NOT_EXIST */ + s_ErrorMessage.Add(9702, "DNS error record format"); /* DNS_ERROR_RECORD_FORMAT */ + s_ErrorMessage.Add(9703, "DNS error node creation failed"); /* DNS_ERROR_NODE_CREATION_FAILED */ + s_ErrorMessage.Add(9704, "DNS error unknown record type"); /* DNS_ERROR_UNKNOWN_RECORD_TYPE */ + s_ErrorMessage.Add(9705, "DNS error record timed out"); /* DNS_ERROR_RECORD_TIMED_OUT */ + s_ErrorMessage.Add(9706, "DNS error name not in zone"); /* DNS_ERROR_NAME_NOT_IN_ZONE */ + s_ErrorMessage.Add(9707, "DNS error CNAME loop"); /* DNS_ERROR_CNAME_LOOP */ + s_ErrorMessage.Add(9708, "DNS error node is CNAME"); /* DNS_ERROR_NODE_IS_CNAME */ + s_ErrorMessage.Add(9709, "DNS error CNAME collision"); /* DNS_ERROR_CNAME_COLLISION */ + s_ErrorMessage.Add(9710, "DNS error record only at zone root"); /* DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT */ + s_ErrorMessage.Add(9711, "DNS error record already exists"); /* DNS_ERROR_RECORD_ALREADY_EXISTS */ + s_ErrorMessage.Add(9712, "DNS error secondary data"); /* DNS_ERROR_SECONDARY_DATA */ + s_ErrorMessage.Add(9713, "DNS error no create cache data"); /* DNS_ERROR_NO_CREATE_CACHE_DATA */ + s_ErrorMessage.Add(9714, "DNS error name does not exist"); /* DNS_ERROR_NAME_DOES_NOT_EXIST */ + s_ErrorMessage.Add(9715, "DNS warning PTR create failed"); /* DNS_WARNING_PTR_CREATE_FAILED */ + s_ErrorMessage.Add(9716, "DNS warning domain undeleted"); /* DNS_WARNING_DOMAIN_UNDELETED */ + s_ErrorMessage.Add(9717, "DNS error ds unavailable"); /* DNS_ERROR_DS_UNAVAILABLE */ + s_ErrorMessage.Add(9718, "DNS error ds zone already exists"); /* DNS_ERROR_DS_ZONE_ALREADY_EXISTS */ + s_ErrorMessage.Add(9719, "DNS error no bootfile if ds zone"); /* DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE */ + s_ErrorMessage.Add(9751, "DNS info AXFR complete"); /* DNS_INFO_AXFR_COMPLETE */ + s_ErrorMessage.Add(9752, "DNS error AXFR"); /* DNS_ERROR_AXFR */ + s_ErrorMessage.Add(9753, "DNS info added local wins"); /* DNS_INFO_ADDED_LOCAL_WINS */ + s_ErrorMessage.Add(9801, "DNS status continue needed"); /* DNS_STATUS_CONTINUE_NEEDED */ + s_ErrorMessage.Add(9851, "DNS error no TCPIP"); /* DNS_ERROR_NO_TCPIP */ + s_ErrorMessage.Add(9852, "DNS error no DNS servers"); /* DNS_ERROR_NO_DNS_SERVERS */ + s_ErrorMessage.Add(9901, "DNS error dp does not exist"); /* DNS_ERROR_DP_DOES_NOT_EXIST */ + s_ErrorMessage.Add(9902, "DNS error dp already exists"); /* DNS_ERROR_DP_ALREADY_EXISTS */ + s_ErrorMessage.Add(9903, "DNS error dp not enlisted"); /* DNS_ERROR_DP_NOT_ENLISTED */ + s_ErrorMessage.Add(9904, "DNS error dp already enlisted"); /* DNS_ERROR_DP_ALREADY_ENLISTED */ + s_ErrorMessage.Add(10110, "E_NO_MORE"); /* WSA_E_NO_MORE */ + s_ErrorMessage.Add(10111, "E_CANCELLED"); /* WSA_E_CANCELLED */ + s_ErrorMessage.Add(11005, "QOS receivers"); /* WSA_QOS_RECEIVERS */ + s_ErrorMessage.Add(11006, "QOS senders"); /* WSA_QOS_SENDERS */ + s_ErrorMessage.Add(11007, "QOS no senders"); /* WSA_QOS_NO_SENDERS */ + s_ErrorMessage.Add(11008, "QOS no receivers"); /* WSA_QOS_NO_RECEIVERS */ + s_ErrorMessage.Add(11009, "QOS request confirmed"); /* WSA_QOS_REQUEST_CONFIRMED */ + s_ErrorMessage.Add(11010, "QOS admission failure"); /* WSA_QOS_ADMISSION_FAILURE */ + s_ErrorMessage.Add(11011, "QOS policy failure"); /* WSA_QOS_POLICY_FAILURE */ + s_ErrorMessage.Add(11012, "QOS bad style"); /* WSA_QOS_BAD_STYLE */ + s_ErrorMessage.Add(11013, "QOS bad object"); /* WSA_QOS_BAD_OBJECT */ + s_ErrorMessage.Add(11014, "QOS traffic ctrl error"); /* WSA_QOS_TRAFFIC_CTRL_ERROR */ + s_ErrorMessage.Add(11015, "QOS generic error"); /* WSA_QOS_GENERIC_ERROR */ + s_ErrorMessage.Add(11016, "QOS eservicetype"); /* WSA_QOS_ESERVICETYPE */ + s_ErrorMessage.Add(11017, "QOS eflowspec"); /* WSA_QOS_EFLOWSPEC */ + s_ErrorMessage.Add(11018, "QOS eprovspecbuf"); /* WSA_QOS_EPROVSPECBUF */ + s_ErrorMessage.Add(11019, "QOS efilterstyle"); /* WSA_QOS_EFILTERSTYLE */ + s_ErrorMessage.Add(11020, "QOS efiltertype"); /* WSA_QOS_EFILTERTYPE */ + s_ErrorMessage.Add(11021, "QOS efiltercount"); /* WSA_QOS_EFILTERCOUNT */ + s_ErrorMessage.Add(11022, "QOS eobjlength"); /* WSA_QOS_EOBJLENGTH */ + s_ErrorMessage.Add(11023, "QOS eflowcount"); /* WSA_QOS_EFLOWCOUNT */ + s_ErrorMessage.Add(11024, "QOS eunknownpsobj"); /* WSA_QOS_EUNKNOWNPSOBJ */ + s_ErrorMessage.Add(11025, "QOS epolicyobj"); /* WSA_QOS_EPOLICYOBJ */ + s_ErrorMessage.Add(11026, "QOS eflowdesc"); /* WSA_QOS_EFLOWDESC */ + s_ErrorMessage.Add(11027, "QOS epsflowspec"); /* WSA_QOS_EPSFLOWSPEC */ + s_ErrorMessage.Add(11028, "QOS epsfilterspec"); /* WSA_QOS_EPSFILTERSPEC */ + s_ErrorMessage.Add(11029, "QOS esdmodeobj"); /* WSA_QOS_ESDMODEOBJ */ + s_ErrorMessage.Add(11030, "QOS eshaperateobj"); /* WSA_QOS_ESHAPERATEOBJ */ + s_ErrorMessage.Add(11031, "QOS reserved petype"); /* WSA_QOS_RESERVED_PETYPE */ + s_ErrorMessage.Add(13000, "IPSEC qm policy exists"); /* ERROR_IPSEC_QM_POLICY_EXISTS */ + s_ErrorMessage.Add(13001, "IPSEC qm policy not found"); /* ERROR_IPSEC_QM_POLICY_NOT_FOUND */ + s_ErrorMessage.Add(13002, "IPSEC qm policy in use"); /* ERROR_IPSEC_QM_POLICY_IN_USE */ + s_ErrorMessage.Add(13003, "IPSEC mm policy exists"); /* ERROR_IPSEC_MM_POLICY_EXISTS */ + s_ErrorMessage.Add(13004, "IPSEC mm policy not found"); /* ERROR_IPSEC_MM_POLICY_NOT_FOUND */ + s_ErrorMessage.Add(13005, "IPSEC mm policy in use"); /* ERROR_IPSEC_MM_POLICY_IN_USE */ + s_ErrorMessage.Add(13006, "IPSEC mm filter exists"); /* ERROR_IPSEC_MM_FILTER_EXISTS */ + s_ErrorMessage.Add(13007, "IPSEC mm filter not found"); /* ERROR_IPSEC_MM_FILTER_NOT_FOUND */ + s_ErrorMessage.Add(13008, "IPSEC transport filter exists"); /* ERROR_IPSEC_TRANSPORT_FILTER_EXISTS */ + s_ErrorMessage.Add(13009, "IPSEC transport filter not found"); /* ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND */ + s_ErrorMessage.Add(13010, "IPSEC mm auth exists"); /* ERROR_IPSEC_MM_AUTH_EXISTS */ + s_ErrorMessage.Add(13011, "IPSEC mm auth not found"); /* ERROR_IPSEC_MM_AUTH_NOT_FOUND */ + s_ErrorMessage.Add(13012, "IPSEC mm auth in use"); /* ERROR_IPSEC_MM_AUTH_IN_USE */ + s_ErrorMessage.Add(13013, "IPSEC default mm policy not found"); /* ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND */ + s_ErrorMessage.Add(13014, "IPSEC default mm auth not found"); /* ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND */ + s_ErrorMessage.Add(13015, "IPSEC default qm policy not found"); /* ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND */ + s_ErrorMessage.Add(13016, "IPSEC tunnel filter exists"); /* ERROR_IPSEC_TUNNEL_FILTER_EXISTS */ + s_ErrorMessage.Add(13017, "IPSEC tunnel filter not found"); /* ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND */ + s_ErrorMessage.Add(13018, "IPSEC mm filter pending deletion"); /* ERROR_IPSEC_MM_FILTER_PENDING_DELETION */ + s_ErrorMessage.Add(13019, "IPSEC transport filter pending deletion"); /* ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION */ + s_ErrorMessage.Add(13020, "IPSEC tunnel filter pending deletion"); /* ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION */ + s_ErrorMessage.Add(13021, "IPSEC mm policy pending deletion"); /* ERROR_IPSEC_MM_POLICY_PENDING_DELETION */ + s_ErrorMessage.Add(13022, "IPSEC mm auth pending deletion"); /* ERROR_IPSEC_MM_AUTH_PENDING_DELETION */ + s_ErrorMessage.Add(13023, "IPSEC qm policy pending deletion"); /* ERROR_IPSEC_QM_POLICY_PENDING_DELETION */ + s_ErrorMessage.Add(13801, "IPSEC IKE auth fail"); /* ERROR_IPSEC_IKE_AUTH_FAIL */ + s_ErrorMessage.Add(13802, "IPSEC IKE attrib fail"); /* ERROR_IPSEC_IKE_ATTRIB_FAIL */ + s_ErrorMessage.Add(13803, "IPSEC IKE negotiation pending"); /* ERROR_IPSEC_IKE_NEGOTIATION_PENDING */ + s_ErrorMessage.Add(13804, "IPSEC IKE general processing error"); /* ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR */ + s_ErrorMessage.Add(13805, "IPSEC IKE timed out"); /* ERROR_IPSEC_IKE_TIMED_OUT */ + s_ErrorMessage.Add(13806, "IPSEC IKE no cert"); /* ERROR_IPSEC_IKE_NO_CERT */ + s_ErrorMessage.Add(13807, "IPSEC IKE sa deleted"); /* ERROR_IPSEC_IKE_SA_DELETED */ + s_ErrorMessage.Add(13808, "IPSEC IKE sa reaped"); /* ERROR_IPSEC_IKE_SA_REAPED */ + s_ErrorMessage.Add(13809, "IPSEC IKE mm acquire drop"); /* ERROR_IPSEC_IKE_MM_ACQUIRE_DROP */ + s_ErrorMessage.Add(13810, "IPSEC IKE qm acquire drop"); /* ERROR_IPSEC_IKE_QM_ACQUIRE_DROP */ + s_ErrorMessage.Add(13811, "IPSEC IKE queue drop mm"); /* ERROR_IPSEC_IKE_QUEUE_DROP_MM */ + s_ErrorMessage.Add(13812, "IPSEC IKE queue drop no mm"); /* ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM */ + s_ErrorMessage.Add(13813, "IPSEC IKE drop no response"); /* ERROR_IPSEC_IKE_DROP_NO_RESPONSE */ + s_ErrorMessage.Add(13814, "IPSEC IKE mm delay drop"); /* ERROR_IPSEC_IKE_MM_DELAY_DROP */ + s_ErrorMessage.Add(13815, "IPSEC IKE qm delay drop"); /* ERROR_IPSEC_IKE_QM_DELAY_DROP */ + s_ErrorMessage.Add(13816, "IPSEC IKE error"); /* ERROR_IPSEC_IKE_ERROR */ + s_ErrorMessage.Add(13817, "IPSEC IKE crl failed"); /* ERROR_IPSEC_IKE_CRL_FAILED */ + s_ErrorMessage.Add(13818, "IPSEC IKE invalid key usage"); /* ERROR_IPSEC_IKE_INVALID_KEY_USAGE */ + s_ErrorMessage.Add(13819, "IPSEC IKE invalid cert type"); /* ERROR_IPSEC_IKE_INVALID_CERT_TYPE */ + s_ErrorMessage.Add(13820, "IPSEC IKE no private key"); /* ERROR_IPSEC_IKE_NO_PRIVATE_KEY */ + s_ErrorMessage.Add(13822, "IPSEC IKE dh fail"); /* ERROR_IPSEC_IKE_DH_FAIL */ + s_ErrorMessage.Add(13824, "IPSEC IKE invalid header"); /* ERROR_IPSEC_IKE_INVALID_HEADER */ + s_ErrorMessage.Add(13825, "IPSEC IKE no policy"); /* ERROR_IPSEC_IKE_NO_POLICY */ + } + + public static void InitializeErrorMessages15() + { + s_ErrorMessage.Add(13826, "IPSEC IKE invalid signature"); /* ERROR_IPSEC_IKE_INVALID_SIGNATURE */ + s_ErrorMessage.Add(13827, "IPSEC IKE kerberos error"); /* ERROR_IPSEC_IKE_KERBEROS_ERROR */ + s_ErrorMessage.Add(13828, "IPSEC IKE no public key"); /* ERROR_IPSEC_IKE_NO_PUBLIC_KEY */ + s_ErrorMessage.Add(13829, "IPSEC IKE process err"); /* ERROR_IPSEC_IKE_PROCESS_ERR */ + s_ErrorMessage.Add(13830, "IPSEC IKE process err sa"); /* ERROR_IPSEC_IKE_PROCESS_ERR_SA */ + s_ErrorMessage.Add(13831, "IPSEC IKE process err prop"); /* ERROR_IPSEC_IKE_PROCESS_ERR_PROP */ + s_ErrorMessage.Add(13832, "IPSEC IKE process err trans"); /* ERROR_IPSEC_IKE_PROCESS_ERR_TRANS */ + s_ErrorMessage.Add(13833, "IPSEC IKE process err ke"); /* ERROR_IPSEC_IKE_PROCESS_ERR_KE */ + s_ErrorMessage.Add(13834, "IPSEC IKE process err ID"); /* ERROR_IPSEC_IKE_PROCESS_ERR_ID */ + s_ErrorMessage.Add(13835, "IPSEC IKE process err cert"); /* ERROR_IPSEC_IKE_PROCESS_ERR_CERT */ + s_ErrorMessage.Add(13836, "IPSEC IKE process err cert req"); /* ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ */ + s_ErrorMessage.Add(13837, "IPSEC IKE process err hash"); /* ERROR_IPSEC_IKE_PROCESS_ERR_HASH */ + s_ErrorMessage.Add(13838, "IPSEC IKE process err sig"); /* ERROR_IPSEC_IKE_PROCESS_ERR_SIG */ + s_ErrorMessage.Add(13839, "IPSEC IKE process err nonce"); /* ERROR_IPSEC_IKE_PROCESS_ERR_NONCE */ + s_ErrorMessage.Add(13840, "IPSEC IKE process err notify"); /* ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY */ + s_ErrorMessage.Add(13841, "IPSEC IKE process err delete"); /* ERROR_IPSEC_IKE_PROCESS_ERR_DELETE */ + s_ErrorMessage.Add(13842, "IPSEC IKE process err vendor"); /* ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR */ + s_ErrorMessage.Add(13843, "IPSEC IKE invalid payload"); /* ERROR_IPSEC_IKE_INVALID_PAYLOAD */ + s_ErrorMessage.Add(13844, "IPSEC IKE load soft sa"); /* ERROR_IPSEC_IKE_LOAD_SOFT_SA */ + s_ErrorMessage.Add(13845, "IPSEC IKE soft sa torn down"); /* ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN */ + s_ErrorMessage.Add(13846, "IPSEC IKE invalid cookie"); /* ERROR_IPSEC_IKE_INVALID_COOKIE */ + s_ErrorMessage.Add(13847, "IPSEC IKE no peer cert"); /* ERROR_IPSEC_IKE_NO_PEER_CERT */ + s_ErrorMessage.Add(13848, "IPSEC IKE peer CRL failed"); /* ERROR_IPSEC_IKE_PEER_CRL_FAILED */ + s_ErrorMessage.Add(13849, "IPSEC IKE policy change"); /* ERROR_IPSEC_IKE_POLICY_CHANGE */ + s_ErrorMessage.Add(13850, "IPSEC IKE no mm policy"); /* ERROR_IPSEC_IKE_NO_MM_POLICY */ + s_ErrorMessage.Add(13851, "IPSEC IKE notcbpriv"); /* ERROR_IPSEC_IKE_NOTCBPRIV */ + s_ErrorMessage.Add(13852, "IPSEC IKE secloadfail"); /* ERROR_IPSEC_IKE_SECLOADFAIL */ + s_ErrorMessage.Add(13853, "IPSEC IKE failsspinit"); /* ERROR_IPSEC_IKE_FAILSSPINIT */ + s_ErrorMessage.Add(13854, "IPSEC IKE failqueryssp"); /* ERROR_IPSEC_IKE_FAILQUERYSSP */ + s_ErrorMessage.Add(13855, "IPSEC IKE srvacqfail"); /* ERROR_IPSEC_IKE_SRVACQFAIL */ + s_ErrorMessage.Add(13856, "IPSEC IKE srvquerycred"); /* ERROR_IPSEC_IKE_SRVQUERYCRED */ + s_ErrorMessage.Add(13857, "IPSEC IKE getspifail"); /* ERROR_IPSEC_IKE_GETSPIFAIL */ + s_ErrorMessage.Add(13858, "IPSEC IKE invalid filter"); /* ERROR_IPSEC_IKE_INVALID_FILTER */ + s_ErrorMessage.Add(13859, "IPSEC IKE out of memory"); /* ERROR_IPSEC_IKE_OUT_OF_MEMORY */ + s_ErrorMessage.Add(13860, "IPSEC IKE add update key failed"); /* ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED */ + s_ErrorMessage.Add(13861, "IPSEC IKE invalid policy"); /* ERROR_IPSEC_IKE_INVALID_POLICY */ + s_ErrorMessage.Add(13862, "IPSEC IKE unknown doi"); /* ERROR_IPSEC_IKE_UNKNOWN_DOI */ + s_ErrorMessage.Add(13863, "IPSEC IKE invalid situation"); /* ERROR_IPSEC_IKE_INVALID_SITUATION */ + s_ErrorMessage.Add(13864, "IPSEC IKE dh failure"); /* ERROR_IPSEC_IKE_DH_FAILURE */ + s_ErrorMessage.Add(13865, "IPSEC IKE invalid group"); /* ERROR_IPSEC_IKE_INVALID_GROUP */ + s_ErrorMessage.Add(13866, "IPSEC IKE encrypt"); /* ERROR_IPSEC_IKE_ENCRYPT */ + s_ErrorMessage.Add(13867, "IPSEC IKE decrypt"); /* ERROR_IPSEC_IKE_DECRYPT */ + s_ErrorMessage.Add(13868, "IPSEC IKE policy match"); /* ERROR_IPSEC_IKE_POLICY_MATCH */ + s_ErrorMessage.Add(13869, "IPSEC IKE unsupported ID"); /* ERROR_IPSEC_IKE_UNSUPPORTED_ID */ + s_ErrorMessage.Add(13870, "IPSEC IKE invalid hash"); /* ERROR_IPSEC_IKE_INVALID_HASH */ + s_ErrorMessage.Add(13871, "IPSEC IKE invalid hash alg"); /* ERROR_IPSEC_IKE_INVALID_HASH_ALG */ + s_ErrorMessage.Add(13872, "IPSEC IKE invalid hash size"); /* ERROR_IPSEC_IKE_INVALID_HASH_SIZE */ + s_ErrorMessage.Add(13873, "IPSEC IKE invalid encrypt alg"); /* ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG */ + s_ErrorMessage.Add(13874, "IPSEC IKE invalid auth alg"); /* ERROR_IPSEC_IKE_INVALID_AUTH_ALG */ + s_ErrorMessage.Add(13875, "IPSEC IKE invalid sig"); /* ERROR_IPSEC_IKE_INVALID_SIG */ + s_ErrorMessage.Add(13876, "IPSEC IKE load failed"); /* ERROR_IPSEC_IKE_LOAD_FAILED */ + s_ErrorMessage.Add(13877, "IPSEC IKE rpc delete"); /* ERROR_IPSEC_IKE_RPC_DELETE */ + s_ErrorMessage.Add(13878, "IPSEC IKE benign reinit"); /* ERROR_IPSEC_IKE_BENIGN_REINIT */ + s_ErrorMessage.Add(13879, "IPSEC IKE invalid responder lifetime notify"); /* ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY */ + s_ErrorMessage.Add(13881, "IPSEC IKE invalid cert keylen"); /* ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN */ + s_ErrorMessage.Add(13882, "IPSEC IKE mm limit"); /* ERROR_IPSEC_IKE_MM_LIMIT */ + s_ErrorMessage.Add(13883, "IPSEC IKE negotiation disabled"); /* ERROR_IPSEC_IKE_NEGOTIATION_DISABLED */ + s_ErrorMessage.Add(13884, "IPSEC IKE neg status end"); /* ERROR_IPSEC_IKE_NEG_STATUS_END */ + s_ErrorMessage.Add(100001, "Device not configured"); /* WSAENXIO */ } +#endif // UNITY } } \ No newline at end of file diff --git a/mcs/class/System/System.CodeDom/CodeCompileUnit.cs b/mcs/class/System/System.CodeDom/CodeCompileUnit.cs new file mode 100644 index 000000000000..d4aa706cc148 --- /dev/null +++ b/mcs/class/System/System.CodeDom/CodeCompileUnit.cs @@ -0,0 +1,12 @@ +#if UNITY_AOT + +namespace System.CodeDom +{ + +public class CodeCompileUnit +{ +} + +} + +#endif \ No newline at end of file diff --git a/mcs/class/System/System.CodeDom/CodeTypeDeclaration.cs b/mcs/class/System/System.CodeDom/CodeTypeDeclaration.cs new file mode 100644 index 000000000000..9f20cb75d095 --- /dev/null +++ b/mcs/class/System/System.CodeDom/CodeTypeDeclaration.cs @@ -0,0 +1,12 @@ +#if UNITY_AOT + +namespace System.CodeDom +{ + +public class CodeTypeDeclaration +{ +} + +} + +#endif \ No newline at end of file diff --git a/mcs/class/System/System.Diagnostics/Process.cs b/mcs/class/System/System.Diagnostics/Process.cs index 35b7f3fac65f..6dfebd9a1978 100644 --- a/mcs/class/System/System.Diagnostics/Process.cs +++ b/mcs/class/System/System.Diagnostics/Process.cs @@ -60,9 +60,7 @@ private struct ProcInfo * the Start_internal icall in * mono/metadata/process.c */ - public IntPtr thread_handle; public int pid; // Contains -GetLastError () on failure. - public int tid; public string[] envVariables; public string UserName; public string Domain; diff --git a/mcs/class/System/System.IO/FileSystemWatcher.cs b/mcs/class/System/System.IO/FileSystemWatcher.cs index 53e31d61f7ea..155f75e8daca 100644 --- a/mcs/class/System/System.IO/FileSystemWatcher.cs +++ b/mcs/class/System/System.IO/FileSystemWatcher.cs @@ -123,6 +123,7 @@ void InitWatcher () ok = DefaultWatcher.GetInstance (out watcher); //ok = WindowsWatcher.GetInstance (out watcher); break; +#if !UNITY_AOT case 2: // libfam ok = FAMWatcher.GetInstance (out watcher, false); break; @@ -135,6 +136,7 @@ void InitWatcher () case 5: // inotify ok = InotifyWatcher.GetInstance (out watcher, true); break; +#endif } if (mode == 0 || !ok) { @@ -173,8 +175,10 @@ internal string MangledFilter { return mangledFilter; string filterLocal = "*.*"; +#if !UNITY_AOT if (!(watcher.GetType () == typeof (WindowsWatcher))) filterLocal = "*"; +#endif return filterLocal; } @@ -183,9 +187,11 @@ internal string MangledFilter { internal SearchPattern2 Pattern { get { if (pattern == null) { +#if !UNITY_AOT if (watcher.GetType () == typeof (KeventWatcher)) pattern = new SearchPattern2 (MangledFilter, true); //assume we want to ignore case (OS X) else +#endif pattern = new SearchPattern2 (MangledFilter); } return pattern; diff --git a/mcs/class/System/System.Net.NetworkInformation/IPAddressCollection.cs b/mcs/class/System/System.Net.NetworkInformation/IPAddressCollection.cs index 54b831f6a439..b8c824c415bc 100644 --- a/mcs/class/System/System.Net.NetworkInformation/IPAddressCollection.cs +++ b/mcs/class/System/System.Net.NetworkInformation/IPAddressCollection.cs @@ -84,6 +84,25 @@ public static Win32IPAddressCollection FromDnsServer (IntPtr ptr) return c; } + public static Win32IPAddressCollection FromSocketAddress (Win32_SOCKET_ADDRESS addr) + { + Win32IPAddressCollection c = new Win32IPAddressCollection (); + if (addr.Sockaddr != IntPtr.Zero) + c.InternalAdd (addr.GetIPAddress ()); + return c; + } + + public static Win32IPAddressCollection FromWinsServer (IntPtr ptr) + { + Win32IPAddressCollection c = new Win32IPAddressCollection (); + Win32_IP_ADAPTER_WINS_SERVER_ADDRESS a; + for (IntPtr p = ptr; p != IntPtr.Zero; p = a.Next) { + a = (Win32_IP_ADAPTER_WINS_SERVER_ADDRESS) Marshal.PtrToStructure (p, typeof (Win32_IP_ADAPTER_WINS_SERVER_ADDRESS)); + c.InternalAdd (a.Address.GetIPAddress ()); + } + return c; + } + void AddSubsequentlyString (IntPtr head) { Win32_IP_ADDR_STRING a; diff --git a/mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs b/mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs index 92bd3ffb363e..b30900dbb6ee 100644 --- a/mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs +++ b/mcs/class/System/System.Net.NetworkInformation/IPGlobalProperties.cs @@ -59,8 +59,17 @@ public override string DhcpScopeName { public override string DomainName { get { byte [] bytes = new byte [256]; - if (getdomainname (bytes, 256) != 0) - throw new NetworkInformationException (); +#if UNITY + try + { +#endif + if (getdomainname (bytes, 256) != 0) + throw new NetworkInformationException (); +#if UNITY + } catch (EntryPointNotFoundException) { + return String.Empty; + } +#endif int len = Array.IndexOf (bytes, 0); return Encoding.ASCII.GetString (bytes, 0, len < 0 ? 256 : len); } @@ -154,6 +163,17 @@ public override string DomainName { } #endif +#if UNITY + sealed class UnixNoLibCIPGlobalProperties : UnixIPGlobalProperties + { + public override string DomainName { + get { + return String.Empty; + } + } + } +#endif + // It expects /proc/net/snmp (or /usr/compat/linux/proc/net/snmp), // formatted like: // http://www.linuxdevcenter.com/linux/2000/11/16/example5.html @@ -364,8 +384,6 @@ class Win32IPGlobalProperties : IPGlobalProperties public const int AF_INET = 2; public const int AF_INET6 = 23; - // FIXME: it might be getting wrong table. I'm getting - // different results from .NET 2.0. unsafe void FillTcpTable (out List tab4, out List tab6) { tab4 = new List (); @@ -613,6 +631,9 @@ public override NetBiosNodeType NodeType { [DllImport ("iphlpapi.dll")] static extern int GetIpStatisticsEx (out Win32_MIB_IPSTATS pStats, int dwFamily); + [DllImport ("Ws2_32.dll")] + static extern ushort ntohs (ushort netshort); + // Win32 structures [StructLayout (LayoutKind.Explicit)] @@ -628,16 +649,16 @@ class Win32_MIB_TCPROW { public TcpState State; public uint LocalAddr; - public int LocalPort; + public uint LocalPort; public uint RemoteAddr; - public int RemotePort; + public uint RemotePort; public IPEndPoint LocalEndPoint { - get { return new IPEndPoint (LocalAddr, LocalPort); } + get { return new IPEndPoint (LocalAddr, ntohs((ushort)LocalPort)); } } public IPEndPoint RemoteEndPoint { - get { return new IPEndPoint (RemoteAddr, RemotePort); } + get { return new IPEndPoint (RemoteAddr, ntohs((ushort)RemotePort)); } } public TcpConnectionInformation TcpInfo { @@ -651,17 +672,17 @@ class Win32_MIB_TCP6ROW public TcpState State; public Win32_IN6_ADDR LocalAddr; public uint LocalScopeId; - public int LocalPort; + public uint LocalPort; public Win32_IN6_ADDR RemoteAddr; public uint RemoteScopeId; - public int RemotePort; + public uint RemotePort; public IPEndPoint LocalEndPoint { - get { return new IPEndPoint (new IPAddress (LocalAddr.Bytes, LocalScopeId), LocalPort); } + get { return new IPEndPoint (new IPAddress (LocalAddr.Bytes, LocalScopeId), ntohs((ushort)LocalPort)); } } public IPEndPoint RemoteEndPoint { - get { return new IPEndPoint (new IPAddress (RemoteAddr.Bytes, RemoteScopeId), RemotePort); } + get { return new IPEndPoint (new IPAddress (RemoteAddr.Bytes, RemoteScopeId), ntohs((ushort)RemotePort)); } } public TcpConnectionInformation TcpInfo { @@ -673,10 +694,10 @@ public TcpConnectionInformation TcpInfo { class Win32_MIB_UDPROW { public uint LocalAddr; - public int LocalPort; + public uint LocalPort; public IPEndPoint LocalEndPoint { - get { return new IPEndPoint (LocalAddr, LocalPort); } + get { return new IPEndPoint (LocalAddr, ntohs((ushort)LocalPort)); } } } @@ -685,10 +706,10 @@ class Win32_MIB_UDP6ROW { public Win32_IN6_ADDR LocalAddr; public uint LocalScopeId; - public int LocalPort; + public uint LocalPort; public IPEndPoint LocalEndPoint { - get { return new IPEndPoint (new IPAddress (LocalAddr.Bytes, LocalScopeId), LocalPort); } + get { return new IPEndPoint (new IPAddress (LocalAddr.Bytes, LocalScopeId), ntohs((ushort)LocalPort)); } } } } diff --git a/mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs b/mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs index bcae621e00d7..aa5bffa8307f 100644 --- a/mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs +++ b/mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs @@ -341,13 +341,11 @@ public Win32IPInterfaceProperties2 (Win32_IP_ADAPTER_ADDRESSES addr, Win32_MIB_I public override IPv4InterfaceProperties GetIPv4Properties () { - Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index); - return new Win32IPv4InterfaceProperties (v4info, mib4); + return new Win32IPv4InterfaceProperties (addr, mib4); } public override IPv6InterfaceProperties GetIPv6Properties () { - Win32_IP_ADAPTER_INFO v6info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib6.Index); return new Win32IPv6InterfaceProperties (mib6); } @@ -371,10 +369,9 @@ static IPAddressInformationCollection Win32FromAnycast (IntPtr ptr) public override IPAddressCollection DhcpServerAddresses { get { - Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index); // FIXME: should ipv6 DhcpServer be considered? try { - return new Win32IPAddressCollection (v4info.DhcpServer); + return Win32IPAddressCollection.FromSocketAddress (addr.Dhcpv4Server); } catch (IndexOutOfRangeException) { return Win32IPAddressCollection.Empty; } @@ -393,28 +390,17 @@ public override GatewayIPAddressInformationCollection GatewayAddresses { get { var col = new GatewayIPAddressInformationCollection (); try { - Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index); // FIXME: should ipv6 DhcpServer be considered? - - var a = v4info.GatewayList; - if (!String.IsNullOrEmpty (a.IpAddress)) { - col.InternalAdd(new SystemGatewayIPAddressInformation(IPAddress.Parse (a.IpAddress))); - AddSubsequently (a.Next, col); + Win32_IP_ADAPTER_GATEWAY_ADDRESS a; + for (IntPtr p = addr.FirstGatewayAddress; p != IntPtr.Zero; p = a.Next) { + a = (Win32_IP_ADAPTER_GATEWAY_ADDRESS) Marshal.PtrToStructure (p, typeof (Win32_IP_ADAPTER_GATEWAY_ADDRESS)); + col.InternalAdd (new SystemGatewayIPAddressInformation (a.Address.GetIPAddress ())); } } catch (IndexOutOfRangeException) {} return col; } } - static void AddSubsequently (IntPtr head, GatewayIPAddressInformationCollection col) - { - Win32_IP_ADDR_STRING a; - for (IntPtr p = head; p != IntPtr.Zero; p = a.Next) { - a = (Win32_IP_ADDR_STRING) Marshal.PtrToStructure (p, typeof (Win32_IP_ADDR_STRING)); - col.InternalAdd (new SystemGatewayIPAddressInformation (IPAddress.Parse (a.IpAddress))); - } - } - public override bool IsDnsEnabled { get { return Win32NetworkInterface.FixedInfo.EnableDns != 0; } } @@ -444,7 +430,6 @@ static MulticastIPAddressInformationCollection Win32FromMulticast (IntPtr ptr) public override UnicastIPAddressInformationCollection UnicastAddresses { get { try { - Win32_IP_ADAPTER_INFO ai = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index); // FIXME: should ipv6 DhcpServer be considered? return Win32FromUnicast (addr.FirstUnicastAddress); } catch (IndexOutOfRangeException) { @@ -467,9 +452,7 @@ static UnicastIPAddressInformationCollection Win32FromUnicast (IntPtr ptr) public override IPAddressCollection WinsServersAddresses { get { try { - Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index); - // FIXME: should ipv6 DhcpServer be considered? - return new Win32IPAddressCollection (v4info.PrimaryWinsServer, v4info.SecondaryWinsServer); + return Win32IPAddressCollection.FromWinsServer (addr.FirstWinsServerAddress); } catch (IndexOutOfRangeException) { return Win32IPAddressCollection.Empty; } diff --git a/mcs/class/System/System.Net.NetworkInformation/IPv4InterfaceProperties.cs b/mcs/class/System/System.Net.NetworkInformation/IPv4InterfaceProperties.cs index 86fba917ab90..ab92dd05f908 100644 --- a/mcs/class/System/System.Net.NetworkInformation/IPv4InterfaceProperties.cs +++ b/mcs/class/System/System.Net.NetworkInformation/IPv4InterfaceProperties.cs @@ -129,13 +129,13 @@ sealed class Win32IPv4InterfaceProperties : IPv4InterfaceProperties [DllImport ("iphlpapi.dll")] static extern int GetPerAdapterInfo (int IfIndex, Win32_IP_PER_ADAPTER_INFO pPerAdapterInfo, ref int pOutBufLen); - Win32_IP_ADAPTER_INFO ainfo; + Win32_IP_ADAPTER_ADDRESSES addr; Win32_IP_PER_ADAPTER_INFO painfo; Win32_MIB_IFROW mib; - public Win32IPv4InterfaceProperties (Win32_IP_ADAPTER_INFO ainfo, Win32_MIB_IFROW mib) + public Win32IPv4InterfaceProperties (Win32_IP_ADAPTER_ADDRESSES addr, Win32_MIB_IFROW mib) { - this.ainfo = ainfo; + this.addr = addr; this.mib = mib; // get per-adapter info. @@ -160,7 +160,7 @@ public override bool IsAutomaticPrivateAddressingEnabled { } public override bool IsDhcpEnabled { - get { return ainfo.DhcpEnabled != 0; } + get { return addr.DhcpEnabled; } } public override bool IsForwardingEnabled { @@ -173,7 +173,7 @@ public override int Mtu { } public override bool UsesWins { - get { return ainfo.HaveWins; } + get { return addr.FirstWinsServerAddress != IntPtr.Zero; } } } diff --git a/mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs b/mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs index fedf95dbb4d3..9743e0c58dcf 100644 --- a/mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs +++ b/mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs @@ -465,9 +465,13 @@ static Win32_IP_ADAPTER_ADDRESSES [] GetAdaptersAddresses () { IntPtr ptr = IntPtr.Zero; int len = 0; - GetAdaptersAddresses (0, 0, IntPtr.Zero, ptr, ref len); + uint flags = Win32_IP_ADAPTER_ADDRESSES.GAA_FLAG_INCLUDE_WINS_INFO | Win32_IP_ADAPTER_ADDRESSES.GAA_FLAG_INCLUDE_GATEWAYS; + GetAdaptersAddresses (0, flags, IntPtr.Zero, ptr, ref len); + if (Marshal.SizeOf (typeof (Win32_IP_ADAPTER_ADDRESSES)) > len) + throw new NetworkInformationException (); + ptr = Marshal.AllocHGlobal(len); - int ret = GetAdaptersAddresses (0, 0, IntPtr.Zero, ptr, ref len); + int ret = GetAdaptersAddresses (0, flags, IntPtr.Zero, ptr, ref len); if (ret != 0) throw new NetworkInformationException (ret); @@ -829,14 +833,6 @@ class Win32NetworkInterface2 : NetworkInterface [DllImport ("iphlpapi.dll", SetLastError = true)] static extern int GetIfEntry (ref Win32_MIB_IFROW row); - public static Win32_IP_ADAPTER_INFO GetAdapterInfoByIndex (int index) - { - foreach (Win32_IP_ADAPTER_INFO info in GetAdaptersInfo ()) - if (info.Index == index) - return info; - throw new IndexOutOfRangeException ("No adapter found for index " + index); - } - static Win32_IP_ADAPTER_INFO [] GetAdaptersInfo () { int len = 0; diff --git a/mcs/class/System/System.Net.NetworkInformation/Win32NetworkInterfaceMarshal.cs b/mcs/class/System/System.Net.NetworkInformation/Win32NetworkInterfaceMarshal.cs index 88ae0b8e3589..667428713214 100644 --- a/mcs/class/System/System.Net.NetworkInformation/Win32NetworkInterfaceMarshal.cs +++ b/mcs/class/System/System.Net.NetworkInformation/Win32NetworkInterfaceMarshal.cs @@ -121,15 +121,37 @@ struct Win32_IP_ADAPTER_ADDRESSES { public NetworkInterfaceType IfType; public OperationalStatus OperStatus; public int Ipv6IfIndex; - [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16 * 4)] + [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] public uint [] ZoneIndices; - - // Note that Vista-only members and XP-SP1-only member are - // omitted. + public IntPtr FirstPrefix; // to PIP_ADAPTER_PREFIX + public UInt64 TransmitLinkSpeed; + public UInt64 ReceiveLinkSpeed; + public IntPtr FirstWinsServerAddress; // to PIP_ADAPTER_WINS_SERVER_ADDRESS_LH + public IntPtr FirstGatewayAddress; // to PIP_ADAPTER_GATEWAY_ADDRESS_LH + public uint Ipv4Metric; + public uint Ipv6Metric; + public UInt64 Luid; + public Win32_SOCKET_ADDRESS Dhcpv4Server; + public uint CompartmentId; + public UInt64 NetworkGuid; + public int ConnectionType; + public int TunnelType; + public Win32_SOCKET_ADDRESS Dhcpv6Server; + [MarshalAs (UnmanagedType.ByValArray, SizeConst = MAX_DHCPV6_DUID_LENGTH)] + public byte [] Dhcpv6ClientDuid; + public ulong Dhcpv6ClientDuidLength; + public ulong Dhcpv6Iaid; + public IntPtr FirstDnsSuffix; // to PIP_ADAPTER_DNS_SUFFIX + + //Flags For GetAdapterAddresses + public const int GAA_FLAG_INCLUDE_WINS_INFO = 0x0040; + public const int GAA_FLAG_INCLUDE_GATEWAYS = 0x0080; const int MAX_ADAPTER_ADDRESS_LENGTH = 8; + const int MAX_DHCPV6_DUID_LENGTH = 130; const int IP_ADAPTER_DDNS_ENABLED = 1; + const int IP_ADAPTER_DHCP_ENABLED = 4; const int IP_ADAPTER_RECEIVE_ONLY = 8; const int IP_ADAPTER_NO_MULTICAST = 0x10; @@ -137,6 +159,10 @@ public bool DdnsEnabled { get { return (Flags & IP_ADAPTER_DDNS_ENABLED) != 0; } } + public bool DhcpEnabled { + get { return (Flags & IP_ADAPTER_DHCP_ENABLED) != 0; } + } + public bool IsReceiveOnly { get { return (Flags & IP_ADAPTER_RECEIVE_ONLY) != 0; } } @@ -267,6 +293,22 @@ struct Win32_IP_ADAPTER_MULTICAST_ADDRESS public Win32_SOCKET_ADDRESS Address; } + [StructLayout (LayoutKind.Sequential)] + struct Win32_IP_ADAPTER_GATEWAY_ADDRESS + { + public Win32LengthFlagsUnion LengthFlags; + public IntPtr Next; // to Win32_IP_ADAPTER_GATEWAY_ADDRESS + public Win32_SOCKET_ADDRESS Address; + } + + [StructLayout (LayoutKind.Sequential)] + struct Win32_IP_ADAPTER_WINS_SERVER_ADDRESS + { + public Win32LengthFlagsUnion LengthFlags; + public IntPtr Next; // to Win32_IP_ADAPTER_WINS_SERVER_ADDRESS + public Win32_SOCKET_ADDRESS Address; + } + [StructLayout (LayoutKind.Sequential)] struct Win32_IP_ADAPTER_UNICAST_ADDRESS { diff --git a/mcs/class/System/System.Net.Sockets/Socket.cs b/mcs/class/System/System.Net.Sockets/Socket.cs index fe7099144ec5..1012e951f777 100644 --- a/mcs/class/System/System.Net.Sockets/Socket.cs +++ b/mcs/class/System/System.Net.Sockets/Socket.cs @@ -47,7 +47,7 @@ using System.Timers; using System.Net.NetworkInformation; -namespace System.Net.Sockets +namespace System.Net.Sockets { public partial class Socket : IDisposable { @@ -91,7 +91,14 @@ public partial class Socket : IDisposable int m_IntCleanedUp; internal bool connect_in_progress; -#region Constructors +#if MONO_WEB_DEBUG + static int nextId; + internal readonly int ID = ++nextId; +#else + internal readonly int ID; +#endif + + #region Constructors public Socket (SocketInformation socketInformation) @@ -1160,7 +1167,24 @@ bool GetCheckedIPs (SocketAsyncEventArgs e, out IPAddress [] addresses) // while skipping entries that do not match the address family DnsEndPoint dep = e.RemoteEndPoint as DnsEndPoint; if (dep != null) { - addresses = Dns.GetHostAddresses (dep.Host); + if (dep.AddressFamily == AddressFamily.Unspecified) + { + addresses = Dns.GetHostAddresses(dep.Host); + } else { + var possibleAddresses = Dns.GetHostAddresses (dep.Host); + var numberOfAddresses = 0; + int[] addressIndices = new int[possibleAddresses.Length]; + for (var i = 0; i < possibleAddresses.Length; i++) { + if (possibleAddresses[i].AddressFamily == dep.AddressFamily) { + addressIndices[numberOfAddresses] = i; + numberOfAddresses++; + } + } + + addresses = new IPAddress[numberOfAddresses]; + for (var i = 0; i < numberOfAddresses; i++) + addresses[i] = possibleAddresses[addressIndices[i]]; + } return true; } else { e.ConnectByNameError = null; @@ -2793,9 +2817,16 @@ internal static int FamilyHint { } } +#if UNITY + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern static bool IsProtocolSupported_internal(NetworkInterfaceComponent networkInterface); +#endif + static bool IsProtocolSupported (NetworkInterfaceComponent networkInterface) { -#if MOBILE +#if UNITY + return IsProtocolSupported_internal(networkInterface); +#elif MOBILE return true; #else var nics = NetworkInterface.GetAllNetworkInterfaces (); diff --git a/mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs b/mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs index 57c549fd2a23..cf283fc560e8 100644 --- a/mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs +++ b/mcs/class/System/System.Net.Sockets/SocketAsyncEventArgs.cs @@ -262,7 +262,7 @@ internal void FinishWrapperConnectSuccess (Socket connectSocket, int bytesTransf SetResults(SocketError.Success, bytesTransferred, flags); current_socket = connectSocket; - OnCompleted (this); + Complete (); } internal void SetResults (SocketError socketError, int bytesTransferred, SocketFlags flags) diff --git a/mcs/class/System/System.Net/HttpWebRequest.cs b/mcs/class/System/System.Net/HttpWebRequest.cs index d05722051f93..542304837416 100644 --- a/mcs/class/System/System.Net/HttpWebRequest.cs +++ b/mcs/class/System/System.Net/HttpWebRequest.cs @@ -4,10 +4,12 @@ // Authors: // Lawrence Pit (loz@cable.a2000.nl) // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Martin Baulig // // (c) 2002 Lawrence Pit // (c) 2003 Ximian, Inc. (http://www.ximian.com) // (c) 2004 Novell, Inc. (http://www.novell.com) +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) // // @@ -30,7 +32,6 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // - #if SECURITY_DEP #if MONO_SECURITY_ALIAS extern alias MonoSecurity; @@ -54,12 +55,14 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; +using System.Threading.Tasks; using Mono.Net.Security; -namespace System.Net +namespace System.Net { [Serializable] - public class HttpWebRequest : WebRequest, ISerializable { + public class HttpWebRequest : WebRequest, ISerializable + { Uri requestUri; Uri actualUri; bool hostChanged; @@ -72,8 +75,7 @@ public class HttpWebRequest : WebRequest, ISerializable { HttpContinueDelegate continueDelegate; CookieContainer cookieContainer; ICredentials credentials; - bool haveResponse; - bool haveRequest; + bool haveResponse; bool requestSent; WebHeaderCollection webHeaders; bool keepAlive = true; @@ -91,23 +93,18 @@ public class HttpWebRequest : WebRequest, ISerializable { bool sendChunked; ServicePoint servicePoint; int timeout = 100000; - - WebConnectionStream writeStream; + + WebRequestStream writeStream; HttpWebResponse webResponse; - WebAsyncResult asyncWrite; - WebAsyncResult asyncRead; - EventHandler abortHandler; + WebCompletionSource responseTask; + WebOperation currentOperation; int aborted; bool gotRequestStream; int redirects; bool expectContinue; - byte[] bodyBuffer; - int bodyBufferLength; bool getResponseCalled; - Exception saved_exc; object locker = new object (); bool finished_reading; - internal WebConnection WebConnection; DecompressionMethods auto_decomp; int maxResponseHeadersLength; static int defaultMaxResponseHeadersLength; @@ -118,7 +115,8 @@ public class HttpWebRequest : WebRequest, ISerializable { #endif ServerCertValidationCallback certValidationCallback; - enum NtlmAuthState { + enum NtlmAuthState + { None, Challenge, Response @@ -127,7 +125,7 @@ enum NtlmAuthState { string host; [NonSerialized] - internal Action ResendContentFactory; + internal Func ResendContentFactory; // Constructors static HttpWebRequest () @@ -152,7 +150,7 @@ static HttpWebRequest () #else internal #endif - HttpWebRequest (Uri uri) + HttpWebRequest (Uri uri) { this.requestUri = uri; this.actualUri = uri; @@ -170,28 +168,28 @@ internal HttpWebRequest (Uri uri, MonoTlsProvider tlsProvider, MonoTlsSettings s this.tlsSettings = settings; } #endif - + [Obsolete ("Serialization is obsoleted for this type", false)] - protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) + protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) { SerializationInfo info = serializationInfo; - requestUri = (Uri) info.GetValue ("requestUri", typeof (Uri)); - actualUri = (Uri) info.GetValue ("actualUri", typeof (Uri)); + requestUri = (Uri)info.GetValue ("requestUri", typeof (Uri)); + actualUri = (Uri)info.GetValue ("actualUri", typeof (Uri)); allowAutoRedirect = info.GetBoolean ("allowAutoRedirect"); allowBuffering = info.GetBoolean ("allowBuffering"); - certificates = (X509CertificateCollection) info.GetValue ("certificates", typeof (X509CertificateCollection)); + certificates = (X509CertificateCollection)info.GetValue ("certificates", typeof (X509CertificateCollection)); connectionGroup = info.GetString ("connectionGroup"); contentLength = info.GetInt64 ("contentLength"); - webHeaders = (WebHeaderCollection) info.GetValue ("webHeaders", typeof (WebHeaderCollection)); + webHeaders = (WebHeaderCollection)info.GetValue ("webHeaders", typeof (WebHeaderCollection)); keepAlive = info.GetBoolean ("keepAlive"); maxAutoRedirect = info.GetInt32 ("maxAutoRedirect"); mediaType = info.GetString ("mediaType"); method = info.GetString ("method"); initialMethod = info.GetString ("initialMethod"); pipelined = info.GetBoolean ("pipelined"); - version = (Version) info.GetValue ("version", typeof (Version)); - proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy)); + version = (Version)info.GetValue ("version", typeof (Version)); + proxy = (IWebProxy)info.GetValue ("proxy", typeof (IWebProxy)); sendChunked = info.GetBoolean ("sendChunked"); timeout = info.GetInt32 ("timeout"); redirects = info.GetInt32 ("redirects"); @@ -199,45 +197,53 @@ protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext ResetAuthorization (); } +#if MONO_WEB_DEBUG + static int nextId; + internal readonly int ID = ++nextId; +#else + internal readonly int ID; +#endif + void ResetAuthorization () { auth_state = new AuthorizationState (this, false); proxy_auth_state = new AuthorizationState (this, true); } - + // Properties - void SetSpecialHeaders(string HeaderName, string value) { - value = WebHeaderCollection.CheckBadChars(value, true); - webHeaders.RemoveInternal(HeaderName); + void SetSpecialHeaders (string HeaderName, string value) + { + value = WebHeaderCollection.CheckBadChars (value, true); + webHeaders.RemoveInternal (HeaderName); if (value.Length != 0) { - webHeaders.AddInternal(HeaderName, value); + webHeaders.AddInternal (HeaderName, value); } } public string Accept { - get { return webHeaders ["Accept"]; } + get { return webHeaders["Accept"]; } set { CheckRequestStarted (); SetSpecialHeaders ("Accept", value); } } - + public Uri Address { get { return actualUri; } internal set { actualUri = value; } // Used by Ftp+proxy } - + public virtual bool AllowAutoRedirect { get { return allowAutoRedirect; } set { this.allowAutoRedirect = value; } } - + public virtual bool AllowWriteStreamBuffering { get { return allowBuffering; } set { allowBuffering = value; } } - + public virtual bool AllowReadStreamBuffering { get { return false; } set { @@ -250,9 +256,8 @@ static Exception GetMustImplement () { return new NotImplementedException (); } - - public DecompressionMethods AutomaticDecompression - { + + public DecompressionMethods AutomaticDecompression { get { return auto_decomp; } @@ -261,7 +266,7 @@ public DecompressionMethods AutomaticDecompression auto_decomp = value; } } - + internal bool InternalAllowBuffering { get { return allowBuffering && MethodWithBuffer; @@ -300,7 +305,7 @@ public X509CertificateCollection ClientCertificates { } public string Connection { - get { return webHeaders ["Connection"]; } + get { return webHeaders["Connection"]; } set { CheckRequestStarted (); @@ -315,59 +320,59 @@ public string Connection { if (keepAlive) value = value + ", Keep-Alive"; - + webHeaders.CheckUpdate ("Connection", value); } - } - - public override string ConnectionGroupName { + } + + public override string ConnectionGroupName { get { return connectionGroup; } set { connectionGroup = value; } } - - public override long ContentLength { + + public override long ContentLength { get { return contentLength; } - set { + set { CheckRequestStarted (); if (value < 0) throw new ArgumentOutOfRangeException ("value", "Content-Length must be >= 0"); - + contentLength = value; haveContentLength = true; } } - + internal long InternalContentLength { set { contentLength = value; } } - + internal bool ThrowOnError { get; set; } - - public override string ContentType { - get { return webHeaders ["Content-Type"]; } + + public override string ContentType { + get { return webHeaders["Content-Type"]; } set { SetSpecialHeaders ("Content-Type", value); } } - + public HttpContinueDelegate ContinueDelegate { get { return continueDelegate; } set { continueDelegate = value; } } - + virtual public CookieContainer CookieContainer { get { return cookieContainer; } set { cookieContainer = value; } } - - public override ICredentials Credentials { + + public override ICredentials Credentials { get { return credentials; } set { credentials = value; } } public DateTime Date { get { - string date = webHeaders ["Date"]; + string date = webHeaders["Date"]; if (date == null) return DateTime.MinValue; return DateTime.ParseExact (date, "r", CultureInfo.InvariantCulture).ToLocalTime (); @@ -377,17 +382,17 @@ public DateTime Date { } } - void SetDateHeaderHelper(string headerName, DateTime dateTime) { + void SetDateHeaderHelper (string headerName, DateTime dateTime) + { if (dateTime == DateTime.MinValue) - SetSpecialHeaders(headerName, null); // remove header + SetSpecialHeaders (headerName, null); // remove header else - SetSpecialHeaders(headerName, HttpProtocolUtils.date2string(dateTime)); + SetSpecialHeaders (headerName, HttpProtocolUtils.date2string (dateTime)); } #if !MOBILE [MonoTODO] - public static new RequestCachePolicy DefaultCachePolicy - { + public static new RequestCachePolicy DefaultCachePolicy { get { throw GetMustImplement (); } @@ -396,10 +401,9 @@ void SetDateHeaderHelper(string headerName, DateTime dateTime) { } } #endif - + [MonoTODO] - public static int DefaultMaximumErrorResponseLength - { + public static int DefaultMaximumErrorResponseLength { get { throw GetMustImplement (); } @@ -407,9 +411,9 @@ public static int DefaultMaximumErrorResponseLength throw GetMustImplement (); } } - + public string Expect { - get { return webHeaders ["Expect"]; } + get { return webHeaders["Expect"]; } set { CheckRequestStarted (); string val = value; @@ -428,19 +432,19 @@ public string Expect { webHeaders.CheckUpdate ("Expect", value); } } - + virtual public bool HaveResponse { get { return haveResponse; } } - - public override WebHeaderCollection Headers { + + public override WebHeaderCollection Headers { get { return webHeaders; } set { CheckRequestStarted (); WebHeaderCollection webHeaders = value; - WebHeaderCollection newWebHeaders = new WebHeaderCollection(WebHeaderCollectionType.HttpWebRequest); + WebHeaderCollection newWebHeaders = new WebHeaderCollection (WebHeaderCollectionType.HttpWebRequest); // Copy And Validate - // Handle the case where their object tries to change @@ -448,16 +452,15 @@ public override WebHeaderCollection Headers { // we need to clone their headers. // - foreach (String headerName in webHeaders.AllKeys ) { - newWebHeaders.Add(headerName,webHeaders[headerName]); + foreach (String headerName in webHeaders.AllKeys) { + newWebHeaders.Add (headerName, webHeaders[headerName]); } this.webHeaders = newWebHeaders; } } - - public - string Host { + + public string Host { get { if (host == null) return actualUri.Authority; @@ -479,7 +482,7 @@ static bool CheckValidHost (string scheme, string val) if (val.Length == 0) return false; - if (val [0] == '.') + if (val[0] == '.') return false; int idx = val.IndexOf ('/'); @@ -495,8 +498,8 @@ static bool CheckValidHost (string scheme, string val) } public DateTime IfModifiedSince { - get { - string str = webHeaders ["If-Modified-Since"]; + get { + string str = webHeaders["If-Modified-Since"]; if (str == null) return DateTime.Now; try { @@ -508,13 +511,13 @@ public DateTime IfModifiedSince { set { CheckRequestStarted (); // rfc-1123 pattern - webHeaders.SetInternal ("If-Modified-Since", + webHeaders.SetInternal ("If-Modified-Since", value.ToUniversalTime ().ToString ("r", null)); // TODO: check last param when using different locale } } - public bool KeepAlive { + public bool KeepAlive { get { return keepAlive; } @@ -522,7 +525,7 @@ public bool KeepAlive { keepAlive = value; } } - + public int MaximumAutomaticRedirections { get { return maxAutoRedirect; } set { @@ -530,7 +533,7 @@ public int MaximumAutomaticRedirections { throw new ArgumentException ("Must be > 0", "value"); maxAutoRedirect = value; - } + } } [MonoTODO ("Use this")] @@ -545,7 +548,7 @@ public static int DefaultMaximumResponseHeadersLength { set { defaultMaxResponseHeadersLength = value; } } - public int ReadWriteTimeout { + public int ReadWriteTimeout { get { return readWriteTimeout; } set { if (requestSent) @@ -557,23 +560,23 @@ public int ReadWriteTimeout { readWriteTimeout = value; } } - + [MonoTODO] public int ContinueTimeout { get { throw new NotImplementedException (); } set { throw new NotImplementedException (); } } - + public string MediaType { get { return mediaType; } - set { + set { mediaType = value; } } - - public override string Method { + + public override string Method { get { return this.method; } - set { + set { if (value == null || value.Trim () == "") throw new ArgumentException ("not a valid method"); @@ -585,43 +588,43 @@ public override string Method { } } } - + public bool Pipelined { get { return pipelined; } set { pipelined = value; } - } - - public override bool PreAuthenticate { + } + + public override bool PreAuthenticate { get { return preAuthenticate; } set { preAuthenticate = value; } } - + public Version ProtocolVersion { get { return version; } - set { + set { if (value != HttpVersion.Version10 && value != HttpVersion.Version11) throw new ArgumentException ("value"); force_version = true; - version = value; + version = value; } } - - public override IWebProxy Proxy { + + public override IWebProxy Proxy { get { return proxy; } - set { + set { CheckRequestStarted (); proxy = value; servicePoint = null; // we may need a new one GetServicePoint (); } } - + public string Referer { - get { return webHeaders ["Referer"]; } + get { return webHeaders["Referer"]; } set { CheckRequestStarted (); - if (value == null || value.Trim().Length == 0) { + if (value == null || value.Trim ().Length == 0) { webHeaders.RemoveInternal ("Referer"); return; } @@ -629,10 +632,10 @@ public string Referer { } } - public override Uri RequestUri { + public override Uri RequestUri { get { return requestUri; } } - + public bool SendChunked { get { return sendChunked; } set { @@ -640,7 +643,7 @@ public bool SendChunked { sendChunked = value; } } - + public ServicePoint ServicePoint { get { return GetServicePoint (); } } @@ -648,14 +651,14 @@ public ServicePoint ServicePoint { internal ServicePoint ServicePointNoLock { get { return servicePoint; } } - public virtual bool SupportsCookieContainer { + public virtual bool SupportsCookieContainer { get { // The managed implementation supports the cookie container // it is only Silverlight that returns false here return true; } } - public override int Timeout { + public override int Timeout { get { return timeout; } set { if (value < -1) @@ -664,9 +667,9 @@ public override int Timeout { timeout = value; } } - + public string TransferEncoding { - get { return webHeaders ["Transfer-Encoding"]; } + get { return webHeaders["Transfer-Encoding"]; } set { CheckRequestStarted (); string val = value; @@ -688,20 +691,18 @@ public string TransferEncoding { } } - public override bool UseDefaultCredentials - { + public override bool UseDefaultCredentials { get { return CredentialCache.DefaultCredentials == Credentials; } set { Credentials = value ? CredentialCache.DefaultCredentials : null; } } - + public string UserAgent { - get { return webHeaders ["User-Agent"]; } + get { return webHeaders["User-Agent"]; } set { webHeaders.SetInternal ("User-Agent", value); } } bool unsafe_auth_blah; - public bool UnsafeAuthenticatedConnectionSharing - { + public bool UnsafeAuthenticatedConnectionSharing { get { return unsafe_auth_blah; } set { unsafe_auth_blah = value; } } @@ -714,11 +715,11 @@ internal bool ExpectContinue { get { return expectContinue; } set { expectContinue = value; } } - + internal Uri AuthUri { get { return actualUri; } } - + internal bool ProxyQuery { get { return servicePoint.UsesProxy && !servicePoint.UseConnect; } } @@ -733,8 +734,7 @@ public RemoteCertificateValidationCallback ServerCertificateValidationCallback { return null; return certValidationCallback.ValidationCallback; } - set - { + set { if (value == null) certValidationCallback = null; else @@ -743,7 +743,7 @@ public RemoteCertificateValidationCallback ServerCertificateValidationCallback { } // Methods - + internal ServicePoint GetServicePoint () { lock (locker) { @@ -755,30 +755,30 @@ internal ServicePoint GetServicePoint () return servicePoint; } - + public void AddRange (int range) { - AddRange ("bytes", (long) range); + AddRange ("bytes", (long)range); } - + public void AddRange (int from, int to) { - AddRange ("bytes", (long) from, (long) to); + AddRange ("bytes", (long)from, (long)to); } - + public void AddRange (string rangeSpecifier, int range) { - AddRange (rangeSpecifier, (long) range); + AddRange (rangeSpecifier, (long)range); } - + public void AddRange (string rangeSpecifier, int from, int to) { - AddRange (rangeSpecifier, (long) from, (long) to); + AddRange (rangeSpecifier, (long)from, (long)to); } public void AddRange (long range) { - AddRange ("bytes", (long) range); + AddRange ("bytes", (long)range); } public @@ -795,7 +795,7 @@ void AddRange (string rangeSpecifier, long range) if (!WebHeaderCollection.IsValidToken (rangeSpecifier)) throw new ArgumentException ("Invalid range specifier", "rangeSpecifier"); - string r = webHeaders ["Range"]; + string r = webHeaders["Range"]; if (r == null) r = rangeSpecifier + "="; else { @@ -825,7 +825,7 @@ void AddRange (string rangeSpecifier, long from, long to) if (to < 0) throw new ArgumentOutOfRangeException ("to"); - string r = webHeaders ["Range"]; + string r = webHeaders["Range"]; if (r == null) r = rangeSpecifier + "="; else @@ -835,11 +835,38 @@ void AddRange (string rangeSpecifier, long from, long to) webHeaders.ChangeInternal ("Range", r); } - - public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) + WebOperation SendRequest (bool redirecting, BufferOffsetSize writeBuffer, CancellationToken cancellationToken) + { + lock (locker) { + WebConnection.Debug ($"HWR SEND REQUEST: Req={ID} requestSent={requestSent} redirecting={redirecting}"); + + WebOperation operation; + if (!redirecting) { + if (requestSent) { + operation = currentOperation; + if (operation == null) + throw new InvalidOperationException ("Should never happen!"); + return operation; + } + } + + operation = new WebOperation (this, writeBuffer, false, cancellationToken); + if (Interlocked.CompareExchange (ref currentOperation, operation, null) != null) + throw new InvalidOperationException ("Invalid nested call."); + + requestSent = true; + if (!redirecting) + redirects = 0; + servicePoint = GetServicePoint (); + servicePoint.SendRequest (operation, connectionGroup); + return operation; + } + } + + async Task MyGetRequestStreamAsync (CancellationToken cancellationToken) { if (Aborted) - throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled); + throw CreateRequestAbortedException (); bool send = !(method == "GET" || method == "CONNECT" || method == "HEAD" || method == "TRACE"); @@ -853,36 +880,29 @@ public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, obje if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "") throw new ProtocolViolationException ("SendChunked should be true."); - lock (locker) - { + WebOperation operation; + lock (locker) { if (getResponseCalled) throw new InvalidOperationException ("The operation cannot be performed once the request has been submitted."); - if (asyncWrite != null) { - throw new InvalidOperationException ("Cannot re-call start of asynchronous " + - "method while a previous call is still in progress."); - } - - asyncWrite = new WebAsyncResult (this, callback, state); - initialMethod = method; - if (haveRequest) { - if (writeStream != null) { - asyncWrite.SetCompleted (true, writeStream); - asyncWrite.DoCallback (); - return asyncWrite; - } - } - - gotRequestStream = true; - WebAsyncResult result = asyncWrite; - if (!requestSent) { - requestSent = true; - redirects = 0; - servicePoint = GetServicePoint (); - abortHandler = servicePoint.SendRequest (this, connectionGroup); + operation = currentOperation; + if (operation == null) { + initialMethod = method; + + gotRequestStream = true; + operation = SendRequest (false, null, cancellationToken); } - return result; } + + return await operation.GetRequestStream ().ConfigureAwait (false); + } + + public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) + { + if (Aborted) + throw CreateRequestAbortedException (); + + return TaskToApm.Begin (RunWithTimeout (MyGetRequestStreamAsync), callback, state); } public override Stream EndGetRequestStream (IAsyncResult asyncResult) @@ -890,34 +910,20 @@ public override Stream EndGetRequestStream (IAsyncResult asyncResult) if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); - WebAsyncResult result = asyncResult as WebAsyncResult; - if (result == null) - throw new ArgumentException ("Invalid IAsyncResult"); - - asyncWrite = result; - result.WaitUntilComplete (); - - Exception e = result.Exception; - if (e != null) - throw e; - - return result.WriteStream; - } - - public override Stream GetRequestStream() - { - IAsyncResult asyncResult = asyncWrite; - if (asyncResult == null) { - asyncResult = BeginGetRequestStream (null, null); - asyncWrite = (WebAsyncResult) asyncResult; + try { + return TaskToApm.End (asyncResult); + } catch (Exception e) { + throw FlattenException (e); } + } - if (!asyncResult.IsCompleted && !asyncResult.AsyncWaitHandle.WaitOne (timeout, false)) { - Abort (); - throw new WebException ("The request timed out", WebExceptionStatus.Timeout); + public override Stream GetRequestStream () + { + try { + return GetRequestStreamAsync ().Result; + } catch (Exception e) { + throw FlattenException (e); } - - return EndGetRequestStream (asyncResult); } [MonoTODO] @@ -926,30 +932,39 @@ public Stream GetRequestStream (out TransportContext context) throw new NotImplementedException (); } - bool CheckIfForceWrite (SimpleAsyncResult result) + internal static async Task RunWithTimeout (Func> func, int timeout, Action abort) { - if (writeStream == null || writeStream.RequestWritten || !InternalAllowBuffering) - return false; - if (contentLength < 0 && writeStream.CanWrite == true && writeStream.WriteBufferLength < 0) - return false; - - if (contentLength < 0 && writeStream.WriteBufferLength >= 0) - InternalContentLength = writeStream.WriteBufferLength; - - // This will write the POST/PUT if the write stream already has the expected - // amount of bytes in it (ContentLength) (bug #77753) or if the write stream - // contains data and it has been closed already (xamarin bug #1512). - - if (writeStream.WriteBufferLength == contentLength || (contentLength == -1 && writeStream.CanWrite == false)) - return writeStream.WriteRequestAsync (result); + using (var cts = new CancellationTokenSource ()) { + var timeoutTask = Task.Delay (timeout); + var workerTask = func (cts.Token); + var ret = await Task.WhenAny (workerTask, timeoutTask).ConfigureAwait (false); + if (ret == timeoutTask) { + try { + cts.Cancel (); + abort (); + } catch { + // Ignore; we report the timeout. + } +#pragma warning disable 4014 + // Make sure the workerTask's Exception is actually observed. + // Fixes https://github.com/mono/mono/issues/10488. + workerTask.ContinueWith (t => t.Exception?.GetHashCode (), TaskContinuationOptions.OnlyOnFaulted); +#pragma warning restore 4014 + throw new WebException (SR.net_timeout, WebExceptionStatus.Timeout); + } + return workerTask.Result; + } + } - return false; + Task RunWithTimeout (Func> func) + { + return RunWithTimeout (func, timeout, Abort); } - public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state) + async Task MyGetResponseAsync (CancellationToken cancellationToken) { if (Aborted) - throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled); + throw CreateRequestAbortedException (); if (method == null) throw new ProtocolViolationException ("Method is null."); @@ -958,93 +973,233 @@ public override IAsyncResult BeginGetResponse (AsyncCallback callback, object st if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "") throw new ProtocolViolationException ("SendChunked should be true."); - Monitor.Enter (locker); - getResponseCalled = true; - if (asyncRead != null && !haveResponse) { - Monitor.Exit (locker); - throw new InvalidOperationException ("Cannot re-call start of asynchronous " + - "method while a previous call is still in progress."); + var completion = new WebCompletionSource (); + WebOperation operation; + lock (locker) { + getResponseCalled = true; + var oldCompletion = Interlocked.CompareExchange (ref responseTask, completion, null); + WebConnection.Debug ($"HWR GET RESPONSE: Req={ID} {oldCompletion != null}"); + if (oldCompletion != null) { + oldCompletion.ThrowOnError (); + if (haveResponse && oldCompletion.IsCompleted) + return webResponse; + throw new InvalidOperationException ("Cannot re-call start of asynchronous " + + "method while a previous call is still in progress."); + } + + operation = currentOperation; + if (currentOperation != null) + writeStream = currentOperation.WriteStream; + + initialMethod = method; + + operation = SendRequest (false, null, cancellationToken); } - asyncRead = new WebAsyncResult (this, callback, state); - WebAsyncResult aread = asyncRead; - initialMethod = method; + while (true) { + WebException throwMe = null; + HttpWebResponse response = null; + WebResponseStream stream = null; + bool redirect = false; + bool mustReadAll = false; + WebOperation ntlm = null; + BufferOffsetSize writeBuffer = null; + + try { + cancellationToken.ThrowIfCancellationRequested (); - SimpleAsyncResult.RunWithLock (locker, CheckIfForceWrite, inner => { - var synch = inner.CompletedSynchronouslyPeek; + WebConnection.Debug ($"HWR GET RESPONSE LOOP: Req={ID} {auth_state.NtlmAuthState}"); - if (inner.GotException) { - aread.SetCompleted (synch, inner.Exception); - aread.DoCallback (); - return; + writeStream = await operation.GetRequestStream (); + await writeStream.WriteRequestAsync (cancellationToken).ConfigureAwait (false); + + stream = await operation.GetResponseStream (); + + WebConnection.Debug ($"HWR RESPONSE LOOP #0: Req={ID} - {stream?.Headers != null}"); + + (response, redirect, mustReadAll, writeBuffer, ntlm) = await GetResponseFromData ( + stream, cancellationToken).ConfigureAwait (false); + } catch (Exception e) { + throwMe = GetWebException (e); } - if (haveResponse) { - Exception saved = saved_exc; - if (webResponse != null) { - if (saved == null) { - aread.SetCompleted (synch, webResponse); - } else { - aread.SetCompleted (synch, saved); - } - aread.DoCallback (); - return; - } else if (saved != null) { - aread.SetCompleted (synch, saved); - aread.DoCallback (); - return; + WebConnection.Debug ($"HWR GET RESPONSE LOOP #1: Req={ID} - redirect={redirect} mustReadAll={mustReadAll} writeBuffer={writeBuffer != null} ntlm={ntlm != null} - {throwMe != null}"); + + lock (locker) { + if (throwMe != null) { + WebConnection.Debug ($"HWR GET RESPONSE LOOP #1 EX: Req={ID} {throwMe.Status} {throwMe.InnerException?.GetType ()}"); + haveResponse = true; + completion.TrySetException (throwMe); + throw throwMe; } - } - if (requestSent) - return; + if (!redirect) { + haveResponse = true; + webResponse = response; + completion.TrySetCompleted (); + return response; + } + + finished_reading = false; + haveResponse = false; + webResponse = null; + currentOperation = ntlm; + WebConnection.Debug ($"HWR GET RESPONSE LOOP #2: Req={ID} {mustReadAll} {ntlm}"); + } try { - requestSent = true; - redirects = 0; - servicePoint = GetServicePoint (); - abortHandler = servicePoint.SendRequest (this, connectionGroup); - } catch (Exception ex) { - aread.SetCompleted (synch, ex); - aread.DoCallback (); + if (mustReadAll) + await stream.ReadAllAsync (redirect || ntlm != null, cancellationToken).ConfigureAwait (false); + operation.CompleteResponseRead (true); + response.Close (); + } catch (Exception e) { + throwMe = GetWebException (e); } - }); - return aread; + lock (locker) { + WebConnection.Debug ($"HWR GET RESPONSE LOOP #3: Req={ID} {writeBuffer != null} {ntlm != null}"); + if (throwMe != null) { + WebConnection.Debug ($"HWR GET RESPONSE LOOP #3 EX: Req={ID} {throwMe.Status} {throwMe.InnerException?.GetType ()}"); + haveResponse = true; + stream?.Close (); + completion.TrySetException (throwMe); + throw throwMe; + } + + if (ntlm == null) { + operation = SendRequest (true, writeBuffer, cancellationToken); + } else { + operation = ntlm; + } + } + } } - public override WebResponse EndGetResponse (IAsyncResult asyncResult) + async Task<(HttpWebResponse response, bool redirect, bool mustReadAll, BufferOffsetSize writeBuffer, WebOperation ntlm)> + GetResponseFromData (WebResponseStream stream, CancellationToken cancellationToken) { - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); + /* + * WebConnection has either called SetResponseData() or SetResponseError(). + */ + + var response = new HttpWebResponse (actualUri, method, stream, cookieContainer); - WebAsyncResult result = asyncResult as WebAsyncResult; - if (result == null) - throw new ArgumentException ("Invalid IAsyncResult", "asyncResult"); + WebException throwMe = null; + bool redirect = false; + bool mustReadAll = false; + WebOperation ntlm = null; + Task rewriteHandler = null; + BufferOffsetSize writeBuffer = null; - if (!result.WaitUntilComplete (timeout, false)) { - Abort (); - throw new WebException("The request timed out", WebExceptionStatus.Timeout); + lock (locker) { + (redirect, mustReadAll, rewriteHandler, throwMe) = CheckFinalStatus (response); } - if (result.GotException) - throw result.Exception; + if (throwMe != null) { + if (mustReadAll) + await stream.ReadAllAsync (false, cancellationToken).ConfigureAwait (false); + throw throwMe; + } + + if (rewriteHandler != null) { + writeBuffer = await rewriteHandler.ConfigureAwait (false); + } - return result.Response; + lock (locker) { + bool isProxy = ProxyQuery && proxy != null && !proxy.IsBypassed (actualUri); + + if (!redirect) { + if ((isProxy ? proxy_auth_state : auth_state).IsNtlmAuthenticated && (int)response.StatusCode < 400) { + stream.Connection.NtlmAuthenticated = true; + } + + // clear internal buffer so that it does not + // hold possible big buffer (bug #397627) + if (writeStream != null) + writeStream.KillBuffer (); + + return (response, false, false, writeBuffer, null); + } + + if (sendChunked) { + sendChunked = false; + webHeaders.RemoveInternal ("Transfer-Encoding"); + } + + bool isChallenge; + (ntlm, isChallenge) = HandleNtlmAuth (stream, response, writeBuffer, cancellationToken); + WebConnection.Debug ($"HWR REDIRECT: {ntlm} {isChallenge} {mustReadAll}"); + } + + return (response, true, mustReadAll, writeBuffer, ntlm); + } + + internal static Exception FlattenException (Exception e) + { + if (e is AggregateException ae) { + ae = ae.Flatten (); + if (ae.InnerExceptions.Count == 1) + return ae.InnerException; + } + + return e; + } + + WebException GetWebException (Exception e) + { + e = FlattenException (e); + if (e is WebException wexc) { + if (!Aborted || wexc.Status == WebExceptionStatus.RequestCanceled || wexc.Status == WebExceptionStatus.Timeout) + return wexc; + } + if (Aborted || e is OperationCanceledException || e is ObjectDisposedException) + return CreateRequestAbortedException (); + return new WebException (e.Message, e, WebExceptionStatus.UnknownError, null); + } + + internal static WebException CreateRequestAbortedException () + { + return new WebException (SR.Format (SR.net_reqaborted, WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled); } - + + public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state) + { + if (Aborted) + throw CreateRequestAbortedException (); + + return TaskToApm.Begin (RunWithTimeout (MyGetResponseAsync), callback, state); + } + + public override WebResponse EndGetResponse (IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException (nameof (asyncResult)); + + try { + return TaskToApm.End (asyncResult); + } catch (Exception e) { + throw FlattenException (e); + } + } + public Stream EndGetRequestStream (IAsyncResult asyncResult, out TransportContext context) { + if (asyncResult == null) + throw new ArgumentNullException (nameof (asyncResult)); + context = null; return EndGetRequestStream (asyncResult); } - public override WebResponse GetResponse() + public override WebResponse GetResponse () { - WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null); - return EndGetResponse (result); + try { + return GetResponseAsync ().Result; + } catch (Exception e) { + throw FlattenException (e); + } } - + internal bool FinishedReading { get { return finished_reading; } set { finished_reading = value; } @@ -1059,56 +1214,23 @@ public override void Abort () if (Interlocked.CompareExchange (ref aborted, 1, 0) == 1) return; - if (haveResponse && finished_reading) - return; + WebConnection.Debug ($"HWR ABORT: Req={ID}"); haveResponse = true; - if (abortHandler != null) { - try { - abortHandler (this, EventArgs.Empty); - } catch (Exception) {} - abortHandler = null; - } - - if (asyncWrite != null) { - WebAsyncResult r = asyncWrite; - if (!r.IsCompleted) { - try { - WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); - r.SetCompleted (false, wexc); - r.DoCallback (); - } catch {} - } - asyncWrite = null; - } - - if (asyncRead != null) { - WebAsyncResult r = asyncRead; - if (!r.IsCompleted) { - try { - WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); - r.SetCompleted (false, wexc); - r.DoCallback (); - } catch {} - } - asyncRead = null; - } + var operation = currentOperation; + if (operation != null) + operation.Abort (); - if (writeStream != null) { - try { - writeStream.Close (); - writeStream = null; - } catch {} - } + responseTask?.TrySetCanceled (); if (webResponse != null) { try { webResponse.Close (); webResponse = null; - } catch {} + } catch { } } - } - + } + void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext) { @@ -1141,8 +1263,8 @@ protected override void GetObjectData (SerializationInfo serializationInfo, info.AddValue ("redirects", redirects); info.AddValue ("host", host); } - - void CheckRequestStarted () + + void CheckRequestStarted () { if (requestSent) throw new InvalidOperationException ("request started"); @@ -1160,8 +1282,8 @@ void RewriteRedirectToGet () webHeaders.RemoveInternal ("Transfer-Encoding"); sendChunked = false; } - - bool Redirect (WebAsyncResult result, HttpStatusCode code, WebResponse response) + + bool Redirect (HttpStatusCode code, WebResponse response) { redirects++; Exception e = null; @@ -1187,12 +1309,13 @@ bool Redirect (WebAsyncResult result, HttpStatusCode code, WebResponse response) break; case HttpStatusCode.Unused: // 306 default: - e = new ProtocolViolationException ("Invalid status code: " + (int) code); + e = new ProtocolViolationException ("Invalid status code: " + (int)code); break; } - if (method != "GET" && !InternalAllowBuffering && (writeStream.WriteBufferLength > 0 || contentLength > 0)) - e = new WebException ("The request requires buffering data to succeed.", null, WebExceptionStatus.ProtocolError, webResponse); + if (method != "GET" && !InternalAllowBuffering && ResendContentFactory == null && + (writeStream.WriteBufferLength > 0 || contentLength > 0)) + e = new WebException ("The request requires buffering data to succeed.", null, WebExceptionStatus.ProtocolError, response); if (e != null) throw e; @@ -1200,19 +1323,18 @@ bool Redirect (WebAsyncResult result, HttpStatusCode code, WebResponse response) if (AllowWriteStreamBuffering || method == "GET") contentLength = -1; - uriString = webResponse.Headers ["Location"]; + uriString = response.Headers["Location"]; if (uriString == null) - throw new WebException ("No Location header found for " + (int) code, - WebExceptionStatus.ProtocolError); + throw new WebException ($"No Location header found for {(int)code}", null, + WebExceptionStatus.ProtocolError, response); Uri prev = actualUri; try { actualUri = new Uri (actualUri, uriString); } catch (Exception) { - throw new WebException (String.Format ("Invalid URL ({0}) for {1}", - uriString, (int) code), - WebExceptionStatus.ProtocolError); + throw new WebException ($"Invalid URL ({uriString}) for {(int)code}", + null, WebExceptionStatus.ProtocolError, response); } hostChanged = (actualUri.Scheme != prev.Scheme || Host != prev.Authority); @@ -1247,7 +1369,7 @@ string GetHeaders () if (actualVersion == HttpVersion.Version11 && continue100 && servicePoint.SendContinue) { // RFC2616 8.2.3 - webHeaders.ChangeInternal ("Expect" , "100-continue"); + webHeaders.ChangeInternal ("Expect", "100-continue"); expectContinue = true; } else { webHeaders.RemoveInternal ("Expect"); @@ -1302,36 +1424,9 @@ void DoPreAuthenticate () webHeaders.RemoveInternal ("Proxy-Authorization"); webHeaders.RemoveInternal ("Authorization"); string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization"; - webHeaders [authHeader] = auth.Message; + webHeaders[authHeader] = auth.Message; usedPreAuth = true; } - - internal void SetWriteStreamError (WebExceptionStatus status, Exception exc) - { - if (Aborted) - return; - - WebAsyncResult r = asyncWrite; - if (r == null) - r = asyncRead; - - if (r != null) { - string msg; - WebException wex; - if (exc == null) { - msg = "Error: " + status; - wex = new WebException (msg, status); - } else { - wex = exc as WebException; - if (wex == null) { - msg = String.Format ("Error: {0} ({1})", status, exc.Message); - wex = new WebException (msg, status, WebExceptionInternalStatus.RequestFatal, exc); - } - } - r.SetCompleted (false, wex); - r.DoCallback (); - } - } internal byte[] GetRequestHeaders () { @@ -1340,11 +1435,11 @@ internal byte[] GetRequestHeaders () if (!ProxyQuery) { query = actualUri.PathAndQuery; } else { - query = String.Format ("{0}://{1}{2}", actualUri.Scheme, + query = String.Format ("{0}://{1}{2}", actualUri.Scheme, Host, actualUri.PathAndQuery); } - + if (!force_version && servicePoint.ProtocolVersion != null && servicePoint.ProtocolVersion < version) { actualVersion = servicePoint.ProtocolVersion; } else { @@ -1358,251 +1453,23 @@ internal byte[] GetRequestHeaders () return Encoding.UTF8.GetBytes (reqstr); } - internal void SetWriteStream (WebConnectionStream stream) - { - if (Aborted) - return; - - writeStream = stream; - if (bodyBuffer != null) { - webHeaders.RemoveInternal ("Transfer-Encoding"); - contentLength = bodyBufferLength; - writeStream.SendChunked = false; - } - - writeStream.SetHeadersAsync (false, result => { - if (result.GotException) { - SetWriteStreamError (result.Exception); - return; - } - - haveRequest = true; - - SetWriteStreamInner (inner => { - if (inner.GotException) { - SetWriteStreamError (inner.Exception); - return; - } - - if (asyncWrite != null) { - asyncWrite.SetCompleted (inner.CompletedSynchronouslyPeek, writeStream); - asyncWrite.DoCallback (); - asyncWrite = null; - } - }); - }); - } - - void SetWriteStreamInner (SimpleAsyncCallback callback) - { - SimpleAsyncResult.Run (result => { - if (bodyBuffer != null) { - // The body has been written and buffered. The request "user" - // won't write it again, so we must do it. - if (auth_state.NtlmAuthState != NtlmAuthState.Challenge && proxy_auth_state.NtlmAuthState != NtlmAuthState.Challenge) { - // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion - writeStream.Write (bodyBuffer, 0, bodyBufferLength); - bodyBuffer = null; - writeStream.Close (); - } - } else if (MethodWithBuffer) { - if (getResponseCalled && !writeStream.RequestWritten) - return writeStream.WriteRequestAsync (result); - } - - return false; - }, callback); - } - - void SetWriteStreamError (Exception exc) - { - WebException wexc = exc as WebException; - if (wexc != null) - SetWriteStreamError (wexc.Status, wexc); - else - SetWriteStreamError (WebExceptionStatus.SendFailure, exc); - } - - internal void SetResponseError (WebExceptionStatus status, Exception e, string where) - { - if (Aborted) - return; - lock (locker) { - string msg = String.Format ("Error getting response stream ({0}): {1}", where, status); - WebAsyncResult r = asyncRead; - if (r == null) - r = asyncWrite; - - WebException wexc; - if (e is WebException) { - wexc = (WebException) e; - } else { - wexc = new WebException (msg, e, status, null); - } - if (r != null) { - if (!r.IsCompleted) { - r.SetCompleted (false, wexc); - r.DoCallback (); - } else if (r == asyncWrite) { - saved_exc = wexc; - } - haveResponse = true; - asyncRead = null; - asyncWrite = null; - } else { - haveResponse = true; - saved_exc = wexc; - } - } - } - - void CheckSendError (WebConnectionData data) + (WebOperation, bool) HandleNtlmAuth (WebResponseStream stream, HttpWebResponse response, + BufferOffsetSize writeBuffer, CancellationToken cancellationToken) { - // Got here, but no one called GetResponse - int status = data.StatusCode; - if (status < 400 || status == 401 || status == 407) - return; - - if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) { - // The request has not been completely sent and we got here! - // We should probably just close and cause an error in any case, - saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse); - if (allowBuffering || sendChunked || writeStream.totalWritten >= contentLength) { - webResponse.ReadAll (); - } else { - writeStream.IgnoreIOErrors = true; - } - } - } - - bool HandleNtlmAuth (WebAsyncResult r) - { - bool isProxy = webResponse.StatusCode == HttpStatusCode.ProxyAuthenticationRequired; - if ((isProxy ? proxy_auth_state.NtlmAuthState : auth_state.NtlmAuthState) == NtlmAuthState.None) - return false; - - WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream; - if (wce != null) { - WebConnection cnc = wce.Connection; - cnc.PriorityRequest = this; - ICredentials creds = (!isProxy || proxy == null) ? credentials : proxy.Credentials; - if (creds != null) { - cnc.NtlmCredential = creds.GetCredential (requestUri, "NTLM"); - cnc.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah; - } - } - r.Reset (); - finished_reading = false; - haveResponse = false; - webResponse.ReadAll (); - webResponse = null; - return true; - } - - internal void SetResponseData (WebConnectionData data) - { - lock (locker) { - if (Aborted) { - if (data.stream != null) - data.stream.Close (); - return; - } - - WebException wexc = null; - try { - webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer); - } catch (Exception e) { - wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null); - if (data.stream != null) - data.stream.Close (); - } - - if (wexc == null && (method == "POST" || method == "PUT")) { - CheckSendError (data); - if (saved_exc != null) - wexc = (WebException) saved_exc; - } - - WebAsyncResult r = asyncRead; - - bool forced = false; - if (r == null && webResponse != null) { - // This is a forced completion (302, 204)... - forced = true; - r = new WebAsyncResult (null, null); - r.SetCompleted (false, webResponse); - } - - if (r != null) { - if (wexc != null) { - haveResponse = true; - if (!r.IsCompleted) - r.SetCompleted (false, wexc); - r.DoCallback (); - return; - } + bool isProxy = response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired; + if ((isProxy ? proxy_auth_state : auth_state).NtlmAuthState == NtlmAuthState.None) + return (null, false); - bool isProxy = ProxyQuery && proxy != null && !proxy.IsBypassed (actualUri); + var isChallenge = auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge; - bool redirected; - try { - redirected = CheckFinalStatus (r); - if (!redirected) { - if ((isProxy ? proxy_auth_state.IsNtlmAuthenticated : auth_state.IsNtlmAuthenticated) && - webResponse != null && (int)webResponse.StatusCode < 400) { - WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream; - if (wce != null) { - WebConnection cnc = wce.Connection; - cnc.NtlmAuthenticated = true; - } - } - - // clear internal buffer so that it does not - // hold possible big buffer (bug #397627) - if (writeStream != null) - writeStream.KillBuffer (); - - haveResponse = true; - r.SetCompleted (false, webResponse); - r.DoCallback (); - } else { - if (sendChunked) { - sendChunked = false; - webHeaders.RemoveInternal ("Transfer-Encoding"); - } - - if (webResponse != null) { - if (HandleNtlmAuth (r)) - return; - webResponse.Close (); - } - finished_reading = false; - haveResponse = false; - webResponse = null; - r.Reset (); - servicePoint = GetServicePoint (); - abortHandler = servicePoint.SendRequest (this, connectionGroup); - } - } catch (WebException wexc2) { - if (forced) { - saved_exc = wexc2; - haveResponse = true; - } - r.SetCompleted (false, wexc2); - r.DoCallback (); - return; - } catch (Exception ex) { - wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null); - if (forced) { - saved_exc = wexc; - haveResponse = true; - } - r.SetCompleted (false, wexc); - r.DoCallback (); - return; - } - } + var operation = new WebOperation (this, writeBuffer, isChallenge, cancellationToken); + stream.Operation.SetPriorityRequest (operation); + var creds = (!isProxy || proxy == null) ? credentials : proxy.Credentials; + if (creds != null) { + stream.Connection.NtlmCredential = creds.GetCredential (requestUri, "NTLM"); + stream.Connection.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah; } + return (operation, isChallenge); } struct AuthorizationState @@ -1645,7 +1512,7 @@ public bool CheckAuthorization (WebResponse response, HttpStatusCode code) if (isProxy && (request.proxy == null || request.proxy.Credentials == null)) return false; - string [] authHeaders = response.Headers.GetValues (isProxy ? "Proxy-Authenticate" : "WWW-Authenticate"); + string[] authHeaders = response.Headers.GetValues (isProxy ? "Proxy-Authenticate" : "WWW-Authenticate"); if (authHeaders == null || authHeaders.Length == 0) return false; @@ -1658,11 +1525,11 @@ public bool CheckAuthorization (WebResponse response, HttpStatusCode code) } if (auth == null) return false; - request.webHeaders [isProxy ? "Proxy-Authorization" : "Authorization"] = auth.Message; + request.webHeaders[isProxy ? "Proxy-Authorization" : "Authorization"] = auth.Message; isCompleted = auth.Complete; bool is_ntlm = (auth.ModuleAuthenticationType == "NTLM"); if (is_ntlm) - ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1); + ntlm_auth_state = (NtlmAuthState)((int)ntlm_auth_state + 1); return true; } @@ -1685,117 +1552,116 @@ bool CheckAuthorization (WebResponse response, HttpStatusCode code) return isProxy ? proxy_auth_state.CheckAuthorization (response, code) : auth_state.CheckAuthorization (response, code); } - // Returns true if redirected - bool CheckFinalStatus (WebAsyncResult result) + (Task task, WebException throwMe) GetRewriteHandler (HttpWebResponse response, bool redirect) { - if (result.GotException) { - bodyBuffer = null; - throw result.Exception; + if (redirect) { + if (!MethodWithBuffer) + return (null, null); + + if (writeStream.WriteBufferLength == 0 || contentLength == 0) + return (null, null); } - Exception throwMe = result.Exception; + // Keep the written body, so it can be rewritten in the retry + if (AllowWriteStreamBuffering) + return (Task.FromResult (writeStream.GetWriteBuffer ()), null); - HttpWebResponse resp = result.Response; - WebExceptionStatus protoError = WebExceptionStatus.ProtocolError; - HttpStatusCode code = 0; - if (throwMe == null && webResponse != null) { - code = webResponse.StatusCode; - if ((!auth_state.IsCompleted && code == HttpStatusCode.Unauthorized && credentials != null) || - (ProxyQuery && !proxy_auth_state.IsCompleted && code == HttpStatusCode.ProxyAuthenticationRequired)) { - if (!usedPreAuth && CheckAuthorization (webResponse, code)) { - // Keep the written body, so it can be rewritten in the retry - if (MethodWithBuffer) { - if (AllowWriteStreamBuffering) { - if (writeStream.WriteBufferLength > 0) { - bodyBuffer = writeStream.WriteBuffer; - bodyBufferLength = writeStream.WriteBufferLength; - } - - return true; - } - - // - // Buffering is not allowed but we have alternative way to get same content (we - // need to resent it due to NTLM Authentication). - // - if (ResendContentFactory != null) { - using (var ms = new MemoryStream ()) { - ResendContentFactory (ms); - bodyBuffer = ms.ToArray (); - bodyBufferLength = bodyBuffer.Length; - } - return true; - } - } else if (method != "PUT" && method != "POST") { - bodyBuffer = null; - return true; - } - - if (!ThrowOnError) - return false; - - writeStream.InternalClose (); - writeStream = null; - webResponse.Close (); - webResponse = null; - bodyBuffer = null; - - throw new WebException ("This request requires buffering " + - "of data for authentication or " + - "redirection to be sucessful."); - } + if (ResendContentFactory == null) + return (null, new WebException ( + "The request requires buffering data to succeed.", null, WebExceptionStatus.ProtocolError, response)); + + Func> handleResendContentFactory = async () => { + using (var ms = new MemoryStream ()) { + await ResendContentFactory (ms).ConfigureAwait (false); + var buffer = ms.ToArray (); + return new BufferOffsetSize (buffer, 0, buffer.Length, false); } + }; + + // + // Buffering is not allowed but we have alternative way to get same content (we + // need to resent it due to NTLM Authentication). + // + return (handleResendContentFactory (), null); + } + + // Returns true if redirected + (bool redirect, bool mustReadAll, Task writeBuffer, WebException throwMe) CheckFinalStatus (HttpWebResponse response) + { + WebException throwMe = null; + + bool mustReadAll = false; + HttpStatusCode code = 0; + Task rewriteHandler = null; + + code = response.StatusCode; + if ((!auth_state.IsCompleted && code == HttpStatusCode.Unauthorized && credentials != null) || + (ProxyQuery && !proxy_auth_state.IsCompleted && code == HttpStatusCode.ProxyAuthenticationRequired)) { + if (!usedPreAuth && CheckAuthorization (response, code)) { + mustReadAll = true; + + // HEAD, GET, MKCOL, CONNECT, TRACE + if (!MethodWithBuffer) + return (true, mustReadAll, null, null); + + (rewriteHandler, throwMe) = GetRewriteHandler (response, false); + if (throwMe == null) + return (true, mustReadAll, rewriteHandler, null); - bodyBuffer = null; - if ((int) code >= 400) { - string err = String.Format ("The remote server returned an error: ({0}) {1}.", - (int) code, webResponse.StatusDescription); - throwMe = new WebException (err, null, protoError, webResponse); - webResponse.ReadAll (); - } else if ((int) code == 304 && allowAutoRedirect) { - string err = String.Format ("The remote server returned an error: ({0}) {1}.", - (int) code, webResponse.StatusDescription); - throwMe = new WebException (err, null, protoError, webResponse); - } else if ((int) code >= 300 && allowAutoRedirect && redirects >= maxAutoRedirect) { - throwMe = new WebException ("Max. redirections exceeded.", null, - protoError, webResponse); - webResponse.ReadAll (); + if (!ThrowOnError) + return (false, mustReadAll, null, null); + + writeStream.InternalClose (); + writeStream = null; + response.Close (); + + return (false, mustReadAll, null, throwMe); } } - bodyBuffer = null; + if ((int)code >= 400) { + string err = String.Format ("The remote server returned an error: ({0}) {1}.", + (int)code, response.StatusDescription); + throwMe = new WebException (err, null, WebExceptionStatus.ProtocolError, response); + mustReadAll = true; + } else if ((int)code == 304 && allowAutoRedirect) { + string err = String.Format ("The remote server returned an error: ({0}) {1}.", + (int)code, response.StatusDescription); + throwMe = new WebException (err, null, WebExceptionStatus.ProtocolError, response); + } else if ((int)code >= 300 && allowAutoRedirect && redirects >= maxAutoRedirect) { + throwMe = new WebException ("Max. redirections exceeded.", null, + WebExceptionStatus.ProtocolError, response); + mustReadAll = true; + } + if (throwMe == null) { + int c = (int)code; bool b = false; - int c = (int) code; if (allowAutoRedirect && c >= 300) { - b = Redirect (result, code, webResponse); - if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) { - bodyBuffer = writeStream.WriteBuffer; - bodyBufferLength = writeStream.WriteBufferLength; - } + b = Redirect (code, response); + (rewriteHandler, throwMe) = GetRewriteHandler (response, true); if (b && !unsafe_auth_blah) { auth_state.Reset (); proxy_auth_state.Reset (); } } - if (resp != null && c >= 300 && c != 304) - resp.ReadAll (); + if (c >= 300 && c != 304) + mustReadAll = true; - return b; + if (throwMe == null) + return (b, mustReadAll, rewriteHandler, null); } - + if (!ThrowOnError) - return false; + return (false, mustReadAll, null, null); if (writeStream != null) { writeStream.InternalClose (); writeStream = null; } - webResponse = null; - - throw throwMe; + return (false, mustReadAll, null, throwMe); } internal bool ReuseConnection { @@ -1803,8 +1669,6 @@ internal bool ReuseConnection { set; } - internal WebConnection StoredConnection; - #region referencesource internal static StringBuilder GenerateConnectionGroup(string connectionGroupName, bool unsafeConnectionGroup, bool isInternalGroup) { diff --git a/mcs/class/System/System.Net/HttpWebResponse.cs b/mcs/class/System/System.Net/HttpWebResponse.cs index 5a3208497235..c44949811c2f 100644 --- a/mcs/class/System/System.Net/HttpWebResponse.cs +++ b/mcs/class/System/System.Net/HttpWebResponse.cs @@ -39,6 +39,8 @@ using System.IO.Compression; using System.Net.Sockets; using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; using System.Text; namespace System.Net @@ -60,16 +62,28 @@ public class HttpWebResponse : WebResponse, ISerializable, IDisposable { Stream stream; // Constructors + + internal HttpWebResponse (Uri uri, string method, HttpStatusCode status, WebHeaderCollection headers) + { + this.uri = uri; + this.method = method; + this.statusCode = status; + this.statusDescription = HttpStatusDescription.Get (status); + this.webHeaders = headers; + version = HttpVersion.Version10; + contentLength = -1; + } - internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container) + internal HttpWebResponse (Uri uri, string method, WebResponseStream stream, CookieContainer container) { this.uri = uri; this.method = method; - webHeaders = data.Headers; - version = data.Version; - statusCode = (HttpStatusCode) data.StatusCode; - statusDescription = data.StatusDescription; - stream = data.stream; + this.stream = stream; + + webHeaders = stream.Headers ?? new WebHeaderCollection (); + version = stream.Version; + statusCode = stream.StatusCode; + statusDescription = stream.StatusDescription ?? HttpStatusDescription.Get (statusCode); contentLength = -1; try { @@ -86,12 +100,12 @@ internal HttpWebResponse (Uri uri, string method, WebConnectionData data, Cookie } string content_encoding = webHeaders ["Content-Encoding"]; - if (content_encoding == "gzip" && (data.request.AutomaticDecompression & DecompressionMethods.GZip) != 0) { - stream = new GZipStream (stream, CompressionMode.Decompress); + if (content_encoding == "gzip" && (stream.Request.AutomaticDecompression & DecompressionMethods.GZip) != 0) { + this.stream = new GZipStream (stream, CompressionMode.Decompress); webHeaders.Remove (HttpRequestHeader.ContentEncoding); } - else if (content_encoding == "deflate" && (data.request.AutomaticDecompression & DecompressionMethods.Deflate) != 0) { - stream = new DeflateStream (stream, CompressionMode.Decompress); + else if (content_encoding == "deflate" && (stream.Request.AutomaticDecompression & DecompressionMethods.Deflate) != 0) { + this.stream = new DeflateStream (stream, CompressionMode.Decompress); webHeaders.Remove (HttpRequestHeader.ContentEncoding); } } @@ -263,17 +277,6 @@ public string GetResponseHeader (string headerName) return (value != null) ? value : ""; } - internal void ReadAll () - { - WebConnectionStream wce = stream as WebConnectionStream; - if (wce == null) - return; - - try { - wce.ReadAll (); - } catch {} - } - public override Stream GetResponseStream () { CheckDisposed (); @@ -310,12 +313,9 @@ protected override void GetObjectData (SerializationInfo serializationInfo, public override void Close () { - if (stream != null) { - Stream st = stream; - stream = null; - if (st != null) - st.Close (); - } + var st = Interlocked.Exchange (ref stream, null); + if (st != null) + st.Close (); } void IDisposable.Dispose () diff --git a/mcs/class/System/System.Net/ServicePoint.cs b/mcs/class/System/System.Net/ServicePoint.cs index 9b6cc5cc5e4d..f8eee86caca9 100644 --- a/mcs/class/System/System.Net/ServicePoint.cs +++ b/mcs/class/System/System.Net/ServicePoint.cs @@ -38,20 +38,15 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; -namespace System.Net +namespace System.Net { public class ServicePoint { readonly Uri uri; - int connectionLimit; - int maxIdleTime; - int currentConnections; - DateTime idleSince; DateTime lastDnsResolve; Version protocolVersion; IPHostEntry host; bool usesProxy; - Dictionary groups; bool sendContinue = true; bool useConnect; object hostE = new object (); @@ -60,21 +55,22 @@ public class ServicePoint bool tcp_keepalive; int tcp_keepalive_time; int tcp_keepalive_interval; - Timer idleTimer; // Constructors internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime) { - this.uri = uri; - this.connectionLimit = connectionLimit; - this.maxIdleTime = maxIdleTime; - this.currentConnections = 0; - this.idleSince = DateTime.UtcNow; + this.uri = uri; + + Scheduler = new ServicePointScheduler (this, connectionLimit, maxIdleTime); + } + + internal ServicePointScheduler Scheduler { + get; } - + // Properties - + public Uri Address { get { return uri; } } @@ -84,15 +80,13 @@ static Exception GetMustImplement () return new NotImplementedException (); } - public BindIPEndPoint BindIPEndPointDelegate - { + public BindIPEndPoint BindIPEndPointDelegate { get { return endPointCallback; } set { endPointCallback = value; } } - + [MonoTODO] - public int ConnectionLeaseTimeout - { + public int ConnectionLeaseTimeout { get { throw GetMustImplement (); } @@ -100,54 +94,39 @@ public int ConnectionLeaseTimeout throw GetMustImplement (); } } - - public int ConnectionLimit { - get { return connectionLimit; } - set { - if (value <= 0) - throw new ArgumentOutOfRangeException (); - connectionLimit = value; - } + public int ConnectionLimit { + get { return Scheduler.ConnectionLimit; } + set { Scheduler.ConnectionLimit = value; } } - + public string ConnectionName { get { return uri.Scheme; } } public int CurrentConnections { get { - return currentConnections; + return Scheduler.CurrentConnections; } } public DateTime IdleSince { get { - return idleSince.ToLocalTime (); + return Scheduler.IdleSince.ToLocalTime (); } } public int MaxIdleTime { - get { return maxIdleTime; } - set { - if (value < Timeout.Infinite || value > Int32.MaxValue) - throw new ArgumentOutOfRangeException (); - - lock (this) { - maxIdleTime = value; - if (idleTimer != null) - idleTimer.Change (maxIdleTime, maxIdleTime); - } - } + get { return Scheduler.MaxIdleTime; } + set { Scheduler.MaxIdleTime = value; } } - + public virtual Version ProtocolVersion { get { return protocolVersion; } } [MonoTODO] - public int ReceiveBufferSize - { + public int ReceiveBufferSize { get { throw GetMustImplement (); } @@ -155,7 +134,7 @@ public int ReceiveBufferSize throw GetMustImplement (); } } - + public bool SupportsPipelining { get { return HttpVersion.Version11.Equals (protocolVersion); } } @@ -172,8 +151,10 @@ public bool UseNagleAlgorithm { } internal bool SendContinue { - get { return sendContinue && - (protocolVersion == null || protocolVersion == HttpVersion.Version11); } + get { + return sendContinue && + (protocolVersion == null || protocolVersion == HttpVersion.Version11); + } set { sendContinue = value; } } // Methods @@ -197,25 +178,25 @@ internal void KeepAliveSetup (Socket socket) if (!tcp_keepalive) return; - byte [] bytes = new byte [12]; - PutBytes (bytes, (uint) (tcp_keepalive ? 1 : 0), 0); - PutBytes (bytes, (uint) tcp_keepalive_time, 4); - PutBytes (bytes, (uint) tcp_keepalive_interval, 8); + byte[] bytes = new byte[12]; + PutBytes (bytes, (uint)(tcp_keepalive ? 1 : 0), 0); + PutBytes (bytes, (uint)tcp_keepalive_time, 4); + PutBytes (bytes, (uint)tcp_keepalive_interval, 8); socket.IOControl (IOControlCode.KeepAliveValues, bytes, null); } - static void PutBytes (byte [] bytes, uint v, int offset) + static void PutBytes (byte[] bytes, uint v, int offset) { if (BitConverter.IsLittleEndian) { - bytes [offset] = (byte) (v & 0x000000ff); - bytes [offset + 1] = (byte) ((v & 0x0000ff00) >> 8); - bytes [offset + 2] = (byte) ((v & 0x00ff0000) >> 16); - bytes [offset + 3] = (byte) ((v & 0xff000000) >> 24); + bytes[offset] = (byte)(v & 0x000000ff); + bytes[offset + 1] = (byte)((v & 0x0000ff00) >> 8); + bytes[offset + 2] = (byte)((v & 0x00ff0000) >> 16); + bytes[offset + 3] = (byte)((v & 0xff000000) >> 24); } else { - bytes [offset + 3] = (byte) (v & 0x000000ff); - bytes [offset + 2] = (byte) ((v & 0x0000ff00) >> 8); - bytes [offset + 1] = (byte) ((v & 0x00ff0000) >> 16); - bytes [offset] = (byte) ((v & 0xff000000) >> 24); + bytes[offset + 3] = (byte)(v & 0x000000ff); + bytes[offset + 2] = (byte)((v & 0x0000ff00) >> 8); + bytes[offset + 1] = (byte)((v & 0x00ff0000) >> 16); + bytes[offset] = (byte)((v & 0xff000000) >> 24); } } @@ -231,107 +212,7 @@ internal bool UseConnect { set { useConnect = value; } } - WebConnectionGroup GetConnectionGroup (string name) - { - if (name == null) - name = ""; - - /* - * Optimization: - * - * In the vast majority of cases, we only have one single WebConnectionGroup per ServicePoint, so we - * don't need to allocate a dictionary. - * - */ - - WebConnectionGroup group; - if (groups != null && groups.TryGetValue (name, out group)) - return group; - - group = new WebConnectionGroup (this, name); - group.ConnectionClosed += (s, e) => currentConnections--; - - if (groups == null) - groups = new Dictionary (); - groups.Add (name, group); - - return group; - } - - void RemoveConnectionGroup (WebConnectionGroup group) - { - if (groups == null || groups.Count == 0) - throw new InvalidOperationException (); - - groups.Remove (group.Name); - } - - bool CheckAvailableForRecycling (out DateTime outIdleSince) - { - outIdleSince = DateTime.MinValue; - - TimeSpan idleTimeSpan; - List groupList = null, removeList = null; - lock (this) { - if (groups == null || groups.Count == 0) { - idleSince = DateTime.MinValue; - return true; - } - - idleTimeSpan = TimeSpan.FromMilliseconds (maxIdleTime); - - /* - * WebConnectionGroup.TryRecycle() must run outside the lock, so we need to - * copy the group dictionary if it exists. - * - * In most cases, we only have a single connection group, so we can simply store - * that in a local variable instead of copying a collection. - * - */ - - groupList = new List (groups.Values); - } - - foreach (var group in groupList) { - if (!group.TryRecycle (idleTimeSpan, ref outIdleSince)) - continue; - if (removeList == null) - removeList = new List (); - removeList.Add (group); - } - - lock (this) { - idleSince = outIdleSince; - - if (removeList != null && groups != null) { - foreach (var group in removeList) - if (groups.ContainsKey (group.Name)) - RemoveConnectionGroup (group); - } - - if (groups != null && groups.Count == 0) - groups = null; - - if (groups == null) { - if (idleTimer != null) { - idleTimer.Dispose (); - idleTimer = null; - } - return true; - } - - return false; - } - } - - void IdleTimerCallback (object obj) - { - DateTime dummy; - CheckAvailableForRecycling (out dummy); - } - - private bool HasTimedOut - { + private bool HasTimedOut { get { int timeout = ServicePointManager.DnsRefreshTimeout; return timeout != Timeout.Infinite && @@ -339,8 +220,7 @@ private bool HasTimedOut } } - internal IPHostEntry HostEntry - { + internal IPHostEntry HostEntry { get { lock (hostE) { string uriHost = uri.Host; @@ -356,7 +236,7 @@ internal IPHostEntry HostEntry } // Creates IPHostEntry - host = new IPHostEntry(); + host = new IPHostEntry (); host.AddressList = new IPAddress[] { IPAddress.Parse (uriHost) }; return host; } @@ -382,41 +262,18 @@ internal void SetVersion (Version version) protocolVersion = version; } - internal EventHandler SendRequest (HttpWebRequest request, string groupName) + internal void SendRequest (WebOperation operation, string groupName) { - WebConnection cnc; - lock (this) { - bool created; - WebConnectionGroup cncGroup = GetConnectionGroup (groupName); - cnc = cncGroup.GetConnection (request, out created); - if (created) { - ++currentConnections; - if (idleTimer == null) - idleTimer = new Timer (IdleTimerCallback, null, maxIdleTime, maxIdleTime); - } + Scheduler.SendRequest (operation, groupName); } - - return cnc.SendRequest (request); } + public bool CloseConnectionGroup (string connectionGroupName) { - WebConnectionGroup cncGroup = null; - lock (this) { - cncGroup = GetConnectionGroup (connectionGroupName); - if (cncGroup != null) { - RemoveConnectionGroup (cncGroup); - } + return Scheduler.CloseConnectionGroup (connectionGroupName); } - - // WebConnectionGroup.Close() must *not* be called inside the lock - if (cncGroup != null) { - cncGroup.Close (); - return true; - } - - return false; } // diff --git a/mcs/class/System/System.Net/ServicePointManager.cs b/mcs/class/System/System.Net/ServicePointManager.cs index 56d682bd4025..e211e2a7fb55 100644 --- a/mcs/class/System/System.Net/ServicePointManager.cs +++ b/mcs/class/System/System.Net/ServicePointManager.cs @@ -132,7 +132,7 @@ public override bool Equals (object obj) { // Fields public const int DefaultNonPersistentConnectionLimit = 4; -#if MOBILE +#if MOBILE && !UNITY_AOT public const int DefaultPersistentConnectionLimit = 10; #else public const int DefaultPersistentConnectionLimit = 2; diff --git a/mcs/class/System/System.Net/ServicePointManager.extra.cs b/mcs/class/System/System.Net/ServicePointManager.extra.cs index d971cdd84124..1d2502e11df5 100644 --- a/mcs/class/System/System.Net/ServicePointManager.extra.cs +++ b/mcs/class/System/System.Net/ServicePointManager.extra.cs @@ -7,7 +7,7 @@ // Copyright 2013-2014 Xamarin Inc. // -#if MOBILE +#if MOBILE && !UNITY using System; using System.Collections.Generic; diff --git a/mcs/class/System/System.Net/ServicePointScheduler.cs b/mcs/class/System/System.Net/ServicePointScheduler.cs new file mode 100644 index 000000000000..2e95656f759a --- /dev/null +++ b/mcs/class/System/System.Net/ServicePointScheduler.cs @@ -0,0 +1,621 @@ +// +// ServicePointScheduler.cs +// +// Author: +// Martin Baulig +// +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Diagnostics; + +namespace System.Net +{ + class ServicePointScheduler + { + public ServicePoint ServicePoint { + get; + } + + public int MaxIdleTime { + get { return maxIdleTime; } + set { + if (value < Timeout.Infinite || value > Int32.MaxValue) + throw new ArgumentOutOfRangeException (); + if (value == maxIdleTime) + return; + maxIdleTime = value; + Debug ($"MAX IDLE TIME = {value}"); + Run (); + } + } + + public int ConnectionLimit { + get { return connectionLimit; } + set { + if (value <= 0) + throw new ArgumentOutOfRangeException (); + + if (value == connectionLimit) + return; + connectionLimit = value; + Debug ($"CONNECTION LIMIT = {value}"); + Run (); + } + } + + public ServicePointScheduler (ServicePoint servicePoint, int connectionLimit, int maxIdleTime) + { + ServicePoint = servicePoint; + this.connectionLimit = connectionLimit; + this.maxIdleTime = maxIdleTime; + + schedulerEvent = new AsyncManualResetEvent (false); + defaultGroup = new ConnectionGroup (this, string.Empty); + operations = new LinkedList<(ConnectionGroup, WebOperation)> (); + idleConnections = new LinkedList<(ConnectionGroup, WebConnection, Task)> (); + idleSince = DateTime.UtcNow; + } + + [Conditional ("MONO_WEB_DEBUG")] + void Debug (string message, params object[] args) + { + WebConnection.Debug ($"SPS({ID}): {string.Format (message, args)}"); + } + + [Conditional ("MONO_WEB_DEBUG")] + void Debug (string message) + { + WebConnection.Debug ($"SPS({ID}): {message}"); + } + + int running; + int maxIdleTime = 100000; + AsyncManualResetEvent schedulerEvent; + ConnectionGroup defaultGroup; + Dictionary groups; + LinkedList<(ConnectionGroup, WebOperation)> operations; + LinkedList<(ConnectionGroup, WebConnection, Task)> idleConnections; + int currentConnections; + int connectionLimit; + DateTime idleSince; + + public int CurrentConnections { + get { + return currentConnections; + } + } + + public DateTime IdleSince { + get { + return idleSince; + } + } + + static int nextId; + public readonly int ID = ++nextId; + + internal string ME { + get; + } + + public void Run () + { + lock (ServicePoint) { + if (Interlocked.CompareExchange (ref running, 1, 0) == 0) + StartScheduler (); + + schedulerEvent.Set (); + } + } + + async void StartScheduler () + { + idleSince = DateTime.UtcNow + TimeSpan.FromDays (3650); + + while (true) { + Debug ($"MAIN LOOP"); + + // Gather list of currently running operations. + ValueTuple[] operationArray; + ValueTuple[] idleArray; + var taskList = new List (); + lock (ServicePoint) { + Cleanup (); + if (groups == null && defaultGroup.IsEmpty () && operations.Count == 0 && idleConnections.Count == 0) { + Debug ($"MAIN LOOP DONE"); + running = 0; + idleSince = DateTime.UtcNow; + schedulerEvent.Reset (); + return; + } + + operationArray = new ValueTuple[operations.Count]; + operations.CopyTo (operationArray, 0); + idleArray = new ValueTuple[idleConnections.Count]; + idleConnections.CopyTo (idleArray, 0); + + taskList.Add (schedulerEvent.WaitAsync (maxIdleTime)); + foreach (var item in operationArray) + taskList.Add (item.Item2.WaitForCompletion (true)); + foreach (var item in idleArray) + taskList.Add (item.Item3); + } + + Debug ($"MAIN LOOP #1: operations={operationArray.Length} idle={idleArray.Length}"); + + var ret = await Task.WhenAny (taskList).ConfigureAwait (false); + + lock (ServicePoint) { + if (ret == taskList[0]) { + RunSchedulerIteration (); + continue; + } + + int idx = -1; + for (int i = 0; i < operationArray.Length; i++) { + if (ret == taskList[i + 1]) { + idx = i; + break; + } + } + + if (idx >= 0) { + var item = operationArray[idx]; + Debug ($"MAIN LOOP #2: {idx} group={item.Item1.ID} Op={item.Item2.ID}"); + operations.Remove (item); + + var opTask = (Task>)ret; + var runLoop = OperationCompleted (item.Item1, item.Item2, opTask); + Debug ($"MAIN LOOP #2 DONE: {idx} {runLoop}"); + if (runLoop) + RunSchedulerIteration (); + continue; + } + + for (int i = 0; i < idleArray.Length; i++) { + if (ret == taskList[i + 1 + operationArray.Length]) { + idx = i; + break; + } + } + + if (idx >= 0) { + var item = idleArray[idx]; + Debug ($"MAIN LOOP #3: {idx} group={item.Item1.ID} Cnc={item.Item2.ID}"); + idleConnections.Remove (item); + CloseIdleConnection (item.Item1, item.Item2); + } + } + } + } + + void Cleanup () + { + if (groups != null) { + var keys = new string[groups.Count]; + groups.Keys.CopyTo (keys, 0); + foreach (var groupName in keys) { + if (!groups.ContainsKey (groupName)) + continue; + var group = groups[groupName]; + if (group.IsEmpty ()) { + Debug ($"CLEANUP - REMOVING group={group.ID}"); + groups.Remove (groupName); + } + } + if (groups.Count == 0) + groups = null; + } + } + + void RunSchedulerIteration () + { + schedulerEvent.Reset (); + + bool repeat; + do { + Debug ($"ITERATION"); + + repeat = SchedulerIteration (defaultGroup); + + Debug ($"ITERATION #1: {repeat} {groups != null}"); + + if (groups != null) { + foreach (var group in groups) { + Debug ($"ITERATION #2: group={group.Value.ID}"); + repeat |= SchedulerIteration (group.Value); + } + } + + Debug ($"ITERATION #3: {repeat}"); + } while (repeat); + } + + bool OperationCompleted (ConnectionGroup group, WebOperation operation, Task<(bool, WebOperation)> task) + { +#if MONO_WEB_DEBUG + var me = $"{nameof (OperationCompleted)}(group={group.ID}, Op={operation.ID}, Cnc={operation.Connection.ID})"; +#else + string me = null; +#endif + + var (ok, next) = task.Status == TaskStatus.RanToCompletion ? task.Result : (false, null); + Debug ($"{me}: {task.Status} {ok} {next?.ID}"); + + if (!ok || !operation.Connection.Continue (next)) { + group.RemoveConnection (operation.Connection); + if (next == null) { + Debug ($"{me}: closed connection and done."); + return true; + } + ok = false; + } + + if (next == null) { + if (ok) { + var idleTask = Task.Delay (MaxIdleTime); + idleConnections.AddLast ((group, operation.Connection, idleTask)); + Debug ($"{me} keeping connection open for {MaxIdleTime} milliseconds."); + } else { + Debug ($"{me}: closed connection and done."); + } + return true; + } + + Debug ($"{me} got new operation next={next.ID}."); + operations.AddLast ((group, next)); + + if (ok) { + Debug ($"{me} continuing next={next.ID} on same connection."); + RemoveIdleConnection (operation.Connection); + return false; + } + + group.Cleanup (); + + var (connection, created) = group.CreateOrReuseConnection (next, true); + Debug ($"{me} created new connection Cnc={connection.ID} next={next.ID}."); + return false; + } + + void CloseIdleConnection (ConnectionGroup group, WebConnection connection) + { + Debug ($"{nameof (CloseIdleConnection)}(group={group.ID}, Cnc={connection.ID}) closing idle connection."); + + group.RemoveConnection (connection); + RemoveIdleConnection (connection); + } + + bool SchedulerIteration (ConnectionGroup group) + { +#if MONO_WEB_DEBUG + var me = $"{nameof (SchedulerIteration)}(group={group.ID})"; +#else + string me = null; +#endif + Debug ($"{me}"); + + // First, let's clean up. + group.Cleanup (); + + // Is there anything in the queue? + var next = group.GetNextOperation (); + Debug ($"{me} no pending operations."); + if (next == null) + return false; + + Debug ($"{me} found pending operation Op={next.ID}"); + + var (connection, created) = group.CreateOrReuseConnection (next, false); + if (connection == null) { + // All connections are currently busy, need to keep it in the queue for now. + Debug ($"{me} all connections busy, keeping operation in queue."); + return false; + } + + Debug ($"{me} started operation: Op={next.ID} Cnc={connection.ID}"); + operations.AddLast ((group, next)); + RemoveIdleConnection (connection); + return true; + } + + void RemoveOperation (WebOperation operation) + { + var iter = operations.First; + while (iter != null) { + var node = iter; + iter = iter.Next; + + if (node.Value.Item2 == operation) + operations.Remove (node); + } + } + + void RemoveIdleConnection (WebConnection connection) + { + var iter = idleConnections.First; + while (iter != null) { + var node = iter; + iter = iter.Next; + + if (node.Value.Item2 == connection) + idleConnections.Remove (node); + } + } + + public void SendRequest (WebOperation operation, string groupName) + { + lock (ServicePoint) { + var group = GetConnectionGroup (groupName); + Debug ($"SEND REQUEST: Op={operation.ID} group={group.ID}"); + group.EnqueueOperation (operation); + Run (); + Debug ($"SEND REQUEST DONE: Op={operation.ID} group={group.ID}"); + } + } + + public bool CloseConnectionGroup (string groupName) + { + lock (ServicePoint) { + ConnectionGroup group; + if (string.IsNullOrEmpty (groupName)) + group = defaultGroup; + else if (groups == null || !groups.TryGetValue (groupName, out group)) + return false; + + Debug ($"CLOSE CONNECTION GROUP: group={group.ID}"); + + if (group != defaultGroup) { + groups.Remove (groupName); + if (groups.Count == 0) + groups = null; + } + + group.Close (); + Run (); + return true; + } + } + + ConnectionGroup GetConnectionGroup (string name) + { + lock (ServicePoint) { + if (string.IsNullOrEmpty (name)) + return defaultGroup; + + if (groups == null) + groups = new Dictionary (); + + if (groups.TryGetValue (name, out ConnectionGroup group)) + return group; + + group = new ConnectionGroup (this, name); + groups.Add (name, group); + return group; + } + } + + void OnConnectionCreated (WebConnection connection) + { + Interlocked.Increment (ref currentConnections); + } + + void OnConnectionClosed (WebConnection connection) + { + RemoveIdleConnection (connection); + Interlocked.Decrement (ref currentConnections); + } + + class ConnectionGroup + { + public ServicePointScheduler Scheduler { + get; + } + + public string Name { + get; + } + + public bool IsDefault => string.IsNullOrEmpty (Name); + + static int nextId; + public readonly int ID = ++nextId; + LinkedList connections; + LinkedList queue; + + public ConnectionGroup (ServicePointScheduler scheduler, string name) + { + Scheduler = scheduler; + Name = name; + + connections = new LinkedList (); + queue = new LinkedList (); + } + + public bool IsEmpty () + { + return connections.Count == 0 && queue.Count == 0; + } + + public void RemoveConnection (WebConnection connection) + { + Scheduler.Debug ($"REMOVING CONNECTION: group={ID} cnc={connection.ID}"); + connections.Remove (connection); + connection.Dispose (); + Scheduler.OnConnectionClosed (connection); + } + + public void Cleanup () + { + var iter = connections.First; + while (iter != null) { + var connection = iter.Value; + var node = iter; + iter = iter.Next; + + if (connection.Closed) { + Scheduler.Debug ($"REMOVING CONNECTION: group={ID} cnc={connection.ID}"); + connections.Remove (node); + Scheduler.OnConnectionClosed (connection); + } + } + } + + public void Close () + { + foreach (var operation in queue) { + operation.Abort (); + Scheduler.RemoveOperation (operation); + } + queue.Clear (); + + foreach (var connection in connections) { + connection.Dispose (); + Scheduler.OnConnectionClosed (connection); + } + connections.Clear (); + } + + public void EnqueueOperation (WebOperation operation) + { + queue.AddLast (operation); + } + + public WebOperation GetNextOperation () + { + // Is there anything in the queue? + var iter = queue.First; + while (iter != null) { + var operation = iter.Value; + var node = iter; + iter = iter.Next; + + if (operation.Aborted) { + queue.Remove (node); + Scheduler.RemoveOperation (operation); + continue; + } + + return operation; + } + + return null; + } + + public WebConnection FindIdleConnection (WebOperation operation) + { + // First let's find the ideal candidate. + WebConnection candidate = null; + foreach (var connection in connections) { + if (connection.CanReuseConnection (operation)) { + if (candidate == null || connection.IdleSince > candidate.IdleSince) + candidate = connection; + } + } + + // Found one? Make sure it's actually willing to run it. + if (candidate != null && candidate.StartOperation (operation, true)) { + queue.Remove (operation); + return candidate; + } + + // Ok, let's loop again and pick the first one that accepts the new operation. + foreach (var connection in connections) { + if (connection.StartOperation (operation, true)) { + queue.Remove (operation); + return connection; + } + } + + return null; + } + + public (WebConnection connection, bool created) CreateOrReuseConnection (WebOperation operation, bool force) + { + var connection = FindIdleConnection (operation); + if (connection != null) + return (connection, false); + + if (force || Scheduler.ServicePoint.ConnectionLimit > connections.Count || connections.Count == 0) { + connection = new WebConnection (Scheduler.ServicePoint); + connection.StartOperation (operation, false); + connections.AddFirst (connection); + Scheduler.OnConnectionCreated (connection); + queue.Remove (operation); + return (connection, true); + } + + return (null, false); + } + } + + // https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/ + class AsyncManualResetEvent + { + volatile TaskCompletionSource m_tcs = new TaskCompletionSource (); + + public Task WaitAsync () { return m_tcs.Task; } + + public bool WaitOne (int millisecondTimeout) + { + WebConnection.Debug ($"AMRE WAIT ONE: {millisecondTimeout}"); + return m_tcs.Task.Wait (millisecondTimeout); + } + + public async Task WaitAsync (int millisecondTimeout) + { + var timeoutTask = Task.Delay (millisecondTimeout); + var ret = await Task.WhenAny (m_tcs.Task, timeoutTask).ConfigureAwait (false); + return ret != timeoutTask; + } + + public void Set () + { + var tcs = m_tcs; + Task.Factory.StartNew (s => ((TaskCompletionSource)s).TrySetResult (true), + tcs, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default); + tcs.Task.Wait (); + } + + public void Reset () + { + while (true) { + var tcs = m_tcs; + if (!tcs.Task.IsCompleted || + Interlocked.CompareExchange (ref m_tcs, new TaskCompletionSource (), tcs) == tcs) + return; + } + } + + public AsyncManualResetEvent (bool state) + { + if (state) + Set (); + } + } + } +} diff --git a/mcs/class/System/System.Net/SimpleAsyncResult.cs b/mcs/class/System/System.Net/SimpleAsyncResult.cs deleted file mode 100644 index 6a2107c97735..000000000000 --- a/mcs/class/System/System.Net/SimpleAsyncResult.cs +++ /dev/null @@ -1,232 +0,0 @@ -// -// SimpleAsyncResult.cs -// -// Authors: -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// Martin Baulig (martin.baulig@xamarin.com) -// -// (C) 2003 Ximian, Inc (http://www.ximian.com) -// Copyright (c) 2014 Xamarin Inc. (http://www.xamarin.com) -// -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; -using System.Threading; - -namespace System.Net -{ - delegate void SimpleAsyncCallback (SimpleAsyncResult result); - - class SimpleAsyncResult : IAsyncResult - { - ManualResetEvent handle; - bool synch; - bool isCompleted; - readonly SimpleAsyncCallback cb; - object state; - bool callbackDone; - Exception exc; - object locker = new object (); - - SimpleAsyncResult (SimpleAsyncCallback cb) - { - this.cb = cb; - } - - protected SimpleAsyncResult (AsyncCallback cb, object state) - { - this.state = state; - this.cb = result => { - if (cb != null) - cb (this); - }; - } - - public static void Run (Func func, SimpleAsyncCallback callback) - { - var result = new SimpleAsyncResult (callback); - try { - if (!func (result)) - result.SetCompleted (true); - } catch (Exception ex) { - result.SetCompleted (true, ex); - } - } - - public static void RunWithLock (object locker, Func func, SimpleAsyncCallback callback) - { - Run (inner => { - bool running = func (inner); - if (running) - Monitor.Exit (locker); - return running; - }, inner => { - if (inner.GotException) { - if (inner.synch) - Monitor.Exit (locker); - callback (inner); - return; - } - - try { - if (!inner.synch) - Monitor.Enter (locker); - - callback (inner); - } finally { - Monitor.Exit (locker); - } - }); - } - - protected void Reset_internal () - { - callbackDone = false; - exc = null; - lock (locker) { - isCompleted = false; - if (handle != null) - handle.Reset (); - } - } - - internal void SetCompleted (bool synch, Exception e) - { - SetCompleted_internal (synch, e); - DoCallback_private (); - } - - internal void SetCompleted (bool synch) - { - SetCompleted_internal (synch); - DoCallback_private (); - } - - void SetCompleted_internal (bool synch, Exception e) - { - this.synch = synch; - exc = e; - lock (locker) { - isCompleted = true; - if (handle != null) - handle.Set (); - } - } - - protected void SetCompleted_internal (bool synch) - { - SetCompleted_internal (synch, null); - } - - void DoCallback_private () - { - if (callbackDone) - return; - callbackDone = true; - if (cb == null) - return; - cb (this); - } - - protected void DoCallback_internal () - { - if (!callbackDone && cb != null) { - callbackDone = true; - cb (this); - } - } - - internal void WaitUntilComplete () - { - if (IsCompleted) - return; - - AsyncWaitHandle.WaitOne (); - } - - internal bool WaitUntilComplete (int timeout, bool exitContext) - { - if (IsCompleted) - return true; - - return AsyncWaitHandle.WaitOne (timeout, exitContext); - } - - public object AsyncState { - get { return state; } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (locker) { - if (handle == null) - handle = new ManualResetEvent (isCompleted); - } - - return handle; - } - } - - bool? user_read_synch; - - public bool CompletedSynchronously { - get { - // - // CompletedSynchronously (for System.Net networking stack) means "was the operation completed before the first time - // that somebody asked if it was completed synchronously"? They do this because some of their asynchronous operations - // (particularly those in the Socket class) will avoid the cost of capturing and transferring the ExecutionContext - // to the callback thread by checking CompletedSynchronously, and calling the callback from within BeginXxx instead of - // on the completion port thread if the native winsock call completes quickly. - // - // TODO: racy - if (user_read_synch != null) - return user_read_synch.Value; - - user_read_synch = synch; - return user_read_synch.Value; - } - } - - internal bool CompletedSynchronouslyPeek { - get { - return synch; - } - } - - public bool IsCompleted { - get { - lock (locker) { - return isCompleted; - } - } - } - - internal bool GotException { - get { return (exc != null); } - } - - internal Exception Exception { - get { return exc; } - } - } -} - diff --git a/mcs/class/System/System.Net/WebAsyncResult.cs b/mcs/class/System/System.Net/WebAsyncResult.cs deleted file mode 100644 index 52fc1a5967e9..000000000000 --- a/mcs/class/System/System.Net/WebAsyncResult.cs +++ /dev/null @@ -1,132 +0,0 @@ -// -// System.Net.WebAsyncResult -// -// Authors: -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// -// (C) 2003 Ximian, Inc (http://www.ximian.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; -using System.Threading; - -namespace System.Net -{ - class WebAsyncResult : SimpleAsyncResult - { - int nbytes; - IAsyncResult innerAsyncResult; - HttpWebResponse response; - Stream writeStream; - byte [] buffer; - int offset; - int size; - public bool EndCalled; - public bool AsyncWriteAll; - public HttpWebRequest AsyncObject; - - public WebAsyncResult (AsyncCallback cb, object state) - : base (cb, state) - { - } - - public WebAsyncResult (HttpWebRequest request, AsyncCallback cb, object state) - : base (cb, state) - { - this.AsyncObject = request; - } - - public WebAsyncResult (AsyncCallback cb, object state, byte [] buffer, int offset, int size) - : base (cb, state) - { - this.buffer = buffer; - this.offset = offset; - this.size = size; - } - - internal void Reset () - { - this.nbytes = 0; - this.response = null; - this.buffer = null; - this.offset = 0; - this.size = 0; - Reset_internal (); - } - - internal void SetCompleted (bool synch, int nbytes) - { - this.nbytes = nbytes; - SetCompleted_internal (synch); - } - - internal void SetCompleted (bool synch, Stream writeStream) - { - this.writeStream = writeStream; - SetCompleted_internal (synch); - } - - internal void SetCompleted (bool synch, HttpWebResponse response) - { - this.response = response; - SetCompleted_internal (synch); - } - - internal void DoCallback () - { - DoCallback_internal (); - } - - internal int NBytes { - get { return nbytes; } - set { nbytes = value; } - } - - internal IAsyncResult InnerAsyncResult { - get { return innerAsyncResult; } - set { innerAsyncResult = value; } - } - - internal Stream WriteStream { - get { return writeStream; } - } - - internal HttpWebResponse Response { - get { return response; } - } - - internal byte [] Buffer { - get { return buffer; } - } - - internal int Offset { - get { return offset; } - } - - internal int Size { - get { return size; } - } - } -} - diff --git a/mcs/class/System/System.Net/WebCompletionSource.cs b/mcs/class/System/System.Net/WebCompletionSource.cs new file mode 100644 index 000000000000..f90dbf50e8da --- /dev/null +++ b/mcs/class/System/System.Net/WebCompletionSource.cs @@ -0,0 +1,103 @@ +// +// WebCompletionSource.cs +// +// Author: +// Martin Baulig +// +// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; + +namespace System.Net +{ + class WebCompletionSource + { + TaskCompletionSource completion; + + public WebCompletionSource () + { + completion = new TaskCompletionSource (); + } + + public bool TrySetCompleted () + { + return completion.TrySetResult (new Result (State.Completed, null)); + } + + public bool TrySetCanceled () + { + var error = new OperationCanceledException (); + var result = new Result (State.Canceled, ExceptionDispatchInfo.Capture (error)); + return completion.TrySetResult (result); + } + + public bool TrySetException (Exception error) + { + var result = new Result (State.Faulted, ExceptionDispatchInfo.Capture (error)); + return completion.TrySetResult (result); + } + + public bool IsCompleted => completion.Task.IsCompleted; + + public void ThrowOnError () + { + if (!completion.Task.IsCompleted) + return; + completion.Task.Result.Error?.Throw (); + } + + public async Task WaitForCompletion (bool throwOnError) + { + var result = await completion.Task.ConfigureAwait (false); + if (result.State == State.Completed) + return true; + if (throwOnError) + result.Error.Throw (); + return false; + } + + enum State : int { + Running, + Completed, + Canceled, + Faulted + } + + class Result + { + public State State { + get; + } + + public ExceptionDispatchInfo Error { + get; + } + + public Result (State state, ExceptionDispatchInfo error) + { + State = state; + Error = error; + } + } + } +} diff --git a/mcs/class/System/System.Net/WebConnection.cs b/mcs/class/System/System.Net/WebConnection.cs index 68c480fa86eb..c3cede54112a 100644 --- a/mcs/class/System/System.Net/WebConnection.cs +++ b/mcs/class/System/System.Net/WebConnection.cs @@ -3,10 +3,11 @@ // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Martin Baulig // // (C) 2003 Ximian, Inc (http://www.ximian.com) +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) // - // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -27,13 +28,14 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // - using System.IO; using System.Collections; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; using System.Diagnostics; using Mono.Net.Security; @@ -48,72 +50,59 @@ enum ReadState Aborted } - class WebConnection + class WebConnection : IDisposable { - ServicePoint sPoint; - Stream nstream; - internal Socket socket; - object socketLock = new object (); - IWebConnectionState state; - WebExceptionStatus status; - bool keepAlive; - byte [] buffer; - EventHandler abortHandler; - AbortHelper abortHelper; - internal WebConnectionData Data; - bool chunkedRead; - MonoChunkStream chunkStream; - Queue queue; - bool reused; - int position; - HttpWebRequest priority_request; NetworkCredential ntlm_credentials; bool ntlm_authenticated; bool unsafe_sharing; + Stream networkStream; + Socket socket; + MonoTlsStream monoTlsStream; + WebConnectionTunnel tunnel; + int disposed; - enum NtlmAuthState - { - None, - Challenge, - Response + public ServicePoint ServicePoint { + get; } - NtlmAuthState connect_ntlm_auth_state; - HttpWebRequest connect_request; - - Exception connect_exception; - MonoTlsStream tlsStream; #if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH [System.Runtime.InteropServices.DllImport ("__Internal")] static extern void xamarin_start_wwan (string uri); #endif - internal MonoChunkStream MonoChunkStream { - get { return chunkStream; } + public WebConnection (ServicePoint sPoint) + { + ServicePoint = sPoint; + } + +#if MONO_WEB_DEBUG + internal static bool EnableWebDebug { + get; set; } - public WebConnection (IWebConnectionState wcs, ServicePoint sPoint) + static WebConnection () { - this.state = wcs; - this.sPoint = sPoint; - buffer = new byte [4096]; - Data = new WebConnectionData (); - queue = wcs.Group.Queue; - abortHelper = new AbortHelper (); - abortHelper.Connection = this; - abortHandler = new EventHandler (abortHelper.Abort); + if (Environment.GetEnvironmentVariable ("MONO_WEB_DEBUG") != null) + EnableWebDebug = true; } +#endif - class AbortHelper { - public WebConnection Connection; + [Conditional ("MONO_WEB_DEBUG")] + internal static void Debug (string message, params object[] args) + { +#if MONO_WEB_DEBUG + if (EnableWebDebug) + Console.Error.WriteLine (string.Format (message, args)); +#endif + } - public void Abort (object sender, EventArgs args) - { - WebConnection other = ((HttpWebRequest) sender).WebConnection; - if (other == null) - other = Connection; - other.Abort (sender, args); - } + [Conditional ("MONO_WEB_DEBUG")] + internal static void Debug (string message) + { +#if MONO_WEB_DEBUG + if (EnableWebDebug) + Console.Error.WriteLine (message); +#endif } bool CanReuse () @@ -122,714 +111,192 @@ bool CanReuse () // but if there's data pending to read (!) we won't reuse the socket. return (socket.Poll (0, SelectMode.SelectRead) == false); } - - void Connect (HttpWebRequest request) + + bool CheckReusable () { - lock (socketLock) { - if (socket != null && socket.Connected && status == WebExceptionStatus.Success) { - // Take the chunked stream to the expected state (State.None) - if (CanReuse () && CompleteChunkedRead ()) { - reused = true; - return; - } - } + if (socket != null && socket.Connected) { + try { + if (CanReuse ()) + return true; + } catch { } + } - reused = false; - if (socket != null) { - socket.Close(); - socket = null; - } + return false; + } - chunkStream = null; - IPHostEntry hostEntry = sPoint.HostEntry; + async Task Connect (WebOperation operation, CancellationToken cancellationToken) + { + IPHostEntry hostEntry = ServicePoint.HostEntry; - if (hostEntry == null) { + if (hostEntry == null || hostEntry.AddressList.Length == 0) { #if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH - xamarin_start_wwan (sPoint.Address.ToString ()); - hostEntry = sPoint.HostEntry; + xamarin_start_wwan (ServicePoint.Address.ToString ()); + hostEntry = ServicePoint.HostEntry; if (hostEntry == null) { #endif - status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure : - WebExceptionStatus.NameResolutionFailure; - return; + throw GetException (ServicePoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure : + WebExceptionStatus.NameResolutionFailure, null); #if MONOTOUCH && !MONOTOUCH_TV && !MONOTOUCH_WATCH } #endif - } - - //WebConnectionData data = Data; - foreach (IPAddress address in hostEntry.AddressList) { - try { - socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - } catch (Exception se) { - // The Socket ctor can throw if we run out of FD's - if (!request.Aborted) - status = WebExceptionStatus.ConnectFailure; - connect_exception = se; - return; - } - IPEndPoint remote = new IPEndPoint (address, sPoint.Address.Port); - socket.NoDelay = !sPoint.UseNagleAlgorithm; - try { - sPoint.KeepAliveSetup (socket); - } catch { - // Ignore. Not supported in all platforms. - } - - if (!sPoint.CallEndPointDelegate (socket, remote)) { - socket.Close (); - socket = null; - status = WebExceptionStatus.ConnectFailure; - } else { - try { - if (request.Aborted) - return; - socket.Connect (remote); - status = WebExceptionStatus.Success; - break; - } catch (ThreadAbortException) { - // program exiting... - Socket s = socket; - socket = null; - if (s != null) - s.Close (); - return; - } catch (ObjectDisposedException) { - // socket closed from another thread - return; - } catch (Exception exc) { - Socket s = socket; - socket = null; - if (s != null) - s.Close (); - if (!request.Aborted) - status = WebExceptionStatus.ConnectFailure; - connect_exception = exc; - } - } - } - } - } - - bool CreateTunnel (HttpWebRequest request, Uri connectUri, - Stream stream, out byte[] buffer) - { - StringBuilder sb = new StringBuilder (); - sb.Append ("CONNECT "); - sb.Append (request.Address.Host); - sb.Append (':'); - sb.Append (request.Address.Port); - sb.Append (" HTTP/"); - if (request.ServicePoint.ProtocolVersion == HttpVersion.Version11) - sb.Append ("1.1"); - else - sb.Append ("1.0"); - - sb.Append ("\r\nHost: "); - sb.Append (request.Address.Authority); - - bool ntlm = false; - var challenge = Data.Challenge; - Data.Challenge = null; - var auth_header = request.Headers ["Proxy-Authorization"]; - bool have_auth = auth_header != null; - if (have_auth) { - sb.Append ("\r\nProxy-Authorization: "); - sb.Append (auth_header); - ntlm = auth_header.ToUpper ().Contains ("NTLM"); - } else if (challenge != null && Data.StatusCode == 407) { - ICredentials creds = request.Proxy.Credentials; - have_auth = true; - - if (connect_request == null) { - // create a CONNECT request to use with Authenticate - connect_request = (HttpWebRequest)WebRequest.Create ( - connectUri.Scheme + "://" + connectUri.Host + ":" + connectUri.Port + "/"); - connect_request.Method = "CONNECT"; - connect_request.Credentials = creds; - } - - if (creds != null) { - for (int i = 0; i < challenge.Length; i++) { - var auth = AuthenticationManager.Authenticate (challenge [i], connect_request, creds); - if (auth == null) - continue; - ntlm = (auth.ModuleAuthenticationType == "NTLM"); - sb.Append ("\r\nProxy-Authorization: "); - sb.Append (auth.Message); - break; - } - } - } - - if (ntlm) { - sb.Append ("\r\nProxy-Connection: keep-alive"); - connect_ntlm_auth_state++; - } - - sb.Append ("\r\n\r\n"); - - Data.StatusCode = 0; - byte [] connectBytes = Encoding.Default.GetBytes (sb.ToString ()); - stream.Write (connectBytes, 0, connectBytes.Length); - - int status; - WebHeaderCollection result = ReadHeaders (stream, out buffer, out status); - if ((!have_auth || connect_ntlm_auth_state == NtlmAuthState.Challenge) && - result != null && status == 407) { // Needs proxy auth - var connectionHeader = result ["Connection"]; - if (socket != null && !string.IsNullOrEmpty (connectionHeader) && - connectionHeader.ToLower() == "close") { - // The server is requesting that this connection be closed - socket.Close(); - socket = null; - } - - Data.StatusCode = status; - Data.Challenge = result.GetValues ("Proxy-Authenticate"); - Data.Headers = result; - return false; - } - - if (status != 200) { - Data.StatusCode = status; - Data.Headers = result; - return false; - } - - return (result != null); - } - - WebHeaderCollection ReadHeaders (Stream stream, out byte [] retBuffer, out int status) - { - retBuffer = null; - status = 200; - - byte [] buffer = new byte [1024]; - MemoryStream ms = new MemoryStream (); - - while (true) { - int n = stream.Read (buffer, 0, 1024); - if (n == 0) { - HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders"); - return null; - } - - ms.Write (buffer, 0, n); - int start = 0; - string str = null; - bool gotStatus = false; - WebHeaderCollection headers = new WebHeaderCollection (); - while (ReadLine (ms.GetBuffer (), ref start, (int) ms.Length, ref str)) { - if (str == null) { - int contentLen = 0; - try { - contentLen = int.Parse(headers["Content-Length"]); - } - catch { - contentLen = 0; - } - - if (ms.Length - start - contentLen > 0) { - // we've read more data than the response header and conents, - // give back extra data to the caller - retBuffer = new byte[ms.Length - start - contentLen]; - Buffer.BlockCopy(ms.GetBuffer(), start + contentLen, retBuffer, 0, retBuffer.Length); - } - else { - // haven't read in some or all of the contents for the response, do so now - FlushContents(stream, contentLen - (int)(ms.Length - start)); - } - - return headers; - } - - if (gotStatus) { - headers.Add (str); - continue; - } - - string[] parts = str.Split (' '); - if (parts.Length < 2) { - HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2"); - return null; - } - - if (String.Compare (parts [0], "HTTP/1.1", true) == 0) - Data.ProxyVersion = HttpVersion.Version11; - else if (String.Compare (parts [0], "HTTP/1.0", true) == 0) - Data.ProxyVersion = HttpVersion.Version10; - else { - HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2"); - return null; - } - - status = (int)UInt32.Parse (parts [1]); - if (parts.Length >= 3) - Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2); - - gotStatus = true; - } - } - } - - void FlushContents(Stream stream, int contentLength) - { - while (contentLength > 0) { - byte[] contentBuffer = new byte[contentLength]; - int bytesRead = stream.Read(contentBuffer, 0, contentLength); - if (bytesRead > 0) { - contentLength -= bytesRead; - } - else { - break; - } } - } - bool CreateStream (HttpWebRequest request) - { - try { - NetworkStream serverStream = new NetworkStream (socket, false); - - if (request.Address.Scheme == Uri.UriSchemeHttps) { -#if SECURITY_DEP - if (!reused || nstream == null || tlsStream == null) { - byte [] buffer = null; - if (sPoint.UseConnect) { - bool ok = CreateTunnel (request, sPoint.Address, serverStream, out buffer); - if (!ok) - return false; - } - tlsStream = new MonoTlsStream (request, serverStream); - nstream = tlsStream.CreateStream (buffer); - } - // we also need to set ServicePoint.Certificate - // and ServicePoint.ClientCertificate but this can - // only be done later (after handshake - which is - // done only after a read operation). -#else - throw new NotSupportedException (); -#endif - } else { - nstream = serverStream; - } - } catch (Exception ex) { - if (tlsStream != null) - status = tlsStream.ExceptionStatus; - else if (!request.Aborted) - status = WebExceptionStatus.ConnectFailure; - connect_exception = ex; - return false; - } + Exception connectException = null; - return true; - } - - void HandleError (WebExceptionStatus st, Exception e, string where) - { - status = st; - lock (this) { - if (st == WebExceptionStatus.RequestCanceled) - Data = new WebConnectionData (); - } + foreach (IPAddress address in hostEntry.AddressList) { + operation.ThrowIfDisposed (cancellationToken); - if (e == null) { // At least we now where it comes from try { - throw new Exception (new System.Diagnostics.StackTrace ().ToString ()); - } catch (Exception e2) { - e = e2; + socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + } catch (Exception se) { + // The Socket ctor can throw if we run out of FD's + throw GetException (WebExceptionStatus.ConnectFailure, se); } - } - - HttpWebRequest req = null; - if (Data != null && Data.request != null) - req = Data.request; - - Close (true); - if (req != null) { - req.FinishedReading = true; - req.SetResponseError (st, e, where); - } - } - - void ReadDone (IAsyncResult result) - { - WebConnectionData data = Data; - Stream ns = nstream; - if (ns == null) { - Close (true); - return; - } - - int nread = -1; - try { - nread = ns.EndRead (result); - } catch (ObjectDisposedException) { - return; - } catch (Exception e) { - if (e.InnerException is ObjectDisposedException) - return; - - HandleError (WebExceptionStatus.ReceiveFailure, e, "ReadDone1"); - return; - } - - if (nread == 0) { - HandleError (WebExceptionStatus.ReceiveFailure, null, "ReadDone2"); - return; - } - - if (nread < 0) { - HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadDone3"); - return; - } - - int pos = -1; - nread += position; - if (data.ReadState == ReadState.None) { - Exception exc = null; + IPEndPoint remote = new IPEndPoint (address, ServicePoint.Address.Port); + socket.NoDelay = !ServicePoint.UseNagleAlgorithm; try { - pos = GetResponse (data, sPoint, buffer, nread); - } catch (Exception e) { - exc = e; + ServicePoint.KeepAliveSetup (socket); + } catch { + // Ignore. Not supported in all platforms. } - if (exc != null || pos == -1) { - HandleError (WebExceptionStatus.ServerProtocolViolation, exc, "ReadDone4"); - return; + if (!ServicePoint.CallEndPointDelegate (socket, remote)) { + Interlocked.Exchange (ref socket, null)?.Close (); + continue; + } else { + try { + operation.ThrowIfDisposed (cancellationToken); + await socket.ConnectAsync (remote).ConfigureAwait (false); + } catch (ObjectDisposedException) { + throw; + } catch (Exception exc) { + Interlocked.Exchange (ref socket, null)?.Close (); + // Something went wrong, but we might have multiple IP Addresses + // and need to probe them all. + connectException = GetException (WebExceptionStatus.ConnectFailure, exc); + continue; + } } - } - - if (data.ReadState == ReadState.Aborted) { - HandleError (WebExceptionStatus.RequestCanceled, null, "ReadDone"); - return; - } - - if (data.ReadState != ReadState.Content) { - int est = nread * 2; - int max = (est < buffer.Length) ? buffer.Length : est; - byte [] newBuffer = new byte [max]; - Buffer.BlockCopy (buffer, 0, newBuffer, 0, nread); - buffer = newBuffer; - position = nread; - data.ReadState = ReadState.None; - InitRead (); - return; - } - - position = 0; - WebConnectionStream stream = new WebConnectionStream (this, data); - bool expect_content = ExpectContent (data.StatusCode, data.request.Method); - string tencoding = null; - if (expect_content) - tencoding = data.Headers ["Transfer-Encoding"]; - - chunkedRead = (tencoding != null && tencoding.IndexOf ("chunked", StringComparison.OrdinalIgnoreCase) != -1); - if (!chunkedRead) { - stream.ReadBuffer = buffer; - stream.ReadBufferOffset = pos; - stream.ReadBufferSize = nread; - try { - stream.CheckResponseInBuffer (); - } catch (Exception e) { - HandleError (WebExceptionStatus.ReceiveFailure, e, "ReadDone7"); - } - } else if (chunkStream == null) { - try { - chunkStream = new MonoChunkStream (buffer, pos, nread, data.Headers); - } catch (Exception e) { - HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone5"); - return; - } - } else { - chunkStream.ResetBuffer (); - try { - chunkStream.Write (buffer, pos, nread); - } catch (Exception e) { - HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone6"); + if (socket != null) return; - } } - data.stream = stream; - - if (!expect_content) - stream.ForceCompletion (); + if (connectException == null) + connectException = GetException (WebExceptionStatus.ConnectFailure, null); - data.request.SetResponseData (data); + throw connectException; } - static bool ExpectContent (int statusCode, string method) - { - if (method == "HEAD") - return false; - return (statusCode >= 200 && statusCode != 204 && statusCode != 304); - } +#if MONO_WEB_DEBUG + static int nextID, nextRequestID; + readonly int id = ++nextID; + public int ID => disposed != 0 ? -id : id; +#else + internal readonly int ID; +#endif - internal void InitRead () + async Task CreateStream (WebOperation operation, bool reused, CancellationToken cancellationToken) { - Stream ns = nstream; +#if MONO_WEB_DEBUG + var requestID = ++nextRequestID; +#else + var requestID = 0; +#endif try { - int size = buffer.Length - position; - ns.BeginRead (buffer, position, size, ReadDone, null); - } catch (Exception e) { - HandleError (WebExceptionStatus.ReceiveFailure, e, "InitRead"); - } - } - - static int GetResponse (WebConnectionData data, ServicePoint sPoint, - byte [] buffer, int max) - { - int pos = 0; - string line = null; - bool lineok = false; - bool isContinue = false; - bool emptyFirstLine = false; - do { - if (data.ReadState == ReadState.Aborted) - return -1; - - if (data.ReadState == ReadState.None) { - lineok = ReadLine (buffer, ref pos, max, ref line); - if (!lineok) - return 0; - - if (line == null) { - emptyFirstLine = true; - continue; - } - emptyFirstLine = false; - data.ReadState = ReadState.Status; - - string [] parts = line.Split (' '); - if (parts.Length < 2) - return -1; - - if (String.Compare (parts [0], "HTTP/1.1", true) == 0) { - data.Version = HttpVersion.Version11; - sPoint.SetVersion (HttpVersion.Version11); - } else { - data.Version = HttpVersion.Version10; - sPoint.SetVersion (HttpVersion.Version10); - } - - data.StatusCode = (int) UInt32.Parse (parts [1]); - if (parts.Length >= 3) - data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2); - else - data.StatusDescription = ""; - - if (pos >= max) - return pos; - } - - emptyFirstLine = false; - if (data.ReadState == ReadState.Status) { - data.ReadState = ReadState.Headers; - data.Headers = new WebHeaderCollection (); - ArrayList headers = new ArrayList (); - bool finished = false; - while (!finished) { - if (ReadLine (buffer, ref pos, max, ref line) == false) - break; - - if (line == null) { - // Empty line: end of headers - finished = true; - continue; - } - - if (line.Length > 0 && (line [0] == ' ' || line [0] == '\t')) { - int count = headers.Count - 1; - if (count < 0) - break; - - string prev = (string) headers [count] + line; - headers [count] = prev; - } else { - headers.Add (line); - } - } - - if (!finished) - return 0; - - // .NET uses ParseHeaders or ParseHeadersStrict which is much better - foreach (string s in headers) { + var stream = new NetworkStream (socket, false); - int pos_s = s.IndexOf (':'); - if (pos_s == -1) - throw new ArgumentException ("no colon found", "header"); + Debug ($"WC CREATE STREAM: Cnc={ID} {requestID} {reused} socket={socket.ID}"); - var header = s.Substring (0, pos_s); - var value = s.Substring (pos_s + 1).Trim (); - - var h = data.Headers; - if (WebHeaderCollection.AllowMultiValues (header)) { - h.AddInternal (header, value); - } else { - h.SetInternal (header, value); - } - } - - if (data.StatusCode == (int) HttpStatusCode.Continue) { - sPoint.SendContinue = true; - if (pos >= max) - return pos; - - if (data.request.ExpectContinue) { - data.request.DoContinueDelegate (data.StatusCode, data.Headers); - // Prevent double calls when getting the - // headers in several packets. - data.request.ExpectContinue = false; + if (operation.Request.Address.Scheme == Uri.UriSchemeHttps) { + if (!reused || monoTlsStream == null) { + if (ServicePoint.UseConnect) { + if (tunnel == null) + tunnel = new WebConnectionTunnel (operation.Request, ServicePoint.Address); + await tunnel.Initialize (stream, cancellationToken).ConfigureAwait (false); + if (!tunnel.Success) + return false; } - - data.ReadState = ReadState.None; - isContinue = true; - } - else { - data.ReadState = ReadState.Content; - return pos; + monoTlsStream = new MonoTlsStream (operation.Request, stream); + networkStream = await monoTlsStream.CreateStream (tunnel, cancellationToken).ConfigureAwait (false); } + return true; } - } while (emptyFirstLine || isContinue); - return -1; + networkStream = stream; + return true; + } catch (Exception ex) { + ex = HttpWebRequest.FlattenException (ex); + Debug ($"WC CREATE STREAM EX: Cnc={ID} {requestID} {operation.Aborted} - {ex.Message}"); + if (operation.Aborted || monoTlsStream == null) + throw GetException (WebExceptionStatus.ConnectFailure, ex); + throw GetException (monoTlsStream.ExceptionStatus, ex); + } finally { + Debug ($"WC CREATE STREAM DONE: Cnc={ID} {requestID}"); + } } - - void InitConnection (HttpWebRequest request) - { - request.WebConnection = this; - if (request.ReuseConnection) - request.StoredConnection = this; - if (request.Aborted) - return; + internal async Task InitConnection (WebOperation operation, CancellationToken cancellationToken) + { + Debug ($"WC INIT CONNECTION: Cnc={ID} Req={operation.Request.ID} Op={operation.ID}"); - keepAlive = request.KeepAlive; - Data = new WebConnectionData (request); + bool reset = true; retry: - Connect (request); - if (request.Aborted) - return; - - if (status != WebExceptionStatus.Success) { - if (!request.Aborted) { - request.SetWriteStreamError (status, connect_exception); - Close (true); + operation.ThrowIfClosedOrDisposed (cancellationToken); + + var reused = CheckReusable (); + Debug ($"WC INIT CONNECTION #1: Cnc={ID} Op={operation.ID} - {reused} - {operation.WriteBuffer != null} {operation.IsNtlmChallenge}"); + if (!reused) { + CloseSocket (); + if (reset) + Reset (); + try { + await Connect (operation, cancellationToken).ConfigureAwait (false); + Debug ($"WC INIT CONNECTION #2: Cnc={ID} Op={operation.ID} {socket.LocalEndPoint}"); + } catch (Exception ex) { + Debug ($"WC INIT CONNECTION #2 FAILED: Cnc={ID} Op={operation.ID} - {ex.Message}\n{ex}"); + throw; } - return; } - - if (!CreateStream (request)) { - if (request.Aborted) - return; - WebExceptionStatus st = status; - if (Data.Challenge != null) - goto retry; + var success = await CreateStream (operation, reused, cancellationToken).ConfigureAwait (false); - Exception cnc_exc = connect_exception; - if (cnc_exc == null && (Data.StatusCode == 401 || Data.StatusCode == 407)) { - st = WebExceptionStatus.ProtocolError; - if (Data.Headers == null) - Data.Headers = new WebHeaderCollection (); + Debug ($"WC INIT CONNECTION #3: Cnc={ID} Op={operation.ID} - {success}"); + if (!success) { + if (tunnel?.Challenge == null) + throw GetException (WebExceptionStatus.ProtocolError, null); - var webResponse = new HttpWebResponse (sPoint.Address, "CONNECT", Data, null); - cnc_exc = new WebException (Data.StatusCode == 407 ? "(407) Proxy Authentication Required" : "(401) Unauthorized", null, st, webResponse); - } - - connect_exception = null; - request.SetWriteStreamError (st, cnc_exc); - Close (true); - return; + if (tunnel.CloseConnection) + CloseSocket (); + reset = false; + goto retry; } - request.SetWriteStream (new WebConnectionStream (this, request)); + return new WebRequestStream (this, operation, networkStream, tunnel); } -#if MONOTOUCH - static bool warned_about_queue = false; -#endif - - internal EventHandler SendRequest (HttpWebRequest request) - { - if (request.Aborted) - return null; - - lock (this) { - if (state.TrySetBusy ()) { - status = WebExceptionStatus.Success; - ThreadPool.QueueUserWorkItem (o => { try { InitConnection ((HttpWebRequest) o); } catch {} }, request); - } else { - lock (queue) { -#if MONOTOUCH - if (!warned_about_queue) { - warned_about_queue = true; - Console.WriteLine ("WARNING: An HttpWebRequest was added to the ConnectionGroup queue because the connection limit was reached."); - } -#endif - queue.Enqueue (request); - } - } - } - - return abortHandler; - } - - void SendNext () + internal static WebException GetException (WebExceptionStatus status, Exception error) { - lock (queue) { - if (queue.Count > 0) { - SendRequest ((HttpWebRequest) queue.Dequeue ()); - } - } + if (error == null) + return new WebException ($"Error: {status}", status); + if (error is WebException wex) + return wex; + return new WebException ($"Error: {status} ({error.Message})", status, + WebExceptionInternalStatus.RequestFatal, error); } - internal void NextRead () - { - lock (this) { - if (Data.request != null) - Data.request.FinishedReading = true; - string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection"; - string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null; - bool keepAlive = (Data.Version == HttpVersion.Version11 && this.keepAlive); - if (Data.ProxyVersion != null && Data.ProxyVersion != HttpVersion.Version11) - keepAlive = false; - if (cncHeader != null) { - cncHeader = cncHeader.ToLower (); - keepAlive = (this.keepAlive && cncHeader.IndexOf ("keep-alive", StringComparison.Ordinal) != -1); - } - - if ((socket != null && !socket.Connected) || - (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close", StringComparison.Ordinal) != -1))) { - Close (false); - } - - state.SetIdle (); - if (priority_request != null) { - SendRequest (priority_request); - priority_request = null; - } else { - SendNext (); - } - } - } - - static bool ReadLine (byte [] buffer, ref int start, int max, ref string output) + internal static bool ReadLine (byte[] buffer, ref int start, int max, ref string output) { bool foundCR = false; StringBuilder text = new StringBuilder (); int c = 0; while (start < max) { - c = (int) buffer [start++]; + c = (int)buffer[start++]; - if (c == '\n') { // newline - if ((text.Length > 0) && (text [text.Length - 1] == '\r')) + if (c == '\n') { // newline + if ((text.Length > 0) && (text[text.Length - 1] == '\r')) text.Length--; foundCR = false; @@ -841,9 +308,9 @@ static bool ReadLine (byte [] buffer, ref int start, int max, ref string output) if (c == '\r') foundCR = true; - - text.Append ((char) c); + + text.Append ((char)c); } if (c != '\n' && c != '\r') @@ -861,357 +328,180 @@ static bool ReadLine (byte [] buffer, ref int start, int max, ref string output) return true; } - - internal IAsyncResult BeginRead (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state) + internal bool CanReuseConnection (WebOperation operation) { - Stream s = null; lock (this) { - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - if (nstream == null) - return null; - s = nstream; - } - - IAsyncResult result = null; - if (!chunkedRead || (!chunkStream.DataAvailable && chunkStream.WantMore)) { - try { - result = s.BeginRead (buffer, offset, size, cb, state); - cb = null; - } catch (Exception) { - HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead"); - throw; - } - } - - if (chunkedRead) { - WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size); - wr.InnerAsyncResult = result; - if (result == null) { - // Will be completed from the data in MonoChunkStream - wr.SetCompleted (true, (Exception) null); - wr.DoCallback (); - } - return wr; - } + if (Closed || currentOperation != null) + return false; + if (!NtlmAuthenticated) + return true; - return result; - } - - internal int EndRead (HttpWebRequest request, IAsyncResult result) - { - Stream s = null; - lock (this) { - if (request.Aborted) - throw new WebException ("Request aborted", WebExceptionStatus.RequestCanceled); - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - if (nstream == null) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - s = nstream; - } + NetworkCredential cnc_cred = NtlmCredential; + var request = operation.Request; - int nbytes = 0; - bool done = false; - WebAsyncResult wr = null; - IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult; - if (chunkedRead && (nsAsync is WebAsyncResult)) { - wr = (WebAsyncResult) nsAsync; - IAsyncResult inner = wr.InnerAsyncResult; - if (inner != null && !(inner is WebAsyncResult)) { - nbytes = s.EndRead (inner); - done = nbytes == 0; - } - } else if (!(nsAsync is WebAsyncResult)) { - nbytes = s.EndRead (nsAsync); - wr = (WebAsyncResult) result; - done = nbytes == 0; - } + bool isProxy = (request.Proxy != null && !request.Proxy.IsBypassed (request.RequestUri)); + ICredentials req_icreds = (!isProxy) ? request.Credentials : request.Proxy.Credentials; + NetworkCredential req_cred = (req_icreds != null) ? req_icreds.GetCredential (request.RequestUri, "NTLM") : null; - if (chunkedRead) { - try { - chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes); - if (!done && nbytes == 0 && chunkStream.WantMore) - nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size); - } catch (Exception e) { - if (e is WebException) - throw e; - - throw new WebException ("Invalid chunked data.", e, - WebExceptionStatus.ServerProtocolViolation, null); - } - - if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) { - HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead"); - throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null); + if (cnc_cred == null || req_cred == null || + cnc_cred.Domain != req_cred.Domain || cnc_cred.UserName != req_cred.UserName || + cnc_cred.Password != req_cred.Password) { + return false; } - } - - return (nbytes != 0) ? nbytes : -1; - } - // To be called on chunkedRead when we can read no data from the MonoChunkStream yet - int EnsureRead (byte [] buffer, int offset, int size) - { - byte [] morebytes = null; - int nbytes = 0; - while (nbytes == 0 && chunkStream.WantMore) { - int localsize = chunkStream.ChunkLeft; - if (localsize <= 0) // not read chunk size yet - localsize = 1024; - else if (localsize > 16384) - localsize = 16384; - - if (morebytes == null || morebytes.Length < localsize) - morebytes = new byte [localsize]; - - int nread = nstream.Read (morebytes, 0, localsize); - if (nread <= 0) - return 0; // Error - - chunkStream.Write (morebytes, 0, nread); - nbytes += chunkStream.Read (buffer, offset + nbytes, size - nbytes); + bool req_sharing = request.UnsafeAuthenticatedConnectionSharing; + bool cnc_sharing = UnsafeAuthenticatedConnectionSharing; + return !(req_sharing == false || req_sharing != cnc_sharing); } - - return nbytes; } - bool CompleteChunkedRead() + bool PrepareSharingNtlm (WebOperation operation) { - if (!chunkedRead || chunkStream == null) + if (operation == null || !NtlmAuthenticated) return true; - while (chunkStream.WantMore) { - int nbytes = nstream.Read (buffer, 0, buffer.Length); - if (nbytes <= 0) - return false; // Socket was disconnected + bool needs_reset = false; + NetworkCredential cnc_cred = NtlmCredential; + var request = operation.Request; - chunkStream.Write (buffer, 0, nbytes); - } - - return true; - } + bool isProxy = (request.Proxy != null && !request.Proxy.IsBypassed (request.RequestUri)); + ICredentials req_icreds = (!isProxy) ? request.Credentials : request.Proxy.Credentials; + NetworkCredential req_cred = (req_icreds != null) ? req_icreds.GetCredential (request.RequestUri, "NTLM") : null; - internal IAsyncResult BeginWrite (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state) - { - Stream s = null; - lock (this) { - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - if (nstream == null) - return null; - s = nstream; + if (cnc_cred == null || req_cred == null || + cnc_cred.Domain != req_cred.Domain || cnc_cred.UserName != req_cred.UserName || + cnc_cred.Password != req_cred.Password) { + needs_reset = true; } - IAsyncResult result = null; - try { - result = s.BeginWrite (buffer, offset, size, cb, state); - } catch (ObjectDisposedException) { - lock (this) { - if (Data.request != request) - return null; - } - throw; - } catch (IOException e) { - SocketException se = e.InnerException as SocketException; - if (se != null && se.SocketErrorCode == SocketError.NotConnected) { - return null; - } - throw; - } catch (Exception) { - status = WebExceptionStatus.SendFailure; - throw; + if (!needs_reset) { + bool req_sharing = request.UnsafeAuthenticatedConnectionSharing; + bool cnc_sharing = UnsafeAuthenticatedConnectionSharing; + needs_reset = (req_sharing == false || req_sharing != cnc_sharing); } - return result; + return needs_reset; } - internal bool EndWrite (HttpWebRequest request, bool throwOnError, IAsyncResult result) + void Reset () { - Stream s = null; lock (this) { - if (status == WebExceptionStatus.RequestCanceled) - return true; - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - if (nstream == null) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - s = nstream; - } - - try { - s.EndWrite (result); - return true; - } catch (Exception exc) { - status = WebExceptionStatus.SendFailure; - if (throwOnError && exc.InnerException != null) - throw exc.InnerException; - return false; + tunnel = null; + ResetNtlm (); } } - internal int Read (HttpWebRequest request, byte [] buffer, int offset, int size) + void Close (bool reset) { - Stream s = null; lock (this) { - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - if (nstream == null) - return 0; - s = nstream; + CloseSocket (); + if (reset) + Reset (); } + } - int result = 0; - try { - bool done = false; - if (!chunkedRead) { - result = s.Read (buffer, offset, size); - done = (result == 0); + void CloseSocket () + { + lock (this) { + if (networkStream != null) { + try { + networkStream.Dispose (); + } catch { } + networkStream = null; } - if (chunkedRead) { + if (socket != null) { try { - chunkStream.WriteAndReadBack (buffer, offset, size, ref result); - if (!done && result == 0 && chunkStream.WantMore) - result = EnsureRead (buffer, offset, size); - } catch (Exception e) { - HandleError (WebExceptionStatus.ReceiveFailure, e, "chunked Read1"); - throw; - } - - if ((done || result == 0) && chunkStream.WantMore) { - HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked Read2"); - throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null); - } + socket.Dispose (); + } catch { } + socket = null; } - } catch (Exception e) { - HandleError (WebExceptionStatus.ReceiveFailure, e, "Read"); + + monoTlsStream = null; } + } + + DateTime idleSince; + WebOperation currentOperation; - return result; + public bool Closed => disposed != 0; + + public bool Busy { + get { return currentOperation != null; } + } + + public DateTime IdleSince { + get { return idleSince; } } - internal bool Write (HttpWebRequest request, byte [] buffer, int offset, int size, ref string err_msg) + public bool StartOperation (WebOperation operation, bool reused) { - err_msg = null; - Stream s = null; lock (this) { - if (Data.request != request) - throw new ObjectDisposedException (typeof (NetworkStream).FullName); - s = nstream; - if (s == null) + if (Closed) return false; - } - - try { - s.Write (buffer, offset, size); - } catch (Exception e) { - err_msg = e.Message; - WebExceptionStatus wes = WebExceptionStatus.SendFailure; - string msg = "Write: " + err_msg; - if (e is WebException) { - HandleError (wes, e, msg); + if (Interlocked.CompareExchange (ref currentOperation, operation, null) != null) return false; + + idleSince = DateTime.UtcNow + TimeSpan.FromDays (3650); + + if (reused && !PrepareSharingNtlm (operation)) { + Debug ($"WC START - CAN'T REUSE: Cnc={ID} Op={operation.ID}"); + Close (true); } - HandleError (wes, e, msg); - return false; + operation.RegisterRequest (ServicePoint, this); + Debug ($"WC START: Cnc={ID} Op={operation.ID}"); } + + operation.Run (); return true; } - internal void Close (bool sendNext) + public bool Continue (WebOperation next) { lock (this) { - if (Data != null && Data.request != null && Data.request.ReuseConnection) { - Data.request.ReuseConnection = false; - return; - } + if (Closed) + return false; - if (nstream != null) { - try { - nstream.Close (); - } catch {} - nstream = null; + Debug ($"WC CONTINUE: Cnc={ID} connected={socket?.Connected} next={next?.ID} current={currentOperation?.ID}"); + if (socket == null || !socket.Connected || !PrepareSharingNtlm (next)) { + Close (true); + return false; } - if (socket != null) { - try { - socket.Close (); - } catch {} - socket = null; - } + currentOperation = next; - if (ntlm_authenticated) - ResetNtlm (); - if (Data != null) { - lock (Data) { - Data.ReadState = ReadState.Aborted; - } - } - state.SetIdle (); - Data = new WebConnectionData (); - if (sendNext) - SendNext (); - - connect_request = null; - connect_ntlm_auth_state = NtlmAuthState.None; + if (next == null) + return true; + + // Ok, we got another connection. Let's run it! + next.RegisterRequest (ServicePoint, this); } + + next.Run (); + return true; } - void Abort (object sender, EventArgs args) + void Dispose (bool disposing) { - lock (this) { - lock (queue) { - HttpWebRequest req = (HttpWebRequest) sender; - if (Data.request == req || Data.request == null) { - if (!req.FinishedReading) { - status = WebExceptionStatus.RequestCanceled; - Close (false); - if (queue.Count > 0) { - Data.request = (HttpWebRequest) queue.Dequeue (); - SendRequest (Data.request); - } - } - return; - } + if (Interlocked.CompareExchange (ref disposed, 1, 0) != 0) + return; + Debug ($"WC DISPOSE: Cnc={ID}"); + Close (true); + } - req.FinishedReading = true; - req.SetResponseError (WebExceptionStatus.RequestCanceled, null, "User aborted"); - if (queue.Count > 0 && queue.Peek () == sender) { - queue.Dequeue (); - } else if (queue.Count > 0) { - object [] old = queue.ToArray (); - queue.Clear (); - for (int i = old.Length - 1; i >= 0; i--) { - if (old [i] != sender) - queue.Enqueue (old [i]); - } - } - } - } + public void Dispose () + { + Dispose (true); } - internal void ResetNtlm () + void ResetNtlm () { ntlm_authenticated = false; ntlm_credentials = null; unsafe_sharing = false; } - internal bool Connected { - get { - lock (this) { - return (socket != null && socket.Connected); - } - } - } - - // -Used for NTLM authentication - internal HttpWebRequest PriorityRequest { - set { priority_request = value; } - } - internal bool NtlmAuthenticated { get { return ntlm_authenticated; } set { ntlm_authenticated = value; } diff --git a/mcs/class/System/System.Net/WebConnectionData.cs b/mcs/class/System/System.Net/WebConnectionData.cs deleted file mode 100644 index 77d19540be16..000000000000 --- a/mcs/class/System/System.Net/WebConnectionData.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// System.Net.WebConnectionData -// -// Authors: -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// -// (C) 2003 Ximian, Inc (http://www.ximian.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; - -namespace System.Net -{ - class WebConnectionData - { - HttpWebRequest _request; - public int StatusCode; - public string StatusDescription; - public WebHeaderCollection Headers; - public Version Version; - public Version ProxyVersion; - public Stream stream; - public string[] Challenge; - ReadState _readState; - - public WebConnectionData () - { - _readState = ReadState.None; - } - - public WebConnectionData (HttpWebRequest request) - { - this._request = request; - } - - public HttpWebRequest request { - get { - return _request; - } - set { - _request = value; - } - } - - public ReadState ReadState { - get { - return _readState; - } - set { - lock (this) { - if ((_readState == ReadState.Aborted) && (value != ReadState.Aborted)) - throw new WebException ("Aborted", WebExceptionStatus.RequestCanceled); - _readState = value; - } - } - } - } -} - diff --git a/mcs/class/System/System.Net/WebConnectionGroup.cs b/mcs/class/System/System.Net/WebConnectionGroup.cs deleted file mode 100644 index 3936ee428981..000000000000 --- a/mcs/class/System/System.Net/WebConnectionGroup.cs +++ /dev/null @@ -1,292 +0,0 @@ -// -// System.Net.WebConnectionGroup -// -// Authors: -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// Martin Baulig (martin.baulig@xamarin.com) -// -// (C) 2003 Ximian, Inc (http://www.ximian.com) -// Copyright 2011-2014 Xamarin, Inc (http://www.xamarin.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Collections; -using System.Collections.Generic; -using System.Net.Configuration; -using System.Net.Sockets; -using System.Diagnostics; - -namespace System.Net -{ - class WebConnectionGroup - { - ServicePoint sPoint; - string name; - LinkedList connections; - Queue queue; - bool closing; - - public WebConnectionGroup (ServicePoint sPoint, string name) - { - this.sPoint = sPoint; - this.name = name; - connections = new LinkedList (); - queue = new Queue (); - } - - public event EventHandler ConnectionClosed; - - void OnConnectionClosed () - { - if (ConnectionClosed != null) - ConnectionClosed (this, null); - } - - public void Close () - { - List connectionsToClose = null; - - //TODO: what do we do with the queue? Empty it out and abort the requests? - //TODO: abort requests or wait for them to finish - lock (sPoint) { - closing = true; - var iter = connections.First; - while (iter != null) { - var cnc = iter.Value.Connection; - var node = iter; - iter = iter.Next; - - // Closing connections inside the lock leads to a deadlock. - if (connectionsToClose == null) - connectionsToClose = new List(); - - connectionsToClose.Add (cnc); - connections.Remove (node); - } - } - - if (connectionsToClose != null) { - foreach (var cnc in connectionsToClose) { - cnc.Close (false); - OnConnectionClosed (); - } - } - } - - public WebConnection GetConnection (HttpWebRequest request, out bool created) - { - lock (sPoint) { - return CreateOrReuseConnection (request, out created); - } - } - - static void PrepareSharingNtlm (WebConnection cnc, HttpWebRequest request) - { - if (!cnc.NtlmAuthenticated) - return; - - bool needs_reset = false; - NetworkCredential cnc_cred = cnc.NtlmCredential; - - bool isProxy = (request.Proxy != null && !request.Proxy.IsBypassed (request.RequestUri)); - ICredentials req_icreds = (!isProxy) ? request.Credentials : request.Proxy.Credentials; - NetworkCredential req_cred = (req_icreds != null) ? req_icreds.GetCredential (request.RequestUri, "NTLM") : null; - - if (cnc_cred == null || req_cred == null || - cnc_cred.Domain != req_cred.Domain || cnc_cred.UserName != req_cred.UserName || - cnc_cred.Password != req_cred.Password) { - needs_reset = true; - } - - if (!needs_reset) { - bool req_sharing = request.UnsafeAuthenticatedConnectionSharing; - bool cnc_sharing = cnc.UnsafeAuthenticatedConnectionSharing; - needs_reset = (req_sharing == false || req_sharing != cnc_sharing); - } - if (needs_reset) { - cnc.Close (false); // closes the authenticated connection - cnc.ResetNtlm (); - } - } - - ConnectionState FindIdleConnection () - { - foreach (var cnc in connections) { - if (cnc.Busy) - continue; - - connections.Remove (cnc); - connections.AddFirst (cnc); - return cnc; - } - - return null; - } - - WebConnection CreateOrReuseConnection (HttpWebRequest request, out bool created) - { - var cnc = FindIdleConnection (); - if (cnc != null) { - created = false; - PrepareSharingNtlm (cnc.Connection, request); - return cnc.Connection; - } - - if (sPoint.ConnectionLimit > connections.Count || connections.Count == 0) { - created = true; - cnc = new ConnectionState (this); - connections.AddFirst (cnc); - return cnc.Connection; - } - - created = false; - cnc = connections.Last.Value; - connections.Remove (cnc); - connections.AddFirst (cnc); - return cnc.Connection; - } - - public string Name { - get { return name; } - } - - internal Queue Queue { - get { return queue; } - } - - internal bool TryRecycle (TimeSpan maxIdleTime, ref DateTime idleSince) - { - var now = DateTime.UtcNow; - - again: - bool recycled; - List connectionsToClose = null; - - lock (sPoint) { - if (closing) { - idleSince = DateTime.MinValue; - return true; - } - - int count = 0; - var iter = connections.First; - while (iter != null) { - var cnc = iter.Value; - var node = iter; - iter = iter.Next; - - ++count; - if (cnc.Busy) - continue; - - if (count <= sPoint.ConnectionLimit && now - cnc.IdleSince < maxIdleTime) { - if (cnc.IdleSince > idleSince) - idleSince = cnc.IdleSince; - continue; - } - - /* - * Do not call WebConnection.Close() while holding the ServicePoint lock - * as this could deadlock when attempting to take the WebConnection lock. - * - */ - - if (connectionsToClose == null) - connectionsToClose = new List (); - connectionsToClose.Add (cnc.Connection); - connections.Remove (node); - } - - recycled = connections.Count == 0; - } - - // Did we find anything that can be closed? - if (connectionsToClose == null) - return recycled; - - // Ok, let's get rid of these! - foreach (var cnc in connectionsToClose) - cnc.Close (false); - - // Re-take the lock, then remove them from the connection list. - goto again; - } - - class ConnectionState : IWebConnectionState { - public WebConnection Connection { - get; - private set; - } - - public WebConnectionGroup Group { - get; - private set; - } - - public ServicePoint ServicePoint { - get { return Group.sPoint; } - } - - bool busy; - DateTime idleSince; - - public bool Busy { - get { return busy; } - } - - public DateTime IdleSince { - get { return idleSince; } - } - - public bool TrySetBusy () - { - lock (ServicePoint) { - if (busy) - return false; - busy = true; - idleSince = DateTime.UtcNow + TimeSpan.FromDays (3650); - return true; - } - } - - public void SetIdle () - { - lock (ServicePoint) { - busy = false; - idleSince = DateTime.UtcNow; - } - } - - public ConnectionState (WebConnectionGroup group) - { - Group = group; - idleSince = DateTime.UtcNow; - Connection = new WebConnection (this, group.sPoint); - } - } - - } -} - diff --git a/mcs/class/System/System.Net/WebConnectionStream.cs b/mcs/class/System/System.Net/WebConnectionStream.cs index 96a2b3bf42fc..f382ac90fccd 100644 --- a/mcs/class/System/System.Net/WebConnectionStream.cs +++ b/mcs/class/System/System.Net/WebConnectionStream.cs @@ -3,9 +3,11 @@ // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Martin Baulig // // (C) 2003 Ximian, Inc (http://www.ximian.com) // (C) 2004 Novell, Inc (http://www.novell.com) +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) // // @@ -28,127 +30,53 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // - using System.IO; using System.Text; using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Net.Sockets; namespace System.Net { - class WebConnectionStream : Stream + abstract class WebConnectionStream : Stream { - static byte [] crlf = new byte [] { 13, 10 }; - bool isRead; - WebConnection cnc; - HttpWebRequest request; - byte [] readBuffer; - int readBufferOffset; - int readBufferSize; - int stream_length; // -1 when CL not present - long contentLength; - long totalRead; - internal long totalWritten; - bool nextReadCalled; - int pendingReads; - int pendingWrites; - ManualResetEvent pending; - bool allowBuffering; - bool sendChunked; - MemoryStream writeBuffer; - bool requestWritten; - byte [] headers; + protected bool closed; bool disposed; - bool headersSent; object locker = new object (); - bool initRead; - bool read_eof; - bool complete_request_written; int read_timeout; int write_timeout; - AsyncCallback cb_wrapper; // Calls to ReadCallbackWrapper or WriteCallbacWrapper internal bool IgnoreIOErrors; - public WebConnectionStream (WebConnection cnc, WebConnectionData data) - { - if (data == null) - throw new InvalidOperationException ("data was not initialized"); - if (data.Headers == null) - throw new InvalidOperationException ("data.Headers was not initialized"); - if (data.request == null) - throw new InvalidOperationException ("data.request was not initialized"); - isRead = true; - cb_wrapper = new AsyncCallback (ReadCallbackWrapper); - pending = new ManualResetEvent (true); - this.request = data.request; - read_timeout = request.ReadWriteTimeout; - write_timeout = read_timeout; - this.cnc = cnc; - string contentType = data.Headers ["Transfer-Encoding"]; - bool chunkedRead = (contentType != null && contentType.IndexOf ("chunked", StringComparison.OrdinalIgnoreCase) != -1); - string clength = data.Headers ["Content-Length"]; - if (!chunkedRead && clength != null && clength != "") { - try { - contentLength = Int32.Parse (clength); - if (contentLength == 0 && !IsNtlmAuth ()) { - ReadAll (); - } - } catch { - contentLength = Int64.MaxValue; - } - } else { - contentLength = Int64.MaxValue; - } - - // Negative numbers? - if (!Int32.TryParse (clength, out stream_length)) - stream_length = -1; - } - - public WebConnectionStream (WebConnection cnc, HttpWebRequest request) + protected WebConnectionStream (WebConnection cnc, WebOperation operation, Stream stream) { - read_timeout = request.ReadWriteTimeout; + Connection = cnc; + Operation = operation; + Request = operation.Request; + InnerStream = stream; + + read_timeout = Request.ReadWriteTimeout; write_timeout = read_timeout; - isRead = false; - cb_wrapper = new AsyncCallback (WriteCallbackWrapper); - this.cnc = cnc; - this.request = request; - allowBuffering = request.InternalAllowBuffering; - sendChunked = request.SendChunked; - if (sendChunked) - pending = new ManualResetEvent (true); - else if (allowBuffering) - writeBuffer = new MemoryStream (); } - bool CheckAuthHeader (string headerName) - { - var authHeader = cnc.Data.Headers [headerName]; - return (authHeader != null && authHeader.IndexOf ("NTLM", StringComparison.Ordinal) != -1); + internal HttpWebRequest Request { + get; } - bool IsNtlmAuth () - { - bool isProxy = (request.Proxy != null && !request.Proxy.IsBypassed (request.Address)); - if (isProxy && CheckAuthHeader ("Proxy-Authenticate")) - return true; - return CheckAuthHeader ("WWW-Authenticate"); + internal WebConnection Connection { + get; } - internal void CheckResponseInBuffer () - { - if (contentLength > 0 && (readBufferSize - readBufferOffset) >= contentLength) { - if (!IsNtlmAuth ()) - ReadAll (); - } + internal WebOperation Operation { + get; } - internal HttpWebRequest Request { - get { return request; } - } + internal ServicePoint ServicePoint => Connection.ServicePoint; - internal WebConnection Connection { - get { return cnc; } + internal Stream InnerStream { + get; } + public override bool CanTimeout { get { return true; } } @@ -177,579 +105,126 @@ public override int WriteTimeout { } } - internal bool CompleteRequestWritten { - get { return complete_request_written; } - } - - internal bool SendChunked { - set { sendChunked = value; } - } - - internal byte [] ReadBuffer { - set { readBuffer = value; } - } - - internal int ReadBufferOffset { - set { readBufferOffset = value; } - } - - internal int ReadBufferSize { - set { readBufferSize = value; } - } - - internal byte[] WriteBuffer { - get { return writeBuffer.GetBuffer (); } - } - - internal int WriteBufferLength { - get { return writeBuffer != null ? (int) writeBuffer.Length : (-1); } - } - - internal void ForceCompletion () - { - if (!nextReadCalled) { - if (contentLength == Int64.MaxValue) - contentLength = 0; - nextReadCalled = true; - cnc.NextRead (); - } - } - - internal void CheckComplete () + protected Exception GetException (Exception e) { - bool nrc = nextReadCalled; - if (!nrc && readBufferSize - readBufferOffset == contentLength) { - nextReadCalled = true; - cnc.NextRead (); - } + e = HttpWebRequest.FlattenException (e); + if (e is WebException) + return e; + if (Operation.Aborted || e is OperationCanceledException || e is ObjectDisposedException) + return HttpWebRequest.CreateRequestAbortedException (); + return e; } - internal void ReadAll () + public override int Read (byte[] buffer, int offset, int size) { - if (!isRead || read_eof || totalRead >= contentLength || nextReadCalled) { - if (isRead && !nextReadCalled) { - nextReadCalled = true; - cnc.NextRead (); - } - return; - } - - if (!pending.WaitOne (ReadTimeout)) - throw new WebException ("The operation has timed out.", WebExceptionStatus.Timeout); - lock (locker) { - if (totalRead >= contentLength) - return; - - byte [] b = null; - int diff = readBufferSize - readBufferOffset; - int new_size; - - if (contentLength == Int64.MaxValue) { - MemoryStream ms = new MemoryStream (); - byte [] buffer = null; - if (readBuffer != null && diff > 0) { - ms.Write (readBuffer, readBufferOffset, diff); - if (readBufferSize >= 8192) - buffer = readBuffer; - } - - if (buffer == null) - buffer = new byte [8192]; - - int read; - while ((read = cnc.Read (request, buffer, 0, buffer.Length)) != 0) - ms.Write (buffer, 0, read); - - b = ms.GetBuffer (); - new_size = (int) ms.Length; - contentLength = new_size; - } else { - new_size = (int) (contentLength - totalRead); - b = new byte [new_size]; - if (readBuffer != null && diff > 0) { - if (diff > new_size) - diff = new_size; - - Buffer.BlockCopy (readBuffer, readBufferOffset, b, 0, diff); - } - - int remaining = new_size - diff; - int r = -1; - while (remaining > 0 && r != 0) { - r = cnc.Read (request, b, diff, remaining); - remaining -= r; - diff += r; - } - } - - readBuffer = b; - readBufferOffset = 0; - readBufferSize = new_size; - totalRead = 0; - nextReadCalled = true; - } + if (!CanRead) + throw new NotSupportedException (SR.net_writeonlystream); + Operation.ThrowIfClosedOrDisposed (); - cnc.NextRead (); - } - - void WriteCallbackWrapper (IAsyncResult r) - { - WebAsyncResult result = r as WebAsyncResult; - if (result != null && result.AsyncWriteAll) - return; - - if (r.AsyncState != null) { - result = (WebAsyncResult) r.AsyncState; - result.InnerAsyncResult = r; - result.DoCallback (); - } else { - try { - EndWrite (r); - } catch { - } - } - } + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); - void ReadCallbackWrapper (IAsyncResult r) - { - WebAsyncResult result; - if (r.AsyncState != null) { - result = (WebAsyncResult) r.AsyncState; - result.InnerAsyncResult = r; - result.DoCallback (); - } else { - try { - EndRead (r); - } catch { - } - } - } + int length = buffer.Length; + if (offset < 0 || length < offset) + throw new ArgumentOutOfRangeException (nameof (offset)); + if (size < 0 || (length - offset) < size) + throw new ArgumentOutOfRangeException (nameof (size)); - public override int Read (byte [] buffer, int offset, int size) - { - AsyncCallback cb = cb_wrapper; - WebAsyncResult res = (WebAsyncResult) BeginRead (buffer, offset, size, cb, null); - if (!res.IsCompleted && !res.WaitUntilComplete (ReadTimeout, false)) { - nextReadCalled = true; - cnc.Close (true); - throw new WebException ("The operation has timed out.", WebExceptionStatus.Timeout); + try { + return ReadAsync (buffer, offset, size, CancellationToken.None).Result; + } catch (Exception e) { + throw GetException (e); } - - return EndRead (res); } - public override IAsyncResult BeginRead (byte [] buffer, int offset, int size, + public override IAsyncResult BeginRead (byte[] buffer, int offset, int size, AsyncCallback cb, object state) { - if (!isRead) - throw new NotSupportedException ("this stream does not allow reading"); + if (!CanRead) + throw new NotSupportedException (SR.net_writeonlystream); + Operation.ThrowIfClosedOrDisposed (); if (buffer == null) - throw new ArgumentNullException ("buffer"); + throw new ArgumentNullException (nameof (buffer)); int length = buffer.Length; if (offset < 0 || length < offset) - throw new ArgumentOutOfRangeException ("offset"); + throw new ArgumentOutOfRangeException (nameof (offset)); if (size < 0 || (length - offset) < size) - throw new ArgumentOutOfRangeException ("size"); + throw new ArgumentOutOfRangeException (nameof (size)); - lock (locker) { - pendingReads++; - pending.Reset (); - } - - WebAsyncResult result = new WebAsyncResult (cb, state, buffer, offset, size); - if (totalRead >= contentLength) { - result.SetCompleted (true, -1); - result.DoCallback (); - return result; - } - - int remaining = readBufferSize - readBufferOffset; - if (remaining > 0) { - int copy = (remaining > size) ? size : remaining; - Buffer.BlockCopy (readBuffer, readBufferOffset, buffer, offset, copy); - readBufferOffset += copy; - offset += copy; - size -= copy; - totalRead += copy; - if (size == 0 || totalRead >= contentLength) { - result.SetCompleted (true, copy); - result.DoCallback (); - return result; - } - result.NBytes = copy; - } - - if (cb != null) - cb = cb_wrapper; - - if (contentLength != Int64.MaxValue && contentLength - totalRead < size) - size = (int)(contentLength - totalRead); - - if (!read_eof) { - result.InnerAsyncResult = cnc.BeginRead (request, buffer, offset, size, cb, result); - } else { - result.SetCompleted (true, result.NBytes); - result.DoCallback (); - } - return result; + var task = ReadAsync (buffer, offset, size, CancellationToken.None); + return TaskToApm.Begin (task, cb, state); } public override int EndRead (IAsyncResult r) { - WebAsyncResult result = (WebAsyncResult) r; - if (result.EndCalled) { - int xx = result.NBytes; - return (xx >= 0) ? xx : 0; - } - - result.EndCalled = true; - - if (!result.IsCompleted) { - int nbytes = -1; - try { - nbytes = cnc.EndRead (request, result); - } catch (Exception exc) { - lock (locker) { - pendingReads--; - if (pendingReads == 0) - pending.Set (); - } - - nextReadCalled = true; - cnc.Close (true); - result.SetCompleted (false, exc); - result.DoCallback (); - throw; - } - - if (nbytes < 0) { - nbytes = 0; - read_eof = true; - } - - totalRead += nbytes; - result.SetCompleted (false, nbytes + result.NBytes); - result.DoCallback (); - if (nbytes == 0) - contentLength = totalRead; - } - - lock (locker) { - pendingReads--; - if (pendingReads == 0) - pending.Set (); - } - - if (totalRead >= contentLength && !nextReadCalled) - ReadAll (); - - int nb = result.NBytes; - return (nb >= 0) ? nb : 0; - } - - void WriteAsyncCB (IAsyncResult r) - { - WebAsyncResult result = (WebAsyncResult) r.AsyncState; - result.InnerAsyncResult = null; + if (r == null) + throw new ArgumentNullException (nameof (r)); try { - cnc.EndWrite (request, true, r); - result.SetCompleted (false, 0); - if (!initRead) { - initRead = true; - cnc.InitRead (); - } + return TaskToApm.End (r); } catch (Exception e) { - KillBuffer (); - nextReadCalled = true; - cnc.Close (true); - if (e is System.Net.Sockets.SocketException) - e = new IOException ("Error writing request", e); - result.SetCompleted (false, e); + throw GetException (e); } - - if (allowBuffering && !sendChunked && request.ContentLength > 0 && totalWritten == request.ContentLength) - complete_request_written = true; - - result.DoCallback (); } - public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size, - AsyncCallback cb, object state) + public override IAsyncResult BeginWrite (byte[] buffer, int offset, int size, + AsyncCallback cb, object state) { - if (request.Aborted) - throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled); - - if (isRead) - throw new NotSupportedException ("this stream does not allow writing"); + if (!CanWrite) + throw new NotSupportedException (SR.net_readonlystream); + Operation.ThrowIfClosedOrDisposed (); if (buffer == null) - throw new ArgumentNullException ("buffer"); + throw new ArgumentNullException (nameof (buffer)); int length = buffer.Length; if (offset < 0 || length < offset) - throw new ArgumentOutOfRangeException ("offset"); + throw new ArgumentOutOfRangeException (nameof (offset)); if (size < 0 || (length - offset) < size) - throw new ArgumentOutOfRangeException ("size"); - - if (sendChunked) { - lock (locker) { - pendingWrites++; - pending.Reset (); - } - } - - WebAsyncResult result = new WebAsyncResult (cb, state); - AsyncCallback callback = new AsyncCallback (WriteAsyncCB); - - if (sendChunked) { - requestWritten = true; - - string cSize = String.Format ("{0:X}\r\n", size); - byte[] head = Encoding.ASCII.GetBytes (cSize); - int chunkSize = 2 + size + head.Length; - byte[] newBuffer = new byte [chunkSize]; - Buffer.BlockCopy (head, 0, newBuffer, 0, head.Length); - Buffer.BlockCopy (buffer, offset, newBuffer, head.Length, size); - Buffer.BlockCopy (crlf, 0, newBuffer, head.Length + size, crlf.Length); - - if (allowBuffering) { - if (writeBuffer == null) - writeBuffer = new MemoryStream (); - writeBuffer.Write (buffer, offset, size); - totalWritten += size; - } - - buffer = newBuffer; - offset = 0; - size = chunkSize; - } else { - CheckWriteOverflow (request.ContentLength, totalWritten, size); - - if (allowBuffering) { - if (writeBuffer == null) - writeBuffer = new MemoryStream (); - writeBuffer.Write (buffer, offset, size); - totalWritten += size; - - if (request.ContentLength <= 0 || totalWritten < request.ContentLength) { - result.SetCompleted (true, 0); - result.DoCallback (); - return result; - } - - result.AsyncWriteAll = true; - requestWritten = true; - buffer = writeBuffer.GetBuffer (); - offset = 0; - size = (int)totalWritten; - } - } + throw new ArgumentOutOfRangeException (nameof (size)); - try { - result.InnerAsyncResult = cnc.BeginWrite (request, buffer, offset, size, callback, result); - if (result.InnerAsyncResult == null) { - if (!result.IsCompleted) - result.SetCompleted (true, 0); - result.DoCallback (); - } - } catch (Exception) { - if (!IgnoreIOErrors) - throw; - result.SetCompleted (true, 0); - result.DoCallback (); - } - totalWritten += size; - return result; - } - - void CheckWriteOverflow (long contentLength, long totalWritten, long size) - { - if (contentLength == -1) - return; - - long avail = contentLength - totalWritten; - if (size > avail) { - KillBuffer (); - nextReadCalled = true; - cnc.Close (true); - throw new ProtocolViolationException ( - "The number of bytes to be written is greater than " + - "the specified ContentLength."); - } + var task = WriteAsync (buffer, offset, size, CancellationToken.None); + return TaskToApm.Begin (task, cb, state); } public override void EndWrite (IAsyncResult r) { if (r == null) - throw new ArgumentNullException ("r"); - - WebAsyncResult result = r as WebAsyncResult; - if (result == null) - throw new ArgumentException ("Invalid IAsyncResult"); - - if (result.EndCalled) - return; - - if (sendChunked) { - lock (locker) { - pendingWrites--; - if (pendingWrites <= 0) - pending.Set (); - } - } + throw new ArgumentNullException (nameof (r)); - result.EndCalled = true; - if (result.AsyncWriteAll) { - result.WaitUntilComplete (); - if (result.GotException) - throw result.Exception; - return; - } - - if (allowBuffering && !sendChunked) - return; - - if (result.GotException) - throw result.Exception; - } - - public override void Write (byte [] buffer, int offset, int size) - { - AsyncCallback cb = cb_wrapper; - WebAsyncResult res = (WebAsyncResult) BeginWrite (buffer, offset, size, cb, null); - if (!res.IsCompleted && !res.WaitUntilComplete (WriteTimeout, false)) { - KillBuffer (); - nextReadCalled = true; - cnc.Close (true); - throw new IOException ("Write timed out."); + try { + TaskToApm.End (r); + } catch (Exception e) { + throw GetException (e); } - - EndWrite (res); - } - - public override void Flush () - { - } - - internal void SetHeadersAsync (bool setInternalLength, SimpleAsyncCallback callback) - { - SimpleAsyncResult.Run (r => SetHeadersAsync (r, setInternalLength), callback); } - bool SetHeadersAsync (SimpleAsyncResult result, bool setInternalLength) + public override void Write (byte[] buffer, int offset, int size) { - if (headersSent) - return false; - - string method = request.Method; - bool no_writestream = (method == "GET" || method == "CONNECT" || method == "HEAD" || - method == "TRACE"); - bool webdav = (method == "PROPFIND" || method == "PROPPATCH" || method == "MKCOL" || - method == "COPY" || method == "MOVE" || method == "LOCK" || - method == "UNLOCK"); - - if (setInternalLength && !no_writestream && writeBuffer != null) - request.InternalContentLength = writeBuffer.Length; + if (!CanWrite) + throw new NotSupportedException (SR.net_readonlystream); + Operation.ThrowIfClosedOrDisposed (); - bool has_content = !no_writestream && (writeBuffer == null || request.ContentLength > -1); - if (!(sendChunked || has_content || no_writestream || webdav)) - return false; - - headersSent = true; - headers = request.GetRequestHeaders (); - - var innerResult = cnc.BeginWrite (request, headers, 0, headers.Length, r => { - try { - cnc.EndWrite (request, true, r); - if (!initRead) { - initRead = true; - cnc.InitRead (); - } - var cl = request.ContentLength; - if (!sendChunked && cl == 0) - requestWritten = true; - result.SetCompleted (false); - } catch (WebException e) { - result.SetCompleted (false, e); - } catch (Exception e) { - result.SetCompleted (false, new WebException ("Error writing headers", WebExceptionStatus.SendFailure, WebExceptionInternalStatus.RequestFatal, e)); - } - }, null); - - return innerResult != null; - } + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); - internal bool RequestWritten { - get { return requestWritten; } - } + int length = buffer.Length; + if (offset < 0 || length < offset) + throw new ArgumentOutOfRangeException (nameof (offset)); + if (size < 0 || (length - offset) < size) + throw new ArgumentOutOfRangeException (nameof (size)); - internal SimpleAsyncResult WriteRequestAsync (SimpleAsyncCallback callback) - { - var result = WriteRequestAsync (callback); try { - if (!WriteRequestAsync (result)) - result.SetCompleted (true); - } catch (Exception ex) { - result.SetCompleted (true, ex); + WriteAsync (buffer, offset, size).Wait (); + } catch (Exception e) { + throw GetException (e); } - return result; } - internal bool WriteRequestAsync (SimpleAsyncResult result) + public override void Flush () { - if (requestWritten) - return false; - - requestWritten = true; - if (sendChunked || !allowBuffering || writeBuffer == null) - return false; - - // Keep the call for a potential side-effect of GetBuffer - var bytes = writeBuffer.GetBuffer (); - var length = (int)writeBuffer.Length; - if (request.ContentLength != -1 && request.ContentLength < length) { - nextReadCalled = true; - cnc.Close (true); - throw new WebException ("Specified Content-Length is less than the number of bytes to write", null, - WebExceptionStatus.ServerProtocolViolation, null); - } - - SetHeadersAsync (true, inner => { - if (inner.GotException) { - result.SetCompleted (inner.CompletedSynchronouslyPeek, inner.Exception); - return; - } - - if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100) { - result.SetCompleted (inner.CompletedSynchronouslyPeek); - return; - } - - if (!initRead) { - initRead = true; - cnc.InitRead (); - } - - if (length == 0) { - complete_request_written = true; - result.SetCompleted (inner.CompletedSynchronouslyPeek); - return; - } - - cnc.BeginWrite (request, bytes, 0, length, r => { - try { - complete_request_written = cnc.EndWrite (request, false, r); - result.SetCompleted (false); - } catch (Exception exc) { - result.SetCompleted (false, exc); - } - }, null); - }); - - return true; } internal void InternalClose () @@ -757,103 +232,26 @@ internal void InternalClose () disposed = true; } - internal bool GetResponseOnClose { - get; set; - } + protected abstract void Close_internal (ref bool disposed); public override void Close () { - if (GetResponseOnClose) { - if (disposed) - return; - disposed = true; - var response = (HttpWebResponse)request.GetResponse (); - response.ReadAll (); - response.Close (); - return; - } - - if (sendChunked) { - if (disposed) - return; - disposed = true; - if (!pending.WaitOne (WriteTimeout)) { - throw new WebException ("The operation has timed out.", WebExceptionStatus.Timeout); - } - byte [] chunk = Encoding.ASCII.GetBytes ("0\r\n\r\n"); - string err_msg = null; - cnc.Write (request, chunk, 0, chunk.Length, ref err_msg); - return; - } - - if (isRead) { - if (!nextReadCalled) { - CheckComplete (); - // If we have not read all the contents - if (!nextReadCalled) { - nextReadCalled = true; - cnc.Close (true); - } - } - return; - } else if (!allowBuffering) { - complete_request_written = true; - if (!initRead) { - initRead = true; - cnc.InitRead (); - } - return; - } - - if (disposed || requestWritten) - return; - - long length = request.ContentLength; - - if (!sendChunked && length != -1 && totalWritten != length) { - IOException io = new IOException ("Cannot close the stream until all bytes are written"); - nextReadCalled = true; - cnc.Close (true); - throw new WebException ("Request was cancelled.", WebExceptionStatus.RequestCanceled, WebExceptionInternalStatus.RequestFatal, io); - } - - // Commented out the next line to fix xamarin bug #1512 - //WriteRequest (); - disposed = true; - } - - internal void KillBuffer () - { - writeBuffer = null; + Close_internal (ref disposed); } public override long Seek (long a, SeekOrigin b) { throw new NotSupportedException (); } - + public override void SetLength (long a) { throw new NotSupportedException (); } - - public override bool CanSeek { - get { return false; } - } - - public override bool CanRead { - get { return !disposed && isRead; } - } - public override bool CanWrite { - get { return !disposed && !isRead; } - } - - public override long Length { + public override bool CanSeek { get { - if (!isRead) - throw new NotSupportedException (); - return stream_length; + return false; } } diff --git a/mcs/class/System/System.Net/WebConnectionTunnel.cs b/mcs/class/System/System.Net/WebConnectionTunnel.cs new file mode 100644 index 000000000000..826d59df922f --- /dev/null +++ b/mcs/class/System/System.Net/WebConnectionTunnel.cs @@ -0,0 +1,271 @@ +// +// System.Net.WebConnectionTunnel +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Martin Baulig +// +// (C) 2003 Ximian, Inc (http://www.ximian.com) +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) +// +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System.IO; +using System.Collections; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Diagnostics; +using Mono.Net.Security; + +namespace System.Net +{ + class WebConnectionTunnel + { + public HttpWebRequest Request { + get; + } + + public Uri ConnectUri { + get; + } + + public WebConnectionTunnel (HttpWebRequest request, Uri connectUri) + { + Request = request; + ConnectUri = connectUri; + } + + enum NtlmAuthState + { + None, + Challenge, + Response + } + + HttpWebRequest connectRequest; + NtlmAuthState ntlmAuthState; + + public bool Success { + get; + private set; + } + + public bool CloseConnection { + get; + private set; + } + + public int StatusCode { + get; + private set; + } + + public string StatusDescription { + get; + private set; + } + + public string[] Challenge { + get; + private set; + } + + public WebHeaderCollection Headers { + get; + private set; + } + + public Version ProxyVersion { + get; + private set; + } + + public byte[] Data { + get; + private set; + } + + internal async Task Initialize (Stream stream, CancellationToken cancellationToken) + { + StringBuilder sb = new StringBuilder (); + sb.Append ("CONNECT "); + sb.Append (Request.Address.Host); + sb.Append (':'); + sb.Append (Request.Address.Port); + sb.Append (" HTTP/"); + if (Request.ProtocolVersion == HttpVersion.Version11) + sb.Append ("1.1"); + else + sb.Append ("1.0"); + + sb.Append ("\r\nHost: "); + sb.Append (Request.Address.Authority); + + bool ntlm = false; + var challenge = Challenge; + Challenge = null; + var auth_header = Request.Headers["Proxy-Authorization"]; + bool have_auth = auth_header != null; + if (have_auth) { + sb.Append ("\r\nProxy-Authorization: "); + sb.Append (auth_header); + ntlm = auth_header.ToUpper ().Contains ("NTLM"); + } else if (challenge != null && StatusCode == 407) { + ICredentials creds = Request.Proxy.Credentials; + have_auth = true; + + if (connectRequest == null) { + // create a CONNECT request to use with Authenticate + connectRequest = (HttpWebRequest)WebRequest.Create ( + ConnectUri.Scheme + "://" + ConnectUri.Host + ":" + ConnectUri.Port + "/"); + connectRequest.Method = "CONNECT"; + connectRequest.Credentials = creds; + } + + if (creds != null) { + for (int i = 0; i < challenge.Length; i++) { + var auth = AuthenticationManager.Authenticate (challenge[i], connectRequest, creds); + if (auth == null) + continue; + ntlm = (auth.ModuleAuthenticationType == "NTLM"); + sb.Append ("\r\nProxy-Authorization: "); + sb.Append (auth.Message); + break; + } + } + } + + if (ntlm) { + sb.Append ("\r\nProxy-Connection: keep-alive"); + ntlmAuthState++; + } + + sb.Append ("\r\n\r\n"); + + StatusCode = 0; + byte[] connectBytes = Encoding.Default.GetBytes (sb.ToString ()); + await stream.WriteAsync (connectBytes, 0, connectBytes.Length, cancellationToken).ConfigureAwait (false); + + (Headers, Data, StatusCode) = await ReadHeaders (stream, cancellationToken).ConfigureAwait (false); + + if ((!have_auth || ntlmAuthState == NtlmAuthState.Challenge) && Headers != null && StatusCode == 407) { // Needs proxy auth + var connectionHeader = Headers["Connection"]; + if (!string.IsNullOrEmpty (connectionHeader) && connectionHeader.ToLower () == "close") { + // The server is requesting that this connection be closed + CloseConnection = true; + } + + Challenge = Headers.GetValues ("Proxy-Authenticate"); + Success = false; + } else { + Success = StatusCode == 200 && Headers != null; + } + + if (Challenge == null && (StatusCode == 401 || StatusCode == 407)) { + var response = new HttpWebResponse (ConnectUri, "CONNECT", (HttpStatusCode)StatusCode, Headers); + throw new WebException ( + StatusCode == 407 ? "(407) Proxy Authentication Required" : "(401) Unauthorized", + null, WebExceptionStatus.ProtocolError, response); + } + } + + async Task<(WebHeaderCollection, byte[], int)> ReadHeaders (Stream stream, CancellationToken cancellationToken) + { + byte[] retBuffer = null; + int status = 200; + + byte[] buffer = new byte[1024]; + MemoryStream ms = new MemoryStream (); + + while (true) { + cancellationToken.ThrowIfCancellationRequested (); + int n = await stream.ReadAsync (buffer, 0, 1024, cancellationToken).ConfigureAwait (false); + if (n == 0) + throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null); + + ms.Write (buffer, 0, n); + int start = 0; + string str = null; + bool gotStatus = false; + WebHeaderCollection headers = new WebHeaderCollection (); + while (WebConnection.ReadLine (ms.GetBuffer (), ref start, (int)ms.Length, ref str)) { + if (str == null) { + int contentLen; + var clengthHeader = headers["Content-Length"]; + if (string.IsNullOrEmpty (clengthHeader) || !int.TryParse (clengthHeader, out contentLen)) + contentLen = 0; + + if (ms.Length - start - contentLen > 0) { + // we've read more data than the response header and conents, + // give back extra data to the caller + retBuffer = new byte[ms.Length - start - contentLen]; + Buffer.BlockCopy (ms.GetBuffer (), start + contentLen, retBuffer, 0, retBuffer.Length); + } else { + // haven't read in some or all of the contents for the response, do so now + FlushContents (stream, contentLen - (int)(ms.Length - start)); + } + + return (headers, retBuffer, status); + } + + if (gotStatus) { + headers.Add (str); + continue; + } + + string[] parts = str.Split (' '); + if (parts.Length < 2) + throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null); + + if (String.Compare (parts[0], "HTTP/1.1", true) == 0) + ProxyVersion = HttpVersion.Version11; + else if (String.Compare (parts[0], "HTTP/1.0", true) == 0) + ProxyVersion = HttpVersion.Version10; + else + throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null); + + status = (int)UInt32.Parse (parts[1]); + if (parts.Length >= 3) + StatusDescription = String.Join (" ", parts, 2, parts.Length - 2); + + gotStatus = true; + } + } + } + + void FlushContents (Stream stream, int contentLength) + { + while (contentLength > 0) { + byte[] contentBuffer = new byte[contentLength]; + int bytesRead = stream.Read (contentBuffer, 0, contentLength); + if (bytesRead > 0) { + contentLength -= bytesRead; + } else { + break; + } + } + } + } +} \ No newline at end of file diff --git a/mcs/class/System/System.Net/WebOperation.cs b/mcs/class/System/System.Net/WebOperation.cs new file mode 100644 index 000000000000..0405c00c1392 --- /dev/null +++ b/mcs/class/System/System.Net/WebOperation.cs @@ -0,0 +1,344 @@ +// +// WebOperation.cs +// +// Author: +// Martin Baulig +// +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.IO; +using System.Collections; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Diagnostics; + +namespace System.Net +{ + class WebOperation + { + public HttpWebRequest Request { + get; + } + + public WebConnection Connection { + get; + private set; + } + + public ServicePoint ServicePoint { + get; + private set; + } + + public BufferOffsetSize WriteBuffer { + get; + } + + public bool IsNtlmChallenge { + get; + } + +#if MONO_WEB_DEBUG + static int nextID; + internal readonly int ID = ++nextID; +#else + internal readonly int ID; +#endif + + public WebOperation (HttpWebRequest request, BufferOffsetSize writeBuffer, bool isNtlmChallenge, CancellationToken cancellationToken) + { + Request = request; + WriteBuffer = writeBuffer; + IsNtlmChallenge = isNtlmChallenge; + cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken); + requestTask = new TaskCompletionSource (); + requestWrittenTask = new TaskCompletionSource (); + completeResponseReadTask = new TaskCompletionSource (); + responseTask = new TaskCompletionSource (); + finishedTask = new TaskCompletionSource<(bool, WebOperation)> (); + } + + CancellationTokenSource cts; + TaskCompletionSource requestTask; + TaskCompletionSource requestWrittenTask; + TaskCompletionSource responseTask; + TaskCompletionSource completeResponseReadTask; + TaskCompletionSource<(bool, WebOperation)> finishedTask; + WebRequestStream writeStream; + WebResponseStream responseStream; + ExceptionDispatchInfo disposedInfo; + ExceptionDispatchInfo closedInfo; + WebOperation priorityRequest; + volatile bool finishedReading; + int requestSent; + + public bool Aborted { + get { + if (disposedInfo != null || Request.Aborted) + return true; + if (cts != null && cts.IsCancellationRequested) + return true; + return false; + } + } + + public bool Closed { + get { + return Aborted || closedInfo != null; + } + } + + public void Abort () + { + var (exception, disposed) = SetDisposed (ref disposedInfo); + if (!disposed) + return; + cts?.Cancel (); + SetCanceled (); + Close (); + } + + public void Close () + { + var (exception, closed) = SetDisposed (ref closedInfo); + if (!closed) + return; + + var stream = Interlocked.Exchange (ref writeStream, null); + if (stream != null) { + try { + stream.Close (); + } catch { } + } + } + + void SetCanceled () + { + requestTask.TrySetCanceled (); + requestWrittenTask.TrySetCanceled (); + responseTask.TrySetCanceled (); + completeResponseReadTask.TrySetCanceled (); + } + + void SetError (Exception error) + { + requestTask.TrySetException (error); + requestWrittenTask.TrySetException (error); + responseTask.TrySetException (error); + completeResponseReadTask.TrySetException (error); + } + + (ExceptionDispatchInfo, bool) SetDisposed (ref ExceptionDispatchInfo field) + { + var wexc = new WebException (SR.GetString (SR.net_webstatus_RequestCanceled), WebExceptionStatus.RequestCanceled); + var exception = ExceptionDispatchInfo.Capture (wexc); + var old = Interlocked.CompareExchange (ref field, exception, null); + return (old ?? exception, old == null); + } + + internal void ThrowIfDisposed () + { + ThrowIfDisposed (CancellationToken.None); + } + + internal void ThrowIfDisposed (CancellationToken cancellationToken) + { + if (Aborted || cancellationToken.IsCancellationRequested) + ThrowDisposed (ref disposedInfo); + } + + internal void ThrowIfClosedOrDisposed () + { + ThrowIfClosedOrDisposed (CancellationToken.None); + } + + internal void ThrowIfClosedOrDisposed (CancellationToken cancellationToken) + { + if (Closed || cancellationToken.IsCancellationRequested) + ThrowDisposed (ref closedInfo); + } + + void ThrowDisposed (ref ExceptionDispatchInfo field) + { + var (exception, disposed) = SetDisposed (ref field); + if (disposed) + cts?.Cancel (); + exception.Throw (); + } + + internal void RegisterRequest (ServicePoint servicePoint, WebConnection connection) + { + if (servicePoint == null) + throw new ArgumentNullException (nameof (servicePoint)); + if (connection == null) + throw new ArgumentNullException (nameof (connection)); + + lock (this) { + if (Interlocked.CompareExchange (ref requestSent, 1, 0) != 0) + throw new InvalidOperationException ("Invalid nested call."); + ServicePoint = servicePoint; + Connection = connection; + } + + cts.Token.Register (() => { + Request.FinishedReading = true; + SetDisposed (ref disposedInfo); + }); + } + + public void SetPriorityRequest (WebOperation operation) + { + lock (this) { + if (requestSent != 1 || ServicePoint == null || finishedReading) + throw new InvalidOperationException ("Should never happen."); + if (Interlocked.CompareExchange (ref priorityRequest, operation, null) != null) + throw new InvalidOperationException ("Invalid nested request."); + } + } + + public Task GetRequestStream () + { + return requestTask.Task; + } + + public Task WaitUntilRequestWritten () + { + return requestWrittenTask.Task; + } + + public WebRequestStream WriteStream { + get { + ThrowIfDisposed (); + return writeStream; + } + } + + public Task GetResponseStream () + { + return responseTask.Task; + } + + internal async Task<(bool, WebOperation)> WaitForCompletion (bool ignoreErrors) + { + try { + return await finishedTask.Task.ConfigureAwait (false); + } catch { + if (ignoreErrors) + return (false, null); + throw; + } + } + + internal async void Run () + { + try { + FinishReading (); + + ThrowIfClosedOrDisposed (); + + var requestStream = await Connection.InitConnection (this, cts.Token).ConfigureAwait (false); + + ThrowIfClosedOrDisposed (); + + writeStream = requestStream; + + await requestStream.Initialize (cts.Token).ConfigureAwait (false); + + ThrowIfClosedOrDisposed (); + + requestTask.TrySetResult (requestStream); + + var stream = new WebResponseStream (requestStream); + responseStream = stream; + + await stream.InitReadAsync (cts.Token).ConfigureAwait (false); + + responseTask.TrySetResult (stream); + } catch (OperationCanceledException) { + SetCanceled (); + } catch (Exception e) { + SetError (e); + } + } + + async void FinishReading () + { + bool ok = false; + Exception error = null; + + try { + ok = await completeResponseReadTask.Task.ConfigureAwait (false); + } catch (Exception e) { + error = e; + } + + WebResponseStream stream; + WebOperation next; + + lock (this) { + finishedReading = true; + stream = Interlocked.Exchange (ref responseStream, null); + next = Interlocked.Exchange (ref priorityRequest, null); + Request.FinishedReading = true; + } + + if (error != null) { + if (next != null) + next.SetError (error); + finishedTask.TrySetException (error); + return; + } + + WebConnection.Debug ($"WO FINISH READING: Cnc={Connection?.ID} Op={ID} ok={ok} error={error != null} stream={stream != null} next={next != null}"); + + var keepAlive = !Aborted && ok && (stream?.KeepAlive ?? false); + if (next != null && next.Aborted) { + next = null; + keepAlive = false; + } + + finishedTask.TrySetResult ((keepAlive, next)); + + WebConnection.Debug ($"WO FINISH READING DONE: Cnc={Connection.ID} Op={ID} - {keepAlive} next={next?.ID}"); + } + + internal void CompleteRequestWritten (WebRequestStream stream, Exception error = null) + { + WebConnection.Debug ($"WO COMPLETE REQUEST WRITTEN: Op={ID} {error != null}"); + + if (error != null) + SetError (error); + else + requestWrittenTask.TrySetResult (stream); + } + + internal void CompleteResponseRead (bool ok, Exception error = null) + { + WebConnection.Debug ($"WO COMPLETE RESPONSE READ: Op={ID} {ok} {error?.GetType ()}"); + + if (error != null) + completeResponseReadTask.TrySetException (error); + else + completeResponseReadTask.TrySetResult (ok); + } + } +} diff --git a/mcs/class/System/System.Net/WebRequestStream.cs b/mcs/class/System/System.Net/WebRequestStream.cs new file mode 100644 index 000000000000..5077ac56cd00 --- /dev/null +++ b/mcs/class/System/System.Net/WebRequestStream.cs @@ -0,0 +1,437 @@ +// +// WebRequestStream.cs +// +// Author: +// Martin Baulig +// +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Net.Sockets; + +namespace System.Net +{ + class WebRequestStream : WebConnectionStream + { + static byte[] crlf = new byte[] { 13, 10 }; + MemoryStream writeBuffer; + bool requestWritten; + bool allowBuffering; + bool sendChunked; + WebCompletionSource pendingWrite; + long totalWritten; + byte[] headers; + bool headersSent; + int completeRequestWritten; + int chunkTrailerWritten; + + internal readonly string ME; + + public WebRequestStream (WebConnection connection, WebOperation operation, + Stream stream, WebConnectionTunnel tunnel) + : base (connection, operation, stream) + { + allowBuffering = operation.Request.InternalAllowBuffering; + sendChunked = operation.Request.SendChunked && operation.WriteBuffer == null; + if (!sendChunked && allowBuffering && operation.WriteBuffer == null) + writeBuffer = new MemoryStream (); + + KeepAlive = Request.KeepAlive; + if (tunnel?.ProxyVersion != null && tunnel?.ProxyVersion != HttpVersion.Version11) + KeepAlive = false; + +#if MONO_WEB_DEBUG + ME = $"WRQ(Cnc={Connection.ID}, Op={Operation.ID})"; +#endif + } + + public bool KeepAlive { + get; + } + + public override long Length { + get { + throw new NotSupportedException (); + } + } + + public override bool CanRead => false; + + public override bool CanWrite => true; + + internal bool SendChunked { + get { return sendChunked; } + set { sendChunked = value; } + } + + internal bool HasWriteBuffer { + get { + return Operation.WriteBuffer != null || writeBuffer != null; + } + } + + internal int WriteBufferLength { + get { + if (Operation.WriteBuffer != null) + return Operation.WriteBuffer.Size; + if (writeBuffer != null) + return (int)writeBuffer.Length; + return -1; + } + } + + internal BufferOffsetSize GetWriteBuffer () + { + if (Operation.WriteBuffer != null) + return Operation.WriteBuffer; + if (writeBuffer == null || writeBuffer.Length == 0) + return null; + var buffer = writeBuffer.GetBuffer (); + return new BufferOffsetSize (buffer, 0, (int)writeBuffer.Length, false); + } + + async Task FinishWriting (CancellationToken cancellationToken) + { + if (Interlocked.CompareExchange (ref completeRequestWritten, 1, 0) != 0) + return; + + WebConnection.Debug ($"{ME} FINISH WRITING: {sendChunked}"); + try { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + if (sendChunked) + await WriteChunkTrailer_inner (cancellationToken).ConfigureAwait (false); + } catch (Exception ex) { + Operation.CompleteRequestWritten (this, ex); + throw; + } finally { + WebConnection.Debug ($"{ME} FINISH WRITING DONE"); + } + + Operation.CompleteRequestWritten (this); + } + + public override async Task WriteAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} WRITE ASYNC: {buffer.Length}/{offset}/{size}"); + + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + if (Operation.WriteBuffer != null) + throw new InvalidOperationException (); + + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); + + int length = buffer.Length; + if (offset < 0 || length < offset) + throw new ArgumentOutOfRangeException (nameof (offset)); + if (size < 0 || (length - offset) < size) + throw new ArgumentOutOfRangeException (nameof (size)); + + var completion = new WebCompletionSource (); + if (Interlocked.CompareExchange (ref pendingWrite, completion, null) != null) + throw new InvalidOperationException (SR.GetString (SR.net_repcall)); + + try { + await ProcessWrite (buffer, offset, size, cancellationToken).ConfigureAwait (false); + + WebConnection.Debug ($"{ME} WRITE ASYNC #1: {allowBuffering} {sendChunked} {Request.ContentLength} {totalWritten}"); + + if (Request.ContentLength > 0 && totalWritten == Request.ContentLength) + await FinishWriting (cancellationToken); + + pendingWrite = null; + completion.TrySetCompleted (); + } catch (Exception ex) { + KillBuffer (); + closed = true; + + WebConnection.Debug ($"{ME} WRITE ASYNC EX: {ex.Message}"); + + if (ex is SocketException) + ex = new IOException ("Error writing request", ex); + + Operation.CompleteRequestWritten (this, ex); + + pendingWrite = null; + completion.TrySetException (ex); + throw; + } + } + + async Task ProcessWrite (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + if (sendChunked) { + requestWritten = true; + + string cSize = String.Format ("{0:X}\r\n", size); + byte[] head = Encoding.ASCII.GetBytes (cSize); + int chunkSize = 2 + size + head.Length; + byte[] newBuffer = new byte[chunkSize]; + Buffer.BlockCopy (head, 0, newBuffer, 0, head.Length); + Buffer.BlockCopy (buffer, offset, newBuffer, head.Length, size); + Buffer.BlockCopy (crlf, 0, newBuffer, head.Length + size, crlf.Length); + + if (allowBuffering) { + if (writeBuffer == null) + writeBuffer = new MemoryStream (); + writeBuffer.Write (buffer, offset, size); + } + + totalWritten += size; + + buffer = newBuffer; + offset = 0; + size = chunkSize; + } else { + CheckWriteOverflow (Request.ContentLength, totalWritten, size); + + if (allowBuffering) { + if (writeBuffer == null) + writeBuffer = new MemoryStream (); + writeBuffer.Write (buffer, offset, size); + totalWritten += size; + + if (Request.ContentLength <= 0 || totalWritten < Request.ContentLength) + return; + + requestWritten = true; + buffer = writeBuffer.GetBuffer (); + offset = 0; + size = (int)totalWritten; + } else { + totalWritten += size; + } + } + + try { + await InnerStream.WriteAsync (buffer, offset, size, cancellationToken).ConfigureAwait (false); + } catch { + if (!IgnoreIOErrors) + throw; + } + } + + void CheckWriteOverflow (long contentLength, long totalWritten, long size) + { + if (contentLength == -1) + return; + + long avail = contentLength - totalWritten; + if (size > avail) { + KillBuffer (); + closed = true; + var throwMe = new ProtocolViolationException ( + "The number of bytes to be written is greater than " + + "the specified ContentLength."); + Operation.CompleteRequestWritten (this, throwMe); + throw throwMe; + } + } + + internal async Task Initialize (CancellationToken cancellationToken) + { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + WebConnection.Debug ($"{ME} INIT: {Operation.WriteBuffer != null}"); + + if (Operation.WriteBuffer != null) { + if (Operation.IsNtlmChallenge) + Request.InternalContentLength = 0; + else + Request.InternalContentLength = Operation.WriteBuffer.Size; + } + + await SetHeadersAsync (false, cancellationToken).ConfigureAwait (false); + + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + if (Operation.WriteBuffer != null && !Operation.IsNtlmChallenge) { + await WriteRequestAsync (cancellationToken); + Close (); + } + } + + async Task SetHeadersAsync (bool setInternalLength, CancellationToken cancellationToken) + { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + if (headersSent) + return; + + string method = Request.Method; + bool no_writestream = (method == "GET" || method == "CONNECT" || method == "HEAD" || + method == "TRACE"); + bool webdav = (method == "PROPFIND" || method == "PROPPATCH" || method == "MKCOL" || + method == "COPY" || method == "MOVE" || method == "LOCK" || + method == "UNLOCK"); + + if (Operation.IsNtlmChallenge) + no_writestream = true; + + if (setInternalLength && !no_writestream && HasWriteBuffer) + Request.InternalContentLength = WriteBufferLength; + + bool has_content = !no_writestream && (!HasWriteBuffer || Request.ContentLength > -1); + if (!(sendChunked || has_content || no_writestream || webdav)) + return; + + headersSent = true; + headers = Request.GetRequestHeaders (); + + WebConnection.Debug ($"{ME} SET HEADERS: {Request.ContentLength}"); + + try { + await InnerStream.WriteAsync (headers, 0, headers.Length, cancellationToken).ConfigureAwait (false); + var cl = Request.ContentLength; + if (!sendChunked && cl == 0) + requestWritten = true; + } catch (Exception e) { + if (e is WebException || e is OperationCanceledException) + throw; + throw new WebException ("Error writing headers", WebExceptionStatus.SendFailure, WebExceptionInternalStatus.RequestFatal, e); + } + } + + internal async Task WriteRequestAsync (CancellationToken cancellationToken) + { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + WebConnection.Debug ($"{ME} WRITE REQUEST: {requestWritten} {sendChunked} {allowBuffering} {HasWriteBuffer}"); + + if (requestWritten) + return; + + requestWritten = true; + if (sendChunked || !HasWriteBuffer) + return; + + BufferOffsetSize buffer = GetWriteBuffer (); + if (buffer != null && !Operation.IsNtlmChallenge && Request.ContentLength != -1 && Request.ContentLength < buffer.Size) { + closed = true; + var throwMe = new WebException ("Specified Content-Length is less than the number of bytes to write", null, + WebExceptionStatus.ServerProtocolViolation, null); + Operation.CompleteRequestWritten (this, throwMe); + throw throwMe; + } + + await SetHeadersAsync (true, cancellationToken).ConfigureAwait (false); + + WebConnection.Debug ($"{ME} WRITE REQUEST #1: {buffer != null}"); + + Operation.ThrowIfClosedOrDisposed (cancellationToken); + if (buffer != null && buffer.Size > 0) + await InnerStream.WriteAsync (buffer.Buffer, 0, buffer.Size, cancellationToken); + + await FinishWriting (cancellationToken); + } + + async Task WriteChunkTrailer_inner (CancellationToken cancellationToken) + { + if (Interlocked.CompareExchange (ref chunkTrailerWritten, 1, 0) != 0) + return; + Operation.ThrowIfClosedOrDisposed (cancellationToken); + byte[] chunk = Encoding.ASCII.GetBytes ("0\r\n\r\n"); + await InnerStream.WriteAsync (chunk, 0, chunk.Length, cancellationToken).ConfigureAwait (false); + } + + async Task WriteChunkTrailer () + { + using (var cts = new CancellationTokenSource ()) { + cts.CancelAfter (WriteTimeout); + var timeoutTask = Task.Delay (WriteTimeout); + while (true) { + var completion = new WebCompletionSource (); + var oldCompletion = Interlocked.CompareExchange (ref pendingWrite, completion, null); + if (oldCompletion == null) + break; + var oldWriteTask = oldCompletion.WaitForCompletion (true); + var ret = await Task.WhenAny (timeoutTask, oldWriteTask).ConfigureAwait (false); + if (ret == timeoutTask) + throw new WebException ("The operation has timed out.", WebExceptionStatus.Timeout); + } + + try { + await WriteChunkTrailer_inner (cts.Token).ConfigureAwait (false); + } catch { + // Intentionally eating exceptions. + } finally { + pendingWrite = null; + } + } + } + + internal void KillBuffer () + { + writeBuffer = null; + } + + public override Task ReadAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + return Task.FromException (new NotSupportedException (SR.net_writeonlystream)); + } + + protected override void Close_internal (ref bool disposed) + { + WebConnection.Debug ($"{ME} CLOSE: {disposed} {requestWritten} {allowBuffering}"); + + if (disposed) + return; + disposed = true; + + if (sendChunked) { + // Don't use FinishWriting() here, we need to block on 'pendingWrite' to ensure that + // any pending WriteAsync() has been completed. + // + // FIXME: I belive .NET simply aborts if you call Close() or Dispose() while writing, + // need to check this. 2017/07/17 Martin. + WriteChunkTrailer ().Wait (); + return; + } + + if (!allowBuffering || requestWritten) { + Operation.CompleteRequestWritten (this); + return; + } + + long length = Request.ContentLength; + + if (!sendChunked && !Operation.IsNtlmChallenge && length != -1 && totalWritten != length) { + IOException io = new IOException ("Cannot close the stream until all bytes are written"); + closed = true; + disposed = true; + var throwMe = new WebException ("Request was cancelled.", WebExceptionStatus.RequestCanceled, WebExceptionInternalStatus.RequestFatal, io); + Operation.CompleteRequestWritten (this, throwMe); + throw throwMe; + } + + // Commented out the next line to fix xamarin bug #1512 + //WriteRequest (); + disposed = true; + Operation.CompleteRequestWritten (this); + } + } +} diff --git a/mcs/class/System/System.Net/WebResponseStream.cs b/mcs/class/System/System.Net/WebResponseStream.cs new file mode 100644 index 000000000000..d6fce9fe5d82 --- /dev/null +++ b/mcs/class/System/System.Net/WebResponseStream.cs @@ -0,0 +1,734 @@ +// +// WebResponseStream.cs +// +// Author: +// Martin Baulig +// +// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.ExceptionServices; +using System.Net.Sockets; + +namespace System.Net +{ + class WebResponseStream : WebConnectionStream + { + BufferOffsetSize readBuffer; + long contentLength; + long totalRead; + bool nextReadCalled; + int stream_length; // -1 when CL not present + WebCompletionSource pendingRead; + object locker = new object (); + int nestedRead; + bool read_eof; + + public WebRequestStream RequestStream { + get; + } + + public WebHeaderCollection Headers { + get; + private set; + } + + public HttpStatusCode StatusCode { + get; + private set; + } + + public string StatusDescription { + get; + private set; + } + + public Version Version { + get; + private set; + } + + public bool KeepAlive { + get; + private set; + } + + internal readonly string ME; + + public WebResponseStream (WebRequestStream request) + : base (request.Connection, request.Operation, request.InnerStream) + { + RequestStream = request; + request.InnerStream.ReadTimeout = ReadTimeout; + +#if MONO_WEB_DEBUG + ME = $"WRP(Cnc={Connection.ID}, Op={Operation.ID})"; +#endif + } + + public override long Length { + get { + return stream_length; + } + } + + public override bool CanRead => true; + + public override bool CanWrite => false; + + protected bool ChunkedRead { + get; + private set; + } + + protected MonoChunkStream ChunkStream { + get; + private set; + } + + public override async Task ReadAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} READ ASYNC"); + + cancellationToken.ThrowIfCancellationRequested (); + + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); + + int length = buffer.Length; + if (offset < 0 || length < offset) + throw new ArgumentOutOfRangeException (nameof (offset)); + if (size < 0 || (length - offset) < size) + throw new ArgumentOutOfRangeException (nameof (size)); + + if (Interlocked.CompareExchange (ref nestedRead, 1, 0) != 0) + throw new InvalidOperationException ("Invalid nested call."); + + var completion = new WebCompletionSource (); + while (!cancellationToken.IsCancellationRequested) { + /* + * 'currentRead' is set by ReadAllAsync(). + */ + var oldCompletion = Interlocked.CompareExchange (ref pendingRead, completion, null); + WebConnection.Debug ($"{ME} READ ASYNC #1: {oldCompletion != null}"); + if (oldCompletion == null) + break; + await oldCompletion.WaitForCompletion (true).ConfigureAwait (false); + } + + WebConnection.Debug ($"{ME} READ ASYNC #2: {totalRead} {contentLength}"); + + int oldBytes = 0, nbytes = 0; + Exception throwMe = null; + + try { + // FIXME: NetworkStream.ReadAsync() does not support cancellation. + (oldBytes, nbytes) = await HttpWebRequest.RunWithTimeout ( + ct => ProcessRead (buffer, offset, size, ct), + ReadTimeout, () => { + Operation.Abort (); + InnerStream.Dispose (); + }).ConfigureAwait (false); + } catch (Exception e) { + throwMe = GetReadException (WebExceptionStatus.ReceiveFailure, e, "ReadAsync"); + } + + WebConnection.Debug ($"{ME} READ ASYNC #3: {totalRead} {contentLength} - {oldBytes} {nbytes} {throwMe?.Message}"); + + if (throwMe != null) { + lock (locker) { + completion.TrySetException (throwMe); + pendingRead = null; + nestedRead = 0; + } + + closed = true; + Operation.CompleteResponseRead (false, throwMe); + throw throwMe; + } + + lock (locker) { + pendingRead.TrySetCompleted (); + pendingRead = null; + nestedRead = 0; + } + + if (totalRead >= contentLength && !nextReadCalled) { + WebConnection.Debug ($"{ME} READ ASYNC - READ COMPLETE: {oldBytes} {nbytes} - {totalRead} {contentLength} {nextReadCalled}"); + if (!nextReadCalled) { + nextReadCalled = true; + Operation.CompleteResponseRead (true); + } + } + + return oldBytes + nbytes; + } + + async Task<(int, int)> ProcessRead (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} PROCESS READ: {totalRead} {contentLength}"); + + cancellationToken.ThrowIfCancellationRequested (); + if (totalRead >= contentLength) { + read_eof = true; + contentLength = totalRead; + return (0, 0); + } + + int oldBytes = 0; + int remaining = readBuffer?.Size ?? 0; + if (remaining > 0) { + int copy = (remaining > size) ? size : remaining; + Buffer.BlockCopy (readBuffer.Buffer, readBuffer.Offset, buffer, offset, copy); + readBuffer.Offset += copy; + readBuffer.Size -= copy; + offset += copy; + size -= copy; + totalRead += copy; + if (totalRead >= contentLength) { + contentLength = totalRead; + read_eof = true; + } + if (size == 0 || totalRead >= contentLength) + return (0, copy); + oldBytes = copy; + } + + if (contentLength != Int64.MaxValue && contentLength - totalRead < size) + size = (int)(contentLength - totalRead); + + WebConnection.Debug ($"{ME} PROCESS READ #1: {oldBytes} {size} {read_eof}"); + + if (read_eof) { + contentLength = totalRead; + return (oldBytes, 0); + } + + var ret = await InnerReadAsync (buffer, offset, size, cancellationToken).ConfigureAwait (false); + + if (ret <= 0) { + read_eof = true; + contentLength = totalRead; + return (oldBytes, 0); + } + + totalRead += ret; + return (oldBytes, ret); + } + + internal async Task InnerReadAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} INNER READ ASYNC"); + + Operation.ThrowIfDisposed (cancellationToken); + + int nbytes = 0; + bool done = false; + + if (!ChunkedRead || (!ChunkStream.DataAvailable && ChunkStream.WantMore)) { + nbytes = await InnerStream.ReadAsync (buffer, offset, size, cancellationToken).ConfigureAwait (false); + WebConnection.Debug ($"{ME} INNER READ ASYNC #1: {nbytes} {ChunkedRead}"); + if (!ChunkedRead) + return nbytes; + done = nbytes == 0; + } + + try { + ChunkStream.WriteAndReadBack (buffer, offset, size, ref nbytes); + WebConnection.Debug ($"{ME} INNER READ ASYNC #1: {done} {nbytes} {ChunkStream.WantMore}"); + if (!done && nbytes == 0 && ChunkStream.WantMore) + nbytes = await EnsureReadAsync (buffer, offset, size, cancellationToken).ConfigureAwait (false); + } catch (Exception e) { + if (e is WebException || e is OperationCanceledException) + throw; + throw new WebException ("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null); + } + + if ((done || nbytes == 0) && ChunkStream.ChunkLeft != 0) { + // HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead"); + throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null); + } + + return nbytes; + } + + async Task EnsureReadAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + byte[] morebytes = null; + int nbytes = 0; + while (nbytes == 0 && ChunkStream.WantMore && !cancellationToken.IsCancellationRequested) { + int localsize = ChunkStream.ChunkLeft; + if (localsize <= 0) // not read chunk size yet + localsize = 1024; + else if (localsize > 16384) + localsize = 16384; + + if (morebytes == null || morebytes.Length < localsize) + morebytes = new byte[localsize]; + + int nread = await InnerStream.ReadAsync (morebytes, 0, localsize, cancellationToken).ConfigureAwait (false); + if (nread <= 0) + return 0; // Error + + ChunkStream.Write (morebytes, 0, nread); + nbytes += ChunkStream.Read (buffer, offset + nbytes, size - nbytes); + } + + return nbytes; + } + + bool CheckAuthHeader (string headerName) + { + var authHeader = Headers[headerName]; + return (authHeader != null && authHeader.IndexOf ("NTLM", StringComparison.Ordinal) != -1); + } + + bool IsNtlmAuth () + { + bool isProxy = (Request.Proxy != null && !Request.Proxy.IsBypassed (Request.Address)); + if (isProxy && CheckAuthHeader ("Proxy-Authenticate")) + return true; + return CheckAuthHeader ("WWW-Authenticate"); + } + + bool ExpectContent { + get { + if (Request.Method == "HEAD") + return false; + return ((int)StatusCode >= 200 && (int)StatusCode != 204 && (int)StatusCode != 304); + } + } + + async Task Initialize (BufferOffsetSize buffer, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} INIT: status={(int)StatusCode} bos={buffer.Offset}/{buffer.Size}"); + + string contentType = Headers["Transfer-Encoding"]; + bool chunkedRead = (contentType != null && contentType.IndexOf ("chunked", StringComparison.OrdinalIgnoreCase) != -1); + string clength = Headers["Content-Length"]; + if (!chunkedRead && !string.IsNullOrEmpty (clength)) { + if (!long.TryParse (clength, out contentLength)) + contentLength = Int64.MaxValue; + } else { + contentLength = Int64.MaxValue; + } + + if (Version == HttpVersion.Version11 && RequestStream.KeepAlive) { + KeepAlive = true; + var cncHeader = Headers[ServicePoint.UsesProxy ? "Proxy-Connection" : "Connection"]; + if (cncHeader != null) { + cncHeader = cncHeader.ToLower (); + KeepAlive = cncHeader.IndexOf ("keep-alive", StringComparison.Ordinal) != -1; + if (cncHeader.IndexOf ("close", StringComparison.Ordinal) != -1) + KeepAlive = false; + } + } + + // Negative numbers? + if (!Int32.TryParse (clength, out stream_length)) + stream_length = -1; + + string me = "WebResponseStream.Initialize()"; + string tencoding = null; + if (ExpectContent) + tencoding = Headers["Transfer-Encoding"]; + + ChunkedRead = (tencoding != null && tencoding.IndexOf ("chunked", StringComparison.OrdinalIgnoreCase) != -1); + if (!ChunkedRead) { + readBuffer = buffer; + try { + if (contentLength > 0 && readBuffer.Size >= contentLength) { + if (!IsNtlmAuth ()) + await ReadAllAsync (false, cancellationToken).ConfigureAwait (false); + } + } catch (Exception e) { + throw GetReadException (WebExceptionStatus.ReceiveFailure, e, me); + } + } else if (ChunkStream == null) { + try { + ChunkStream = new MonoChunkStream (buffer.Buffer, buffer.Offset, buffer.Offset + buffer.Size, Headers); + } catch (Exception e) { + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, e, me); + } + } else { + ChunkStream.ResetBuffer (); + try { + ChunkStream.Write (buffer.Buffer, buffer.Offset, buffer.Size); + } catch (Exception e) { + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, e, me); + } + } + + WebConnection.Debug ($"{ME} INIT #1: - {ExpectContent} {closed} {nextReadCalled}"); + + if (!ExpectContent) { + if (!closed && !nextReadCalled) { + if (contentLength == Int64.MaxValue) + contentLength = 0; + nextReadCalled = true; + } + Operation.CompleteResponseRead (true); + } + } + + internal async Task ReadAllAsync (bool resending, CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} READ ALL ASYNC: resending={resending} eof={read_eof} total={totalRead} " + + "length={contentLength} nextReadCalled={nextReadCalled}"); + if (read_eof || totalRead >= contentLength || nextReadCalled) { + if (!nextReadCalled) { + nextReadCalled = true; + Operation.CompleteResponseRead (true); + } + return; + } + + var timeoutTask = Task.Delay (ReadTimeout); + var completion = new WebCompletionSource (); + while (true) { + /* + * 'currentRead' is set by ReadAsync(). + */ + cancellationToken.ThrowIfCancellationRequested (); + var oldCompletion = Interlocked.CompareExchange (ref pendingRead, completion, null); + if (oldCompletion == null) + break; + + // ReadAsync() is in progress. + var oldReadTask = oldCompletion.WaitForCompletion (true); + var anyTask = await Task.WhenAny (oldReadTask, timeoutTask).ConfigureAwait (false); + if (anyTask == timeoutTask) + throw new WebException ("The operation has timed out.", WebExceptionStatus.Timeout); + } + + WebConnection.Debug ($"{ME} READ ALL ASYNC #1"); + + cancellationToken.ThrowIfCancellationRequested (); + + try { + if (totalRead >= contentLength) + return; + + byte[] b = null; + int new_size; + + if (contentLength == Int64.MaxValue && !ChunkedRead) { + WebConnection.Debug ($"{ME} READ ALL ASYNC - NEITHER LENGTH NOR CHUNKED"); + /* + * This is a violation of the HTTP Spec - the server neither send a + * "Content-Length:" nor a "Transfer-Encoding: chunked" header. + * + * When we're redirecting or resending for NTLM, then we can simply close + * the connection here. + * + * However, if it's the final reply, then we need to try our best to read it. + */ + if (resending) { + Close (); + return; + } + KeepAlive = false; + } + + if (contentLength == Int64.MaxValue) { + MemoryStream ms = new MemoryStream (); + BufferOffsetSize buffer = null; + if (readBuffer != null && readBuffer.Size > 0) { + ms.Write (readBuffer.Buffer, readBuffer.Offset, readBuffer.Size); + readBuffer.Offset = 0; + readBuffer.Size = readBuffer.Buffer.Length; + if (readBuffer.Buffer.Length >= 8192) + buffer = readBuffer; + } + + if (buffer == null) + buffer = new BufferOffsetSize (new byte[8192], false); + + int read; + while ((read = await InnerReadAsync (buffer.Buffer, buffer.Offset, buffer.Size, cancellationToken)) != 0) + ms.Write (buffer.Buffer, buffer.Offset, read); + + new_size = (int)ms.Length; + contentLength = new_size; + readBuffer = new BufferOffsetSize (ms.GetBuffer (), 0, new_size, false); + } else { + new_size = (int)(contentLength - totalRead); + b = new byte[new_size]; + int readSize = 0; + if (readBuffer != null && readBuffer.Size > 0) { + readSize = readBuffer.Size; + if (readSize > new_size) + readSize = new_size; + + Buffer.BlockCopy (readBuffer.Buffer, readBuffer.Offset, b, 0, readSize); + } + + int remaining = new_size - readSize; + int r = -1; + while (remaining > 0 && r != 0) { + r = await InnerReadAsync (b, readSize, remaining, cancellationToken); + remaining -= r; + readSize += r; + } + } + + readBuffer = new BufferOffsetSize (b, 0, new_size, false); + totalRead = 0; + nextReadCalled = true; + completion.TrySetCompleted (); + } catch (Exception ex) { + WebConnection.Debug ($"{ME} READ ALL ASYNC EX: {ex.Message}"); + completion.TrySetException (ex); + throw; + } finally { + WebConnection.Debug ($"{ME} READ ALL ASYNC #2"); + pendingRead = null; + } + + Operation.CompleteResponseRead (true); + } + + public override Task WriteAsync (byte[] buffer, int offset, int size, CancellationToken cancellationToken) + { + return Task.FromException (new NotSupportedException (SR.net_readonlystream)); + } + + protected override void Close_internal (ref bool disposed) + { + WebConnection.Debug ($"{ME} CLOSE: {disposed} {closed} {nextReadCalled}"); + if (!closed && !nextReadCalled) { + nextReadCalled = true; + if (totalRead >= contentLength) { + disposed = true; + Operation.CompleteResponseRead (true); + } else { + // If we have not read all the contents + closed = true; + disposed = true; + Operation.CompleteResponseRead (false); + } + } + } + + WebException GetReadException (WebExceptionStatus status, Exception error, string where) + { + error = GetException (error); + string msg = $"Error getting response stream ({where}): {status}"; + if (error == null) + return new WebException ($"Error getting response stream ({where}): {status}", status); + if (error is WebException wexc) + return wexc; + if (Operation.Aborted || error is OperationCanceledException || error is ObjectDisposedException) + return HttpWebRequest.CreateRequestAbortedException (); + return new WebException ($"Error getting response stream ({where}): {status} {error.Message}", status, + WebExceptionInternalStatus.RequestFatal, error); + } + + internal async Task InitReadAsync (CancellationToken cancellationToken) + { + WebConnection.Debug ($"{ME} INIT READ ASYNC"); + + var buffer = new BufferOffsetSize (new byte[4096], false); + var state = ReadState.None; + int position = 0; + + while (true) { + Operation.ThrowIfClosedOrDisposed (cancellationToken); + + WebConnection.Debug ($"{ME} INIT READ ASYNC LOOP: {state} {position} - {buffer.Offset}/{buffer.Size}"); + + var nread = await InnerStream.ReadAsync ( + buffer.Buffer, buffer.Offset, buffer.Size, cancellationToken).ConfigureAwait (false); + + WebConnection.Debug ($"{ME} INIT READ ASYNC LOOP #1: {state} {position} - {buffer.Offset}/{buffer.Size} - {nread}"); + + if (nread == 0) + throw GetReadException (WebExceptionStatus.ReceiveFailure, null, "ReadDoneAsync2"); + + if (nread < 0) + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, null, "ReadDoneAsync3"); + + buffer.Offset += nread; + buffer.Size -= nread; + + if (state == ReadState.None) { + try { + var oldPos = position; + if (!GetResponse (buffer, ref position, ref state)) + position = oldPos; + } catch (Exception e) { + WebConnection.Debug ($"{ME} INIT READ ASYNC FAILED: {e.Message}\n{e}"); + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, e, "ReadDoneAsync4"); + } + } + + if (state == ReadState.Aborted) + throw GetReadException (WebExceptionStatus.RequestCanceled, null, "ReadDoneAsync5"); + + if (state == ReadState.Content) { + buffer.Size = buffer.Offset - position; + buffer.Offset = position; + break; + } + + int est = nread * 2; + if (est > buffer.Size) { + var newBuffer = new byte [buffer.Buffer.Length + est]; + Buffer.BlockCopy (buffer.Buffer, 0, newBuffer, 0, buffer.Offset); + buffer = new BufferOffsetSize (newBuffer, buffer.Offset, newBuffer.Length - buffer.Offset, false); + } + state = ReadState.None; + position = 0; + } + + WebConnection.Debug ($"{ME} INIT READ ASYNC LOOP DONE: {buffer.Offset} {buffer.Size}"); + + try { + Operation.ThrowIfDisposed (cancellationToken); + await Initialize (buffer, cancellationToken).ConfigureAwait (false); + } catch (Exception e) { + throw GetReadException (WebExceptionStatus.ReceiveFailure, e, "ReadDoneAsync6"); + } + } + + bool GetResponse (BufferOffsetSize buffer, ref int pos, ref ReadState state) + { + string line = null; + bool lineok = false; + bool isContinue = false; + bool emptyFirstLine = false; + do { + if (state == ReadState.Aborted) + throw GetReadException (WebExceptionStatus.RequestCanceled, null, "GetResponse"); + + if (state == ReadState.None) { + lineok = WebConnection.ReadLine (buffer.Buffer, ref pos, buffer.Offset, ref line); + if (!lineok) + return false; + + if (line == null) { + emptyFirstLine = true; + continue; + } + emptyFirstLine = false; + state = ReadState.Status; + + string[] parts = line.Split (' '); + if (parts.Length < 2) + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, null, "GetResponse"); + + if (String.Compare (parts[0], "HTTP/1.1", true) == 0) { + Version = HttpVersion.Version11; + ServicePoint.SetVersion (HttpVersion.Version11); + } else { + Version = HttpVersion.Version10; + ServicePoint.SetVersion (HttpVersion.Version10); + } + + StatusCode = (HttpStatusCode)UInt32.Parse (parts[1]); + if (parts.Length >= 3) + StatusDescription = String.Join (" ", parts, 2, parts.Length - 2); + else + StatusDescription = string.Empty; + + if (pos >= buffer.Size) + return true; + } + + emptyFirstLine = false; + if (state == ReadState.Status) { + state = ReadState.Headers; + Headers = new WebHeaderCollection (); + var headerList = new List (); + bool finished = false; + while (!finished) { + if (WebConnection.ReadLine (buffer.Buffer, ref pos, buffer.Offset, ref line) == false) + break; + + if (line == null) { + // Empty line: end of headers + finished = true; + continue; + } + + if (line.Length > 0 && (line[0] == ' ' || line[0] == '\t')) { + int count = headerList.Count - 1; + if (count < 0) + break; + + string prev = headerList[count] + line; + headerList[count] = prev; + } else { + headerList.Add (line); + } + } + + if (!finished) + return false; + + // .NET uses ParseHeaders or ParseHeadersStrict which is much better + foreach (string s in headerList) { + + int pos_s = s.IndexOf (':'); + if (pos_s == -1) + throw new ArgumentException ("no colon found", "header"); + + var header = s.Substring (0, pos_s); + var value = s.Substring (pos_s + 1).Trim (); + + if (WebHeaderCollection.AllowMultiValues (header)) { + Headers.AddInternal (header, value); + } else { + Headers.SetInternal (header, value); + } + } + + if (StatusCode == HttpStatusCode.Continue) { + ServicePoint.SendContinue = true; + if (pos >= buffer.Offset) + return true; + + if (Request.ExpectContinue) { + Request.DoContinueDelegate ((int)StatusCode, Headers); + // Prevent double calls when getting the + // headers in several packets. + Request.ExpectContinue = false; + } + + state = ReadState.None; + isContinue = true; + } else { + state = ReadState.Content; + return true; + } + } + } while (emptyFirstLine || isContinue); + + throw GetReadException (WebExceptionStatus.ServerProtocolViolation, null, "GetResponse"); + } + + + } +} diff --git a/mcs/class/System/System/Platform.cs b/mcs/class/System/System/Platform.cs index f4214393fc32..9cf36642a856 100644 --- a/mcs/class/System/System/Platform.cs +++ b/mcs/class/System/System/Platform.cs @@ -59,30 +59,46 @@ private static void CheckOS() { } IntPtr buf = Marshal.AllocHGlobal (8192); - if (uname (buf) == 0) { - string os = Marshal.PtrToStringAnsi (buf); - switch (os) { - case "Darwin": - isMacOS = true; - break; - case "FreeBSD": - isFreeBSD = true; - break; + try { + if (uname (buf) == 0) { + string os = Marshal.PtrToStringAnsi (buf); + switch (os) { + case "Darwin": + isMacOS = true; + break; + case "FreeBSD": + isFreeBSD = true; + break; + } } } - Marshal.FreeHGlobal (buf); - checkedOS = true; + finally { + Marshal.FreeHGlobal (buf); + checkedOS = true; + } } #endif + // UNITY: runtime replaces this with intrinsic public static bool IsMacOS { get { if (!checkedOS) +#if UNITY + try { + CheckOS(); + } + catch (DllNotFoundException) { + // libc does not exist, so this is not MacOS + isMacOS = false; + } +#else CheckOS(); +#endif return isMacOS; } } + // UNITY: runtime replaces this with intrinsic public static bool IsFreeBSD { get { if (!checkedOS) diff --git a/mcs/class/System/Test/System.Net/HttpWebRequestTest.cs b/mcs/class/System/Test/System.Net/HttpWebRequestTest.cs index 4cabb0ca84ea..bdcde4afe539 100644 --- a/mcs/class/System/Test/System.Net/HttpWebRequestTest.cs +++ b/mcs/class/System/Test/System.Net/HttpWebRequestTest.cs @@ -303,7 +303,8 @@ public void BeginGetRequestStream_Body_NotAllowed () request.Method = "GET"; try { - request.BeginGetRequestStream (null, null); + var result = request.BeginGetRequestStream (null, null); + request.EndGetRequestStream (result); Assert.Fail ("#A1"); } catch (ProtocolViolationException ex) { // Cannot send a content-body with this @@ -316,7 +317,8 @@ public void BeginGetRequestStream_Body_NotAllowed () request.Method = "HEAD"; try { - request.BeginGetRequestStream (null, null); + var res = request.BeginGetRequestStream (null, null); + request.EndGetRequestStream (res); Assert.Fail ("#B1"); } catch (ProtocolViolationException ex) { // Cannot send a content-body with this @@ -358,7 +360,8 @@ public void BeginGetRequestStream_NoBuffering () req.AllowWriteStreamBuffering = false; try { - req.BeginGetRequestStream (null, null); + var result = req.BeginGetRequestStream (null, null); + req.EndGetRequestStream (result); Assert.Fail ("#A1"); } catch (ProtocolViolationException ex) { // When performing a write operation with @@ -3076,7 +3079,8 @@ public void CanWrite () try { Assert.IsTrue (rs.CanWrite, "#1"); rs.Close (); - Assert.IsFalse (rs.CanWrite, "#2"); + // CanRead and CanWrite do not change status after closing. + Assert.IsTrue (rs.CanWrite, "#2"); } finally { rs.Close (); req.Abort (); diff --git a/mcs/class/System/common.sources b/mcs/class/System/common.sources index 418336ceef91..6072a61bc9bd 100644 --- a/mcs/class/System/common.sources +++ b/mcs/class/System/common.sources @@ -59,10 +59,8 @@ System.Net/ListenerPrefix.cs System.Net/MonoHttpDate.cs System.Net/NetConfig.cs System.Net/RequestStream.cs -System.Net/SimpleAsyncResult.cs System.Net/SocketPermissionAttribute.cs System.Net/SocketPermission.cs -System.Net/WebAsyncResult.cs System.Net.Mail/AlternateView.cs System.Net.Mail/AlternateViewCollection.cs @@ -258,6 +256,15 @@ Mono.AppleTls/Trust.cs Mono.AppleTls/SslConnection.cs Mono.AppleTls/Items.cs +Mono.UnityTls/UnityTls.cs +Mono.UnityTls/UnityTlsProvider.cs +Mono.UnityTls/UnityTlsStream.cs +Mono.UnityTls/UnityTlsContext.cs +Mono.UnityTls/UnityTlsConversions.cs +Mono.UnityTls/X509ChainImplUnityTls.cs +Mono.UnityTls/Debug.cs +Mono.UnityTls/CertHelper.cs + Mono.Util/MonoPInvokeCallbackAttribute.cs MonoTouch/Dummy.cs @@ -898,7 +905,7 @@ corefx/SR.cs ../../../external/corefx/src/System.Collections/src/System/Collections/Generic/StackDebugView.cs ../../../external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs -../../../external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs +../../../external/corefx-bugfix/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs ../../../external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/CDSCollectionETWBCLProvider.cs ../../../external/corefx/src/System.IO.Compression/src/System/IO/Compression/CompressionLevel.cs @@ -923,3 +930,5 @@ corefx/SR.cs ../../../external/corefx/src/System.Private.Uri/src/System/UriBuilder.cs ../../../external/corefx/src/System.Runtime.Extensions/src/System/CodeDom/Compiler/IndentedTextWriter.cs + +../../../external/corefx/src/System.Runtime.InteropServices/src/System/Security/SecureStringMarshal.cs diff --git a/mcs/class/System/common_networking.sources b/mcs/class/System/common_networking.sources index f161a1657e9e..1327ddfb3c14 100644 --- a/mcs/class/System/common_networking.sources +++ b/mcs/class/System/common_networking.sources @@ -37,16 +37,19 @@ System.Net/HttpListenerResponse.cs System.Net/HttpListenerTimeoutManager.cs System.Net/HttpWebRequest.cs System.Net/HttpWebResponse.cs -System.Net/IWebConnectionState.cs System.Net/ListenerAsyncResult.cs System.Net/ResponseStream.cs System.Net/ServicePoint.cs System.Net/ServicePointManager.cs System.Net/ServicePointManager.extra.cs +System.Net/ServicePointScheduler.cs +System.Net/WebCompletionSource.cs System.Net/WebConnection.cs -System.Net/WebConnectionData.cs -System.Net/WebConnectionGroup.cs System.Net/WebConnectionStream.cs +System.Net/WebConnectionTunnel.cs +System.Net/WebOperation.cs +System.Net/WebRequestStream.cs +System.Net/WebResponseStream.cs ../referencesource/System/net/System/Net/Sockets/TCPClient.cs ../referencesource/System/net/System/Net/Sockets/TCPListener.cs diff --git a/mcs/class/System/unityaot_System.dll.exclude.sources b/mcs/class/System/unityaot_System.dll.exclude.sources new file mode 100644 index 000000000000..7287141431cf --- /dev/null +++ b/mcs/class/System/unityaot_System.dll.exclude.sources @@ -0,0 +1 @@ +System.IO/FileSystemWatcher_mobile.cs diff --git a/mcs/class/System/unityaot_System.dll.sources b/mcs/class/System/unityaot_System.dll.sources new file mode 100644 index 000000000000..b49bbad0e053 --- /dev/null +++ b/mcs/class/System/unityaot_System.dll.sources @@ -0,0 +1,13 @@ +#include mobile_System.dll.sources +../System.Web/System.Web/HttpUtility.cs +../System.Web/System.Web.Util/Helpers.cs +../System.Web/System.Web.Util/HttpEncoder.cs +System.CodeDom/CodeCompileUnit.cs +System.CodeDom/CodeTypeDeclaration.cs + +System.IO/DefaultWatcher.cs +System.IO/FileAction.cs +System.IO/FileSystemWatcher.cs +System.IO/IFileWatcher.cs +System.IO/NullFileWatcher.cs +System.IO/SearchPattern.cs diff --git a/mcs/class/System/unityjit_System.dll.sources b/mcs/class/System/unityjit_System.dll.sources new file mode 100644 index 000000000000..beb6f2415cf1 --- /dev/null +++ b/mcs/class/System/unityjit_System.dll.sources @@ -0,0 +1 @@ +#include net_4_x_System.dll.sources diff --git a/mcs/class/corlib/Assembly/AssemblyInfo.cs b/mcs/class/corlib/Assembly/AssemblyInfo.cs index 54b21cdf3ba7..2d2de030429d 100644 --- a/mcs/class/corlib/Assembly/AssemblyInfo.cs +++ b/mcs/class/corlib/Assembly/AssemblyInfo.cs @@ -59,7 +59,7 @@ [assembly: CLSCompliant (true)] [assembly: AssemblyDelaySign (true)] -#if MOBILE +#if MOBILE && !UNITY_AOT [assembly: AssemblyKeyFile ("../silverlight.pub")] #else [assembly: AssemblyKeyFile ("../ecma.pub")] diff --git a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml index 4d36758a43c9..304d4b03c7f3 100644 --- a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml +++ b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml @@ -4,7 +4,6 @@ - @@ -20,7 +19,10 @@ - + + + + @@ -123,7 +125,10 @@ - + + + + @@ -255,7 +260,10 @@ - + + + + @@ -304,6 +312,8 @@ + + @@ -498,13 +508,6 @@ note: there's no fields (static type) but that will mark the type itself --> - - - - - - - @@ -532,42 +535,71 @@ - + + + + - + + + + - + + + + - - - + + + + + + + + + + - - - + + + + + + + + - + + + + + + - + + + + @@ -594,39 +626,39 @@ - - - - - + + + + + - + - - - - - + + + + + - + - - - + + + - + - + - - + + @@ -634,34 +666,34 @@ - + - + - - + + - + - + - + - + - + - + - + @@ -712,22 +744,36 @@ - + + + + + + + + + + + + + + + @@ -735,18 +781,6 @@ - - - - - - - - - - - - @@ -853,5 +887,16 @@ + + + + + + + + + + + diff --git a/mcs/class/corlib/Makefile b/mcs/class/corlib/Makefile index 910f7b672f4c..a5cfd954198a 100644 --- a/mcs/class/corlib/Makefile +++ b/mcs/class/corlib/Makefile @@ -42,7 +42,6 @@ MODULE_DEPS = $(IL_REPLACE) endif RESOURCE_FILES = \ - resources/charinfo.nlp \ resources/collation.core.bin \ resources/collation.tailoring.bin \ resources/collation.cjkCHS.bin \ diff --git a/mcs/class/corlib/ReferenceSources/win32native.cs b/mcs/class/corlib/ReferenceSources/win32native.cs index 06b5c9ea478a..2f27d85c2c69 100644 --- a/mcs/class/corlib/ReferenceSources/win32native.cs +++ b/mcs/class/corlib/ReferenceSources/win32native.cs @@ -79,5 +79,8 @@ internal class WIN32_FIND_DATA internal int dwFileAttributes = 0; internal String cFileName = null; } + + [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] + internal static extern uint GetCurrentProcessId(); } } \ No newline at end of file diff --git a/mcs/class/corlib/System.IO/DriveInfo.cs b/mcs/class/corlib/System.IO/DriveInfo.cs index 6c4c7eee87ce..b71e5d003895 100644 --- a/mcs/class/corlib/System.IO/DriveInfo.cs +++ b/mcs/class/corlib/System.IO/DriveInfo.cs @@ -140,10 +140,9 @@ public DirectoryInfo RootDirectory { } } - [MonoTODO("It always returns true")] public bool IsReady { get { - return true; + return Directory.Exists (Name); } } diff --git a/mcs/class/corlib/System.IO/MonoIO.cs b/mcs/class/corlib/System.IO/MonoIO.cs index 3567809b9da5..68418f794f37 100644 --- a/mcs/class/corlib/System.IO/MonoIO.cs +++ b/mcs/class/corlib/System.IO/MonoIO.cs @@ -733,6 +733,9 @@ public extern static char PathSeparator { [MethodImplAttribute (MethodImplOptions.InternalCall)] extern static void DumpHandles (); + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + public extern static bool RemapPath (string path, out string newPath); } } diff --git a/mcs/class/corlib/System.IO/Path.cs b/mcs/class/corlib/System.IO/Path.cs index bf0093e1203e..8aaa49340d27 100644 --- a/mcs/class/corlib/System.IO/Path.cs +++ b/mcs/class/corlib/System.IO/Path.cs @@ -422,6 +422,9 @@ internal static string InsecureGetFullPath (string path) if (IsDirectorySeparator (end) && (path [path.Length - 1] != DirectorySeparatorChar)) path += DirectorySeparatorChar; + string newPath; + if (MonoIO.RemapPath(path, out newPath)) + path = newPath; return path; } diff --git a/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs b/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs index 82e099c4c418..c8311aed81b5 100644 --- a/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs +++ b/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs @@ -172,7 +172,7 @@ public Delegate CreateDelegate (Type delegateType) CreateDynMethod (); - deleg = Delegate.CreateDelegate (delegateType, this); + deleg = Delegate.CreateDelegate (delegateType, null, this); return deleg; } diff --git a/mcs/class/corlib/System.Reflection/MonoProperty.cs b/mcs/class/corlib/System.Reflection/MonoProperty.cs index 16f44ec583b4..a4c807d3de67 100644 --- a/mcs/class/corlib/System.Reflection/MonoProperty.cs +++ b/mcs/class/corlib/System.Reflection/MonoProperty.cs @@ -378,7 +378,7 @@ public override object GetValue (object obj, object[] index) { if (index == null || index.Length == 0) { /*FIXME we should check if the number of arguments matches the expected one, otherwise the error message will be pretty criptic.*/ -#if !FULL_AOT_RUNTIME +#if !FULL_AOT_RUNTIME && !UNITY_AOT if (cached_getter == null) { MethodInfo method = GetGetMethod (true); if (!DeclaringType.IsValueType && !method.ContainsGenericParameters) { //FIXME find a way to build an invoke delegate for value types. diff --git a/mcs/class/corlib/System.Runtime.InteropServices/LIBFLAGS.cs b/mcs/class/corlib/System.Runtime.InteropServices/LIBFLAGS.cs index f58f6a608dc8..9dccb0a55377 100644 --- a/mcs/class/corlib/System.Runtime.InteropServices/LIBFLAGS.cs +++ b/mcs/class/corlib/System.Runtime.InteropServices/LIBFLAGS.cs @@ -34,7 +34,7 @@ namespace System.Runtime.InteropServices { [Obsolete] [Flags, Serializable] - public enum LIBFLAGS + public enum LIBFLAGS : short { LIBFLAG_FRESTRICTED = 1, LIBFLAG_FCONTROL = 2, diff --git a/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs b/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs index e94898a5c386..e12d825cd58c 100644 --- a/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs +++ b/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs @@ -53,14 +53,14 @@ public static class Marshal public static readonly int SystemMaxDBCSCharSize = 2; // don't know what this is public static readonly int SystemDefaultCharSize = Environment.IsRunningOnWindows ? 2 : 1; -#if !MOBILE +#if !MOBILE || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static int AddRefInternal (IntPtr pUnk); #endif public static int AddRef (IntPtr pUnk) { -#if !MOBILE +#if !MOBILE || UNITY_AOT if (pUnk == IntPtr.Zero) throw new ArgumentException ("Value cannot be null.", "pUnk"); return AddRefInternal (pUnk); @@ -210,7 +210,9 @@ public static IntPtr CreateAggregatedObject (IntPtr pOuter, T o) { public static object CreateWrapperOfType (object o, Type t) { -#if FULL_AOT_RUNTIME +#if UNITY_AOT + throw new NotImplementedException("Marshal.CreateWrapperOfType is not implemented."); +#elif FULL_AOT_RUNTIME throw new PlatformNotSupportedException (); #else __ComObject co = o as __ComObject; @@ -307,7 +309,7 @@ public static void ZeroFreeGlobalAllocUnicode (IntPtr s) FreeHGlobal (s); } -#if !FULL_AOT_RUNTIME +#if !FULL_AOT_RUNTIME || UNITY_AOT public static Guid GenerateGuidForType (Type type) { return type.GUID; @@ -342,15 +344,17 @@ public static object GetActiveObject (string progID) throw new NotImplementedException (); } -#if !MOBILE +#if !MOBILE || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static IntPtr GetCCW (object o, Type T); private static IntPtr GetComInterfaceForObjectInternal (object o, Type T) { +#if !UNITY_AOT if (IsComObject (o)) return ((__ComObject)o).GetInterface (T); else +#endif return GetCCW (o, T); } #endif @@ -358,7 +362,7 @@ private static IntPtr GetComInterfaceForObjectInternal (object o, Type T) public static IntPtr GetComInterfaceForObject (object o, Type T) { -#if MOBILE +#if MOBILE && !UNITY_AOT throw new PlatformNotSupportedException (); #else IntPtr pItf = GetComInterfaceForObjectInternal (o, T); @@ -377,7 +381,7 @@ public static IntPtr GetComInterfaceForObject (T o) { return GetComInterfaceForObject ((object)o, typeof (T)); } -#if !FULL_AOT_RUNTIME +#if !FULL_AOT_RUNTIME || UNITY_AOT [MonoTODO] public static IntPtr GetComInterfaceForObjectInContext (object o, Type t) { @@ -430,7 +434,7 @@ public static IntPtr GetHINSTANCE (Module m) return m.GetHINSTANCE (); } -#endif // !FULL_AOT_RUNTIME +#endif // !FULL_AOT_RUNTIME || UNITY_AOT public static int GetExceptionCode () { @@ -453,14 +457,14 @@ public static int GetHRForException (Exception e) [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] public static int GetHRForLastWin32Error() { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else throw new NotImplementedException (); #endif } - -#if !FULL_AOT_RUNTIME + +#if !FULL_AOT_RUNTIME || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static IntPtr GetIDispatchForObjectInternal (object o); @@ -471,7 +475,9 @@ public static IntPtr GetIDispatchForObject (object o) AddRef (pUnk); return pUnk; } +#endif // !FULL_AOT_RUNTIME || UNITY_AOT +#if !FULL_AOT_RUNTIME [MonoTODO] public static IntPtr GetIDispatchForObjectInContext (object o) { @@ -502,15 +508,16 @@ public static MemberInfo GetMethodInfoForComSlot (Type t, int slot, ref ComMembe { throw new NotImplementedException (); } +#endif // !FULL_AOT_RUNTIME +#if !FULL_AOT_RUNTIME || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static IntPtr GetIUnknownForObjectInternal (object o); - -#endif // !FULL_AOT_RUNTIME +#endif // !FULL_AOT_RUNTIME || UNITY_AOT public static IntPtr GetIUnknownForObject (object o) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else IntPtr pUnk = GetIUnknownForObjectInternal (o); @@ -522,7 +529,7 @@ public static IntPtr GetIUnknownForObject (object o) public static void GetNativeVariantForObject (object obj, IntPtr pDstNativeVariant) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else Variant vt = new Variant(); @@ -535,29 +542,31 @@ public static void GetNativeVariantForObject (T obj, IntPtr pDstNativeVariant GetNativeVariantForObject ((object)obj, pDstNativeVariant); } -#if !MOBILE && !FULL_AOT_RUNTIME +#if (!MOBILE && !FULL_AOT_RUNTIME) || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private static extern object GetObjectForCCW (IntPtr pUnk); #endif public static object GetObjectForIUnknown (IntPtr pUnk) { -#if MOBILE || FULL_AOT_RUNTIME +#if (MOBILE || FULL_AOT_RUNTIME) && !UNITY_AOT throw new PlatformNotSupportedException (); #else object obj = GetObjectForCCW (pUnk); +#if !UNITY_AOT // was not a CCW if (obj == null) { ComInteropProxy proxy = ComInteropProxy.GetProxy (pUnk, typeof (__ComObject)); obj = proxy.GetTransparentProxy (); } +#endif return obj; #endif } public static object GetObjectForNativeVariant (IntPtr pSrcNativeVariant) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else Variant vt = (Variant)Marshal.PtrToStructure(pSrcNativeVariant, typeof(Variant)); @@ -567,7 +576,7 @@ public static object GetObjectForNativeVariant (IntPtr pSrcNativeVariant) public static T GetObjectForNativeVariant (IntPtr pSrcNativeVariant) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else Variant vt = (Variant)Marshal.PtrToStructure(pSrcNativeVariant, typeof(Variant)); @@ -577,7 +586,7 @@ public static T GetObjectForNativeVariant (IntPtr pSrcNativeVariant) public static object[] GetObjectsForNativeVariants (IntPtr aSrcNativeVariant, int cVars) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else if (cVars < 0) @@ -592,7 +601,7 @@ public static object[] GetObjectsForNativeVariants (IntPtr aSrcNativeVariant, in public static T[] GetObjectsForNativeVariants (IntPtr aSrcNativeVariant, int cVars) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else if (cVars < 0) @@ -608,7 +617,7 @@ public static T[] GetObjectsForNativeVariants (IntPtr aSrcNativeVariant, int [MonoTODO] public static int GetStartComSlot (Type t) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else throw new NotImplementedException (); @@ -735,7 +744,7 @@ public static object GetUniqueObjectForIUnknown (IntPtr unknown) throw new PlatformNotSupportedException (); } -#if !MOBILE +#if !MOBILE || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] public extern static bool IsComObject (object o); #else @@ -815,14 +824,14 @@ public static T PtrToStructure (IntPtr ptr) { return (T) PtrToStructure (ptr, typeof (T)); } -#if !MOBILE +#if !MOBILE || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static int QueryInterfaceInternal (IntPtr pUnk, ref Guid iid, out IntPtr ppv); #endif public static int QueryInterface (IntPtr pUnk, ref Guid iid, out IntPtr ppv) { -#if !MOBILE +#if !MOBILE || UNITY_AOT if (pUnk == IntPtr.Zero) throw new ArgumentException ("Value cannot be null.", "pUnk"); return QueryInterfaceInternal (pUnk, ref iid, out ppv); @@ -986,7 +995,7 @@ public static IntPtr ReadIntPtr ([In, MarshalAs (UnmanagedType.AsAny)] object pt [MethodImplAttribute(MethodImplOptions.InternalCall)] public extern static IntPtr ReAllocHGlobal (IntPtr pv, IntPtr cb); -#if !MOBILE +#if !MOBILE || UNITY_AOT [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)] [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static int ReleaseInternal (IntPtr pUnk); @@ -995,7 +1004,7 @@ public static IntPtr ReadIntPtr ([In, MarshalAs (UnmanagedType.AsAny)] object pt [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] public static int Release (IntPtr pUnk) { -#if !MOBILE +#if !MOBILE || UNITY_AOT if (pUnk == IntPtr.Zero) throw new ArgumentException ("Value cannot be null.", "pUnk"); @@ -1005,14 +1014,14 @@ public static int Release (IntPtr pUnk) #endif } -#if !FULL_AOT_RUNTIME +#if !FULL_AOT_RUNTIME || UNITY_AOT [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static int ReleaseComObjectInternal (object co); #endif public static int ReleaseComObject (object o) { -#if FULL_AOT_RUNTIME +#if FULL_AOT_RUNTIME && !UNITY_AOT throw new PlatformNotSupportedException (); #else if (o == null) @@ -1752,7 +1761,7 @@ internal static void SetLastWin32Error (int error) { } -#if FEATURE_COMINTEROP || MONO_COM +#if FEATURE_COMINTEROP || MONO_COM || UNITY_AOT // Copied from referencesource/mscorlib/system/runtime/interopservices/marshal.cs //==================================================================== // return the raw IUnknown* for a COM Object not related to current diff --git a/mcs/class/corlib/System/Debug.cs b/mcs/class/corlib/System/Debug.cs new file mode 100644 index 000000000000..aa111394c504 --- /dev/null +++ b/mcs/class/corlib/System/Debug.cs @@ -0,0 +1,13 @@ +//Stubs for Assert used by CharUnicodeInfo pulled in from corert +namespace System.Globalization +{ + public static partial class CharUnicodeInfo + { + internal static class Debug + { + internal static void Assert(bool condition, string message) + { + } + } + } +} diff --git a/mcs/class/corlib/System/Delegate.cs b/mcs/class/corlib/System/Delegate.cs index 48d78605147f..958b70ce816e 100644 --- a/mcs/class/corlib/System/Delegate.cs +++ b/mcs/class/corlib/System/Delegate.cs @@ -134,6 +134,8 @@ private static bool arg_type_match (Type delArgType, Type argType) { if (!match) { if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType) match = true; + else if (argType.IsEnum && Enum.GetUnderlyingType (argType) == delArgType) + match = true; } return match; diff --git a/mcs/class/corlib/System/EnumSharing.cs b/mcs/class/corlib/System/EnumSharing.cs new file mode 100644 index 000000000000..aa1def337693 --- /dev/null +++ b/mcs/class/corlib/System/EnumSharing.cs @@ -0,0 +1,18 @@ +using System; + +#if UNITY_AOT + +namespace System +{ + enum SByteEnum : sbyte {} + enum Int16Enum : short {} + enum Int32Enum : int {} + enum Int64Enum : long {} + + enum ByteEnum : byte {} + enum UInt16Enum : ushort {} + enum UInt32Enum : uint {} + enum UInt64Enum : ulong {} +} + +#endif diff --git a/mcs/class/corlib/System/TimeZone.cs b/mcs/class/corlib/System/TimeZone.cs index 1cfa2ee9bcc3..b32195359662 100644 --- a/mcs/class/corlib/System/TimeZone.cs +++ b/mcs/class/corlib/System/TimeZone.cs @@ -210,5 +210,18 @@ public override bool IsDaylightSavingTime (DateTime dateTime) return LocalTimeZone.IsDaylightSavingTime (dateTime); } + + + // Internal method to get timezone data. + // data[0]: start of daylight saving time (in DateTime ticks). + // data[1]: end of daylight saving time (in DateTime ticks). + // data[2]: utcoffset (in TimeSpan ticks). + // data[3]: additional offset when daylight saving (in TimeSpan ticks). + // name[0]: name of this timezone when not daylight saving. + // name[1]: name of this timezone when daylight saving. +#if UNITY + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern bool GetTimeZoneData (int year, out Int64[] data, out string[] names, out bool daylight_inverted); +#endif } } diff --git a/mcs/class/corlib/System/TimeZoneInfo.Unity.cs b/mcs/class/corlib/System/TimeZoneInfo.Unity.cs new file mode 100644 index 000000000000..d724f464976f --- /dev/null +++ b/mcs/class/corlib/System/TimeZoneInfo.Unity.cs @@ -0,0 +1,174 @@ +// +// System.TimeZoneInfo helper for Unity +// because the devices cannot access the file system to read the data +// +// Authors: +// Michael DeRoy +// Jonathan Chambers +// +// Copyright 2018 Unity Technologies, Inc. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if UNITY + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; + +namespace System { + + public partial class TimeZoneInfo { + enum TimeZoneData + { + DaylightSavingFirstTransitionIdx, + DaylightSavingSecondTransitionIdx, + UtcOffsetIdx, + AdditionalDaylightOffsetIdx + }; + + enum TimeZoneNames + { + StandardNameIdx, + DaylightNameIdx + }; + + static List CreateAdjustmentRule (int year, out Int64[] data, out string[] names, string standardNameCurrentYear, string daylightNameCurrentYear) + { + List rulesForYear = new List (); + bool dst_inverted; + if (!System.CurrentSystemTimeZone.GetTimeZoneData(year, out data, out names, out dst_inverted)) + return rulesForYear; + var firstTransition = new DateTime (data[(int)TimeZoneData.DaylightSavingFirstTransitionIdx]); + var secondTransition = new DateTime (data[(int)TimeZoneData.DaylightSavingSecondTransitionIdx]); + var daylightOffset = new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]); + + /* C# TimeZoneInfo does not support timezones the same way as unix. In unix, timezone files are specified by region such as + * America/New_York or Asia/Singapore. If a region like Asia/Singapore changes it's timezone from +0730 to +08, the UTC offset + * has changed, but there is no support in the C# code to transition to this new UTC offset except for the case of daylight + * savings time. As such we'll only generate timezone rules for a region at the times associated with the timezone of the current year. + */ + if(standardNameCurrentYear != names[(int)TimeZoneNames.StandardNameIdx]) + return rulesForYear; + if(daylightNameCurrentYear != names[(int)TimeZoneNames.DaylightNameIdx]) + return rulesForYear; + + // If the first and second transition DateTime objects are the same, ValidateAdjustmentRule will throw + // an exception. I'm unsure why these would be the same, but we do see that occur for some locales. + // In that case, just exit early. + if (firstTransition.Equals(secondTransition)) + return rulesForYear; + + var beginningOfYear = new DateTime (year, 1, 1, 0, 0, 0, 0); + var endOfYearDay = new DateTime (year, 12, DateTime.DaysInMonth (year, 12)); + var endOfYearMaxTimeout = new DateTime (year, 12, DateTime.DaysInMonth(year, 12), 23, 59, 59, 999); + + if (!dst_inverted) { + // For daylight savings time that happens between jan and dec, create a rule from jan 1 to dec 31 (the entire year) + + // This rule (for the whole year) specifies the starting and ending months of daylight savings time. + var startOfDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1).Add (firstTransition.TimeOfDay), + firstTransition.Month, firstTransition.Day); + var endOfDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1).Add (secondTransition.TimeOfDay), + secondTransition.Month, secondTransition.Day); + + var fullYearRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule (beginningOfYear, + endOfYearDay, + daylightOffset, + startOfDaylightSavingsTime, + endOfDaylightSavingsTime); + rulesForYear.Add (fullYearRule); + } else { + // Some timezones (Australia/Sydney) have daylight savings over the new year. + // Our icall returns the transitions for the current year, so we need two adjustment rules each year for this case + + // The first rule specifies daylight savings starting at jan 1 and ending at the first transition. + var startOfFirstDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1), 1, 1); + var endOfFirstDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1).Add (firstTransition.TimeOfDay), + firstTransition.Month, firstTransition.Day); + + var transitionOutOfDaylightSavingsRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule ( + new DateTime (year, 1, 1), + new DateTime (firstTransition.Year, firstTransition.Month, firstTransition.Day), + daylightOffset, + startOfFirstDaylightSavingsTime, + endOfFirstDaylightSavingsTime); + rulesForYear.Add (transitionOutOfDaylightSavingsRule); + + // The second rule specifies daylight savings time starting the day after we transition out of daylight savings + // and ending at the end of the year, with daylight savings starting near the end and ending on the last day of the year + var startOfSecondDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1).Add (secondTransition.TimeOfDay), + secondTransition.Month, secondTransition.Day); + var endOfSecondDaylightSavingsTime = TransitionTime.CreateFixedDateRule (new DateTime (1,1,1).Add (endOfYearMaxTimeout.TimeOfDay), + endOfYearMaxTimeout.Month, endOfYearMaxTimeout.Day); + + var transitionIntoDaylightSavingsRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule ( + new DateTime (firstTransition.Year, firstTransition.Month, firstTransition.Day).AddDays (1), + endOfYearDay, + daylightOffset, + startOfSecondDaylightSavingsTime, + endOfSecondDaylightSavingsTime); + rulesForYear.Add (transitionIntoDaylightSavingsRule); + } + return rulesForYear; + } + + static TimeZoneInfo CreateLocalUnity () + { + Int64[] data; + string[] names; + //Some timezones start in DST on january first and disable it during the summer + bool dst_inverted; + int currentYear = DateTime.UtcNow.Year; + if (!System.CurrentSystemTimeZone.GetTimeZoneData (currentYear, out data, out names, out dst_inverted)) + throw new NotSupportedException ("Can't get timezone name."); + + var utcOffsetTS = TimeSpan.FromTicks (data[(int)TimeZoneData.UtcOffsetIdx]); + char utcOffsetSign = (utcOffsetTS >= TimeSpan.Zero) ? '+' : '-'; + string displayName = "(GMT" + utcOffsetSign + utcOffsetTS.ToString (@"hh\:mm") + ") Local Time"; + string standardDisplayName = names[(int)TimeZoneNames.StandardNameIdx]; + string daylightDisplayName = names[(int)TimeZoneNames.DaylightNameIdx]; + + var adjustmentRulesList = new List (); + bool disableDaylightSavings = data[(int)TimeZoneData.AdditionalDaylightOffsetIdx] == 0; + //If the timezone supports daylight savings time, generate adjustment rules for the timezone + if (!disableDaylightSavings) { + //the icall only supports years from 1970 through 2037. + int firstSupportedDate = 1971; + int lastSupportedDate = 2037; + + //first, generate rules from the current year until the last year mktime is guaranteed to supports + for (int year = currentYear; year <= lastSupportedDate; year++) { + var rulesForCurrentYear = CreateAdjustmentRule (year, out data, out names, standardDisplayName, daylightDisplayName); + //breakout if no more rules + if (rulesForCurrentYear.Count > 0) + adjustmentRulesList.AddRange (rulesForCurrentYear); + else + break; + + } + + for (int year = currentYear - 1; year >= firstSupportedDate; year--) { + var rulesForCurrentYear = CreateAdjustmentRule (year, out data, out names, standardDisplayName, daylightDisplayName); + //breakout if no more rules + if (rulesForCurrentYear.Count > 0) + adjustmentRulesList.AddRange (rulesForCurrentYear); + else + break; + } + + adjustmentRulesList.Sort ( (rule1, rule2) => rule1.DateStart.CompareTo (rule2.DateStart) ); + } + return TimeZoneInfo.CreateCustomTimeZone ("Local", + utcOffsetTS, + displayName, + standardDisplayName, + daylightDisplayName, + adjustmentRulesList.ToArray (), + disableDaylightSavings); + } + } +} + +#endif diff --git a/mcs/class/corlib/System/TimeZoneInfo.WinRT.cs b/mcs/class/corlib/System/TimeZoneInfo.WinRT.cs index fc27fe267d26..4b5cff7f486f 100755 --- a/mcs/class/corlib/System/TimeZoneInfo.WinRT.cs +++ b/mcs/class/corlib/System/TimeZoneInfo.WinRT.cs @@ -61,11 +61,14 @@ internal struct DYNAMIC_TIME_ZONE_INFORMATION internal const uint TIME_ZONE_ID_INVALID = 0xffffffff; internal const uint ERROR_NO_MORE_ITEMS = 259; + internal const uint ERROR_SUCCESS = 0; [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")] internal extern static uint EnumDynamicTimeZoneInformation (uint dwIndex, out DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation); [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")] internal extern static uint GetDynamicTimeZoneInformation (out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); + [DllImport ("kernel32.dll", EntryPoint="GetDynamicTimeZoneInformation")] + internal extern static uint GetDynamicTimeZoneInformationWin32 (out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")] internal extern static uint GetDynamicTimeZoneInformationEffectiveYears(ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, out uint FirstYear, out uint LastYear); [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")] @@ -310,6 +313,24 @@ internal static TimeZoneInfo GetLocalTimeZoneInfoWinRTFallback () return timeZoneInfo != null ? timeZoneInfo : Utc; } catch { return Utc; + + } + } + + internal static string GetLocalTimeZoneKeyNameWin32Fallback () + { + try { + DYNAMIC_TIME_ZONE_INFORMATION dtzi; + var result = GetDynamicTimeZoneInformationWin32 (out dtzi); + if (result == TIME_ZONE_ID_INVALID) + return null; + if (!string.IsNullOrEmpty(dtzi.TimeZoneKeyName)) + return dtzi.TimeZoneKeyName; + else if (!string.IsNullOrEmpty(dtzi.TZI.StandardName)) + return dtzi.TZI.StandardName; + return null; + } catch { + return null; } } @@ -329,7 +350,7 @@ internal static List GetSystemTimeZonesWinRTFallback () try { uint index = 0; DYNAMIC_TIME_ZONE_INFORMATION dtzi; - while (EnumDynamicTimeZoneInformation (index++, out dtzi) != ERROR_NO_MORE_ITEMS) { + while (EnumDynamicTimeZoneInformation (index++, out dtzi) == ERROR_SUCCESS) { var timeZoneInfo = TryCreateTimeZone (dtzi); if (timeZoneInfo != null) result.Add (timeZoneInfo); @@ -338,8 +359,17 @@ internal static List GetSystemTimeZonesWinRTFallback () // EnumDynamicTimeZoneInformation() might not be available. } + // If we are in this function we know that TimeZoneKey is null and need to use the fallback + // Adding Local here will cause a stack overflow if (result.Count == 0) - result.Add (Local); + { + var l = GetLocalTimeZoneInfoWinRTFallback(); + if (Interlocked.CompareExchange (ref local, l, null) != null) + l = local; + + result.Add(l); + } + return result; } } diff --git a/mcs/class/corlib/System/TimeZoneInfo.cs b/mcs/class/corlib/System/TimeZoneInfo.cs index a1eaf2a7cf37..7afdbd857de2 100644 --- a/mcs/class/corlib/System/TimeZoneInfo.cs +++ b/mcs/class/corlib/System/TimeZoneInfo.cs @@ -1,5 +1,4 @@ - -/* + /* * System.TimeZoneInfo * * Author(s) @@ -122,6 +121,11 @@ private static string readlink (string path) private static bool TryGetNameFromPath (string path, out string name) { name = null; +#if UNITY + //Avoids calling readlink on webgl, which causes abort due to dlopen + if(!File.Exists(path)) + return false; +#endif var linkPath = readlink (path); if (linkPath != null) { if (Path.IsPathRooted(linkPath)) @@ -158,21 +162,49 @@ static TimeZoneInfo CreateLocal () if (name == null) name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp name = TrimSpecial (name); + if (string.IsNullOrEmpty(name)) + name = GetLocalTimeZoneKeyNameWin32Fallback(); if (name != null) - return TimeZoneInfo.FindSystemTimeZoneById (name); + { + try{ + return TimeZoneInfo.FindSystemTimeZoneById (name); + } catch (TimeZoneNotFoundException) { + return GetLocalTimeZoneInfoWinRTFallback(); + } + } } else if (IsWindows) { return GetLocalTimeZoneInfoWinRTFallback (); } #endif +#if UNITY + TimeZoneInfo localTimeZoneFallback = null; + try { + localTimeZoneFallback = CreateLocalUnity(); + } catch { + localTimeZoneFallback = null; + } + + if (localTimeZoneFallback == null) + localTimeZoneFallback = Utc; +#endif + var tz = Environment.GetEnvironmentVariable ("TZ"); if (tz != null) { if (tz == String.Empty) +#if UNITY + return localTimeZoneFallback; +#else return Utc; +#endif try { return FindSystemTimeZoneByFileName (tz, Path.Combine (TimeZoneDirectory, tz)); } catch { +#if UNITY + return localTimeZoneFallback; +#else return Utc; +#endif } } @@ -185,13 +217,18 @@ static TimeZoneInfo CreateLocal () string tzName = null; if (!TryGetNameFromPath (tzFilePath, out tzName)) tzName = "Local"; - return FindSystemTimeZoneByFileName (tzName, tzFilePath); + if (File.Exists(tzFilePath)) + return FindSystemTimeZoneByFileName (tzName, tzFilePath); } catch (TimeZoneNotFoundException) { continue; } } +#if UNITY + return localTimeZoneFallback; +#else return Utc; +#endif } static TimeZoneInfo FindSystemTimeZoneByIdCore (string id) @@ -1533,4 +1570,4 @@ public override string ToString () } #endif } -} +} \ No newline at end of file diff --git a/mcs/class/corlib/System/WeakAttribute.cs b/mcs/class/corlib/System/WeakAttribute.cs index 72de431d1727..b7a377935d1a 100644 --- a/mcs/class/corlib/System/WeakAttribute.cs +++ b/mcs/class/corlib/System/WeakAttribute.cs @@ -1,6 +1,6 @@ using System; -#if MOBILE +#if MOBILE && !UNITY namespace System { [AttributeUsage(AttributeTargets.Field)] diff --git a/mcs/class/corlib/Test/System.Reflection.Emit/DynamicMethodTest.cs b/mcs/class/corlib/Test/System.Reflection.Emit/DynamicMethodTest.cs index da29af0f406c..862d2ca9272c 100644 --- a/mcs/class/corlib/Test/System.Reflection.Emit/DynamicMethodTest.cs +++ b/mcs/class/corlib/Test/System.Reflection.Emit/DynamicMethodTest.cs @@ -744,6 +744,55 @@ private static void AssertTypedRef (TypedReference tr) Assert.AreEqual (typeof (TypedRefTarget), TypedReference.GetTargetType (tr)); } #endif + + static Action GenerateProblematicMethod (bool add_extra, bool mismatch = false, bool use_vts = false) + { + Type this_type = typeof(object); + Type bound_type = typeof(object); + if (mismatch) { + this_type = typeof (string); + bound_type = typeof (DynamicMethodTest); + } else if (use_vts) { + this_type = typeof (int); + bound_type = typeof (long); + } + + Type[] args; + if (add_extra) + args = new[] { this_type }; + else + args = new Type [0]; + + var mb = new DynamicMethod("Peek", null, args, bound_type, true); + var il = mb.GetILGenerator (); + il.Emit(OpCodes.Ret); + return (Action) mb.CreateDelegate(typeof(Action)); + } + + [Test] + public void ExtraArgGetsIgnored () + { + GenerateProblematicMethod (true) (); + } + + [Test] + public void ExactNumberOfArgsWork () + { + GenerateProblematicMethod (false) (); + } + + [Test] + public void ExtraArgWithMismatchedTypes () + { + GenerateProblematicMethod (true, mismatch: true) (); + } + + [Test] + [ExpectedException (typeof (ArgumentException))] + public void ExtraArgWithValueType () + { + GenerateProblematicMethod (true, use_vts: true) (); + } } } diff --git a/mcs/class/corlib/Test/System/DelegateTest.cs b/mcs/class/corlib/Test/System/DelegateTest.cs index 26c449615b30..19a4bd894dc3 100644 --- a/mcs/class/corlib/Test/System/DelegateTest.cs +++ b/mcs/class/corlib/Test/System/DelegateTest.cs @@ -1395,6 +1395,23 @@ public void EnumBaseTypeConversion () { Assert.IsTrue (d (0, 0)); } + [Test] + public void EnumBaseTypeConversion2 () { + Func dm = EnumArg; + var d = (Func)Delegate.CreateDelegate (typeof (Func), dm.Method); + Assert.AreEqual (1, d (1)); + } + + public enum Enum22 { + none, + one, + two + } + + public static int EnumArg (Enum22 e) { + return (int)e; + } + #if !MONOTOUCH && !FULL_AOT_RUNTIME public static void DynInvokeWithClosedFirstArg (object a, object b) { diff --git a/mcs/class/corlib/Test/System/TimeZoneInfoTest.cs b/mcs/class/corlib/Test/System/TimeZoneInfoTest.cs index b6384d11162e..25adc0c7d0a8 100644 --- a/mcs/class/corlib/Test/System/TimeZoneInfoTest.cs +++ b/mcs/class/corlib/Test/System/TimeZoneInfoTest.cs @@ -124,13 +124,388 @@ public void LocalId () } catch (DllNotFoundException e) { return; } -#if !MONOTOUCH && !XAMMAC +#if !MONOTOUCH && !XAMMAC && !UNITY // this assumption is incorrect for iOS, tvO, watchOS and OSX Assert.IsTrue (TimeZoneInfo.Local.Id != "Local", "Local timezone id should not be \"Local\""); #endif } } +#if UNITY + [TestFixture] + public class UnityTests + { + public TimeZoneInfo GetLocalUnity () + { + return (TimeZoneInfo)typeof (TimeZoneInfo).GetMethod ("CreateLocalUnity", BindingFlags.NonPublic | BindingFlags.Static).Invoke (null, null); + } + + public void AssertNoDLS (TimeZoneInfo local, DateTime beforeDLSStart, DateTime afterDLSStart, DateTime beforeDLSEnd, DateTime afterDLSEnd) + { + Assert.IsFalse (local.IsDaylightSavingTime(beforeDLSStart), "Expected Not Daylight Savings " + beforeDLSStart.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime(afterDLSStart), "Expected Not Daylight Savings " + afterDLSStart.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime(beforeDLSEnd), "Expected Not Daylight Savings " + beforeDLSEnd.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime(afterDLSEnd), "Expected Not Daylight Savings " + afterDLSEnd.ToString ()); + } + + public void AssertDLS (TimeZoneInfo local, DateTime beforeDLSStart, DateTime afterDLSStart, DateTime beforeDLSEnd, DateTime afterDLSEnd) + { + Assert.IsFalse (local.IsDaylightSavingTime (beforeDLSStart), "Expected Not Daylight Savings " + beforeDLSStart.ToString ()); + Assert.IsTrue (local.IsDaylightSavingTime (afterDLSStart), "Expected Daylight Savings " + afterDLSStart.ToString ()); + Assert.IsTrue (local.IsDaylightSavingTime (beforeDLSEnd), "Expected Daylight Savings " + beforeDLSEnd.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime (afterDLSEnd), "Expected Not Daylight Savings " + afterDLSEnd.ToString ()); + } + + //Similar to Above but for Timezones that begin in daylight savings time jan 1 + public void AssertDLSInverse (TimeZoneInfo local, DateTime beforeDLSEnd, DateTime afterDLSEnd, DateTime beforeDLSStart, DateTime afterDLSStart) + { + Assert.IsTrue (local.IsDaylightSavingTime (beforeDLSEnd), "Expected Daylight Savings " + beforeDLSEnd.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime (afterDLSEnd), "Expected Not Daylight Savings " + afterDLSEnd.ToString ()); + Assert.IsFalse (local.IsDaylightSavingTime (beforeDLSStart), "Expected Not Daylight Savings " + beforeDLSStart.ToString ()); + Assert.IsTrue (local.IsDaylightSavingTime (afterDLSStart), "Expected Daylight Savings " + afterDLSStart.ToString ()); + } + + [Test] + public void CanGetLocalUnity () + { + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.IsTrue (local.Id == "Local"); + } + + [Test] + public void LocalIsNotLocalUnityOnDesktop () + { + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreNotEqual (local, TimeZoneInfo.Local); + } + + [Test] + public void EST () + { + Environment.SetEnvironmentVariable ("TZ", "America/New_York"); + TimeZoneInfo local = GetLocalUnity (); + + Assert.IsNotNull (local); + Assert.AreEqual ("-05:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("EST", local.StandardName); + Assert.AreEqual ("EDT", local.DaylightName); + Assert.IsTrue (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT-05:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,2,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual(6, StandardTimeConverted.Hour); + + var UTCInDaylightMonth = new DateTime (2018,7,5,11,22,56); + var DaylightTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInDaylightMonth, local); + Assert.AreEqual (7, DaylightTimeConverted.Hour); + + //Before DLS Supported Year + AssertNoDLS (local, + new DateTime (1970,4,25), + new DateTime (1970,4,27), + new DateTime (1970,10,24), + new DateTime (1970,10,26) + ); + + //First DLS Supported Year + AssertDLS (local, + new DateTime (1971,4,24), + new DateTime (1971,4,26), + new DateTime (1971,10,30), + new DateTime (1971,11,1) + ); + + //Near current year + AssertDLS (local, + new DateTime (2018,3,10), + new DateTime (2018,3,12), + new DateTime (2018,11,3), + new DateTime (2018,11,5) + ); + + //Last DLS Supported Year + AssertDLS (local, + new DateTime (2037,3,7), + new DateTime (2037,3,9), + new DateTime (2037,10,30), + new DateTime (2037,11,2) + ); + + //After Last DLS Supported Year + AssertNoDLS (local, + new DateTime (2038,2,1), + new DateTime (2038,5,1), + new DateTime (2038,6,1), + new DateTime (2038,12,1) + ); + } + + [Test] + public void PST () + { + Environment.SetEnvironmentVariable ("TZ", "America/Los_Angeles"); + TimeZoneInfo local = GetLocalUnity (); + + Assert.IsNotNull (local); + Assert.AreEqual ("-08:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("PST", local.StandardName); + Assert.AreEqual ("PDT", local.DaylightName); + Assert.IsTrue (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT-08:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,2,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (3, StandardTimeConverted.Hour); + + var UTCInDaylightMonth = new DateTime (2018,7,5,11,22,56); + var DaylightTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInDaylightMonth, local); + Assert.AreEqual (4, DaylightTimeConverted.Hour); + + //Before DLS Supported Year + AssertNoDLS (local, + new DateTime (1970,4,25), + new DateTime (1970,4,27), + new DateTime (1970,10,24), + new DateTime (1970,10,26) + ); + + //First DLS Supported Year + AssertDLS (local, + new DateTime (1971,4,24), + new DateTime (1971,4,26), + new DateTime (1971,10,30), + new DateTime (1971,11,1) + ); + + //Near current year + AssertDLS (local, + new DateTime (2018,3,10), + new DateTime (2018,3,12), + new DateTime (2018,11,3), + new DateTime (2018,11,5) + ); + + //Last DLS Supported Year + AssertDLS (local, + new DateTime (2037,3,7), + new DateTime (2037,3,9), + new DateTime (2037,10,30), + new DateTime (2037,11,2) + ); + + //After Last DLS Supported Year + AssertNoDLS (local, + new DateTime (2038,2,1), + new DateTime (2038,5,1), + new DateTime (2038,6,1), + new DateTime (2038,12,1) + ); + } + + [Test] + public void MST_Arizona () + { + //Arizona is special in that there hasn't been daylight savings since 1967 (before our first supported year) + Environment.SetEnvironmentVariable ("TZ", "America/Phoenix"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreEqual ("-07:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("MST", local.StandardName); + Assert.AreEqual ("", local.DaylightName); + Assert.IsFalse (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT-07:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,2,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (4, StandardTimeConverted.Hour); + } + + [Test] + public void EET_Egypt () + { + //Egypt is special in that it stopped doing daylight savings in 2014 (after our first supported year) + Environment.SetEnvironmentVariable ("TZ", "Egypt"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreEqual ("02:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("EET", local.StandardName); + Assert.AreEqual ("", local.DaylightName); + Assert.IsFalse (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT+02:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,2,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (13, StandardTimeConverted.Hour); + } + + [Test] + public void MSK_Crimea () + { + //Crimea is special, because they switched form EET(with dls) to MSK (without dls) due to world conflicts in 2014 + //C# timezoneinfo class only supports a single utc offset and transition times only account for daylight savings changes. + //In this case, it will display MSK with no support for daylight savings + Environment.SetEnvironmentVariable ("TZ", "Europe/Simferopol"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreEqual ("03:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("MSK", local.StandardName); + Assert.AreEqual ("", local.DaylightName); + Assert.IsFalse (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT+03:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,2,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (14, StandardTimeConverted.Hour); + } + + [Test] + public void SA_Samoa () + { + //Samoa is special, switched form -10/-11 to +13/+14 in 2011...The Timezoneinfo class only supports a single base utcoffset so + //years before 2011 will not have daylight savings information + Environment.SetEnvironmentVariable ("TZ", "Pacific/Apia"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreEqual ("13:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("+13", local.StandardName); + Assert.AreEqual ("+14", local.DaylightName); + Assert.IsTrue (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT+13:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,4,5,1,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (14, StandardTimeConverted.Hour); + + var UTCInDaylightMonth = new DateTime (2018,11,5,1,22,56); + var DaylightTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInDaylightMonth, local); + Assert.AreEqual (15, DaylightTimeConverted.Hour); + + //2011 transitioned from utc -10 to utc +14...since we switched timezones in this year, we shouldn't have any DLS info for this year and prior + AssertNoDLS (local, + new DateTime (2011,4,1), + new DateTime (2011,4,3), + new DateTime (2011,12,28), + new DateTime (2011,12,31) + ); + AssertNoDLS (local, + new DateTime (2010,9,24), + new DateTime (2010,9,26), + new DateTime (2010,12,28), + new DateTime (2010,12,31) + ); + + //Near current year + AssertDLSInverse (local, + new DateTime (2018,3,31), + new DateTime (2018,4,2), + new DateTime (2018,9,29), + new DateTime (2018,10,1) + ); + } + + [Test] + public void AEST () + { + //Timezone that begins Jan 1 in DLS time + Environment.SetEnvironmentVariable ("TZ", "Australia/Sydney"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + Assert.AreEqual ("10:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("AEST", local.StandardName); + Assert.AreEqual ("AEDT", local.DaylightName); + Assert.IsTrue (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT+10:00) Local Time", local.DisplayName); + Assert.IsTrue (local.GetAdjustmentRules ().Length > 0); + + var UTCInStandardMonth = new DateTime (2018,5,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (21, StandardTimeConverted.Hour); + + var UTCInDaylightMonth = new DateTime (2018,1,5,11,22,56); + var DaylightTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInDaylightMonth, local); + Assert.AreEqual (22, DaylightTimeConverted.Hour); + + //Before DLS Supported Year + AssertNoDLS (local, + new DateTime (1970,4,25), + new DateTime (1970,4,27), + new DateTime (1970,10,24), + new DateTime (1970,10,26) + ); + + //First DLS Supported Year...Austrialia did not start DLS until oct of 71 + AssertNoDLS (local, + new DateTime (1971,10,30), + new DateTime (1971,11,1), + new DateTime (1971,2,26), + new DateTime (1971,2,28) + ); + + AssertDLSInverse (local, + new DateTime (1972,2,26), + new DateTime (1972,2,28), + new DateTime (1972,10,28), + new DateTime (1972,10,30) + ); + + //Near current year + AssertDLSInverse (local, + new DateTime (2018,3,31), + new DateTime (2018,4,2), + new DateTime (2018,10,6), + new DateTime (2018,10,8) + ); + + //Last DLS Supported Year + AssertDLSInverse (local, + new DateTime (2037,4,4), + new DateTime (2037,4,6), + new DateTime (2037,10,3), + new DateTime (2037,10,5) + ); + + //After Last DLS Supported Year + AssertNoDLS (local, + new DateTime (2038,2,1), + new DateTime (2038,5,1), + new DateTime (2038,6,1), + new DateTime (2038,12,1) + ); + } + + [Test] + public void Singapore () + { + //Singapore changes it's timezone from +0730 to +08...use the latest offsets + Environment.SetEnvironmentVariable ("TZ", "Asia/Singapore"); + TimeZoneInfo local = GetLocalUnity (); + Assert.IsNotNull (local); + + Assert.AreEqual ("08:00:00", local.BaseUtcOffset.ToString ()); + Assert.AreEqual ("Local", local.Id); + Assert.AreEqual ("+08", local.StandardName); + Assert.AreEqual ("", local.DaylightName); + Assert.IsFalse (local.SupportsDaylightSavingTime); + Assert.AreEqual ("(GMT+08:00) Local Time", local.DisplayName); + + var UTCInStandardMonth = new DateTime (2018,5,5,11,22,56); + var StandardTimeConverted = TimeZoneInfo.ConvertTimeFromUtc (UTCInStandardMonth, local); + Assert.AreEqual (19, StandardTimeConverted.Hour); + } + + } +#endif + [TestFixture] public class CreateCustomTimezoneTests { diff --git a/mcs/class/corlib/Test/System/TypeTest.cs b/mcs/class/corlib/Test/System/TypeTest.cs index 6608ef9cc430..2c9bd2728afd 100644 --- a/mcs/class/corlib/Test/System/TypeTest.cs +++ b/mcs/class/corlib/Test/System/TypeTest.cs @@ -4886,6 +4886,30 @@ public void NullFullNameForSpecificGenericTypes() } } + // https://github.com/mono/mono/issues/6579 + [Test] + public void GetInterfaceCaseInsensitiveTest() + { + var type = typeof(Dictionary); + + Assert.NotNull ( + type.GetInterface ("System.Collections.IDictionary", false), + "strict named interface must be found (ignoreCase = false)" + ); + Assert.NotNull ( + type.GetInterface ("System.Collections.IDictionary", true), + "strict named interface must be found (ignoreCase = true)" + ); + Assert.Null ( + type.GetInterface ("System.Collections.Idictionary", false), + "interface, named in mixed case, must not be found (ignoreCase = false)" + ); + Assert.NotNull ( + type.GetInterface ("System.Collections.Idictionary", true), + "interface, named in mixed case, must be found (ignoreCase = true)" + ); + } + interface Bug59738Interface { } diff --git a/mcs/class/corlib/corlib-net_4_x.csproj b/mcs/class/corlib/corlib-net_4_x.csproj index f0e0c847d895..0748b480b981 100644 --- a/mcs/class/corlib/corlib-net_4_x.csproj +++ b/mcs/class/corlib/corlib-net_4_x.csproj @@ -293,11 +293,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + @@ -980,22 +1016,6 @@ - - - - - - - - - - - - - - - - diff --git a/mcs/class/corlib/corlib.dll.sources b/mcs/class/corlib/corlib.dll.sources index fa940530add6..67203168c076 100644 --- a/mcs/class/corlib/corlib.dll.sources +++ b/mcs/class/corlib/corlib.dll.sources @@ -104,6 +104,7 @@ System/Delegate.cs System/DelegateSerializationHolder.cs System/DomainManagerInitializationFlags.cs System/EmptyArray.cs +System/EnumSharing.cs System/Environment.cs System/EnvironmentVariableTarget.cs System/Guid.cs @@ -142,6 +143,7 @@ System/TimeZoneInfo.cs System/TimeZoneInfo.Android.cs System/TimeZoneInfo.MonoTouch.cs System/TimeZoneInfo.Serialization.cs +System/TimeZoneInfo.Unity.cs System/TimeZoneInfo.WinRT.cs ../../build/common/MonoTODOAttribute.cs System/TypeIdentifier.cs @@ -165,22 +167,6 @@ System.Deployment.Internal/InternalApplicationIdentityHelper.cs System.Diagnostics/Debugger.cs System.Diagnostics/StackFrame.cs System.Diagnostics/StackTrace.cs -System.Diagnostics.Tracing/EventAttribute.cs -System.Diagnostics.Tracing/EventCommand.cs -System.Diagnostics.Tracing/EventCounter.cs -System.Diagnostics.Tracing/EventSource.cs -System.Diagnostics.Tracing/EventSourceAttribute.cs -System.Diagnostics.Tracing/EventSourceSettings.cs -System.Diagnostics.Tracing/EventCommandEventArgs.cs -System.Diagnostics.Tracing/EventListener.cs -System.Diagnostics.Tracing/EventWrittenEventArgs.cs -System.Diagnostics.Tracing/NonEventAttribute.cs -System.Diagnostics.Tracing/EventDataAttribute.cs -System.Diagnostics.Tracing/EventFieldAttribute.cs -System.Diagnostics.Tracing/EventFieldFormat.cs -System.Diagnostics.Tracing/EventFieldTags.cs -System.Diagnostics.Tracing/EventIgnoreAttribute.cs -System.Diagnostics.Tracing/EventManifestOptions.cs System.Diagnostics.SymbolStore/ISymbolBinder.cs System.Diagnostics.SymbolStore/ISymbolBinder1.cs System.Diagnostics.SymbolStore/ISymbolDocument.cs @@ -1097,12 +1083,49 @@ ReferenceSources/AppContextDefaultValues.cs ../referencesource/mscorlib/system/diagnostics/contracts/contracts.cs ../referencesource/mscorlib/system/diagnostics/contracts/contractsbcl.cs +../referencesource/mscorlib/system/diagnostics/eventing/activitytracker.cs ../referencesource/mscorlib/system/diagnostics/eventing/eventactivityoptions.cs +../referencesource/mscorlib/system/diagnostics/eventing/eventdescriptor.cs +../referencesource/mscorlib/system/diagnostics/eventing/eventprovider.cs +../referencesource/mscorlib/system/diagnostics/eventing/eventsource.cs ../referencesource/mscorlib/system/diagnostics/eventing/eventsourceexception.cs +../referencesource/mscorlib/system/diagnostics/eventing/StubEnvironment.cs ../referencesource/mscorlib/system/diagnostics/eventing/winmeta.cs +../../../external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventCounter.cs +../../../external/corefx-bugfix/src/Common/src/CoreLib/System/Diagnostics/Tracing/unsafenativemethods.cs + +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/ArrayTypeInfo.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/ConcurrentSet.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/ConcurrentSetItem.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/DataCollector.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EmptyStruct.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EnumerableTypeInfo.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EnumHelper.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventDataAttribute.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventFieldAttribute.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventFieldFormat.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventIgnoreAttribute.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventPayload.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventSourceActivity.cs ../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/EventSourceOptions.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/FieldMetadata.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/InvokeTypeInfo.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/NameInfo.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/PropertyAccessor.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/PropertyAnalysis.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleEventTypes.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleTypeInfos.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/Statics.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingDataCollector.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingDataType.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventSource.cs ../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventTraits.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventTypes.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingMetadataCollector.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingTypeInfo.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingTypeInfo_T.cs +../referencesource/mscorlib/system/diagnostics/eventing/TraceLogging/TypeAnalysis.cs ../referencesource/mscorlib/system/globalization/bidicategory.cs ../referencesource/mscorlib/system/globalization/calendar.cs @@ -1110,7 +1133,9 @@ ReferenceSources/AppContextDefaultValues.cs ../referencesource/mscorlib/system/globalization/CalendricalCalculationsHelper.cs ../referencesource/mscorlib/system/globalization/calendardata.cs ../referencesource/mscorlib/system/globalization/calendarweekrule.cs -../referencesource/mscorlib/system/globalization/charunicodeinfo.cs +System/Debug.cs +../../../external/corert/src/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs +../../../external/corert/src/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfoData.cs ../referencesource/mscorlib/system/globalization/chineselunisolarcalendar.cs ../referencesource/mscorlib/system/globalization/compareinfo.cs ../referencesource/mscorlib/system/globalization/culturenotfoundexception.cs diff --git a/mcs/class/referencesource/System.Core/System/Linq/Enumerable.cs b/mcs/class/referencesource/System.Core/System/Linq/Enumerable.cs index 84a043557188..91fdedb40f99 100644 --- a/mcs/class/referencesource/System.Core/System/Linq/Enumerable.cs +++ b/mcs/class/referencesource/System.Core/System/Linq/Enumerable.cs @@ -2776,7 +2776,11 @@ public string Empty { get { +#if UNITY_AOT + return SR.EmptyEnumerable; +#else return Strings.EmptyEnumerable; +#endif } } } @@ -2829,4 +2833,49 @@ public object[] Items [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] private int count; } + +#if UNITY_AOT + // + /// An iterator that can produce an array or through an optimized path. + /// + internal interface IIListProvider : IEnumerable + { + /// + /// Produce an array of the sequence through an optimized path. + /// + /// The array. + TElement[] ToArray(); + + /// + /// Produce a of the sequence through an optimized path. + /// + /// The . + List ToList(); + + /// + /// Returns the count of elements in the sequence. + /// + /// If true then the count should only be calculated if doing + /// so is quick (sure or likely to be constant time), otherwise -1 should be returned. + /// The number of elements. + int GetCount(bool onlyIfCheap); + } + + internal static partial class Error + { + internal static Exception ArgumentNull(string s) => new ArgumentNullException(s); + + internal static Exception ArgumentOutOfRange(string s) => new ArgumentOutOfRangeException(s); + + internal static Exception MoreThanOneElement() => new InvalidOperationException(SR.MoreThanOneElement); + + internal static Exception MoreThanOneMatch() => new InvalidOperationException(SR.MoreThanOneMatch); + + internal static Exception NoElements() => new InvalidOperationException(SR.NoElements); + + internal static Exception NoMatch() => new InvalidOperationException(SR.NoMatch); + + internal static Exception NotSupported() => new NotSupportedException(); + } +#endif } diff --git a/mcs/class/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/IDataContractSurrogate.cs b/mcs/class/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/IDataContractSurrogate.cs index 6063e911271b..76922d7b5706 100644 --- a/mcs/class/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/IDataContractSurrogate.cs +++ b/mcs/class/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/IDataContractSurrogate.cs @@ -17,7 +17,7 @@ public interface IDataContractSurrogate object GetCustomDataToExport(Type clrType, Type dataContractType); void GetKnownCustomDataTypes(Collection customDataTypes); Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData); -#if !NO_CODEDOM +#if !NO_CODEDOM || UNITY_AOT CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit); #endif } @@ -70,11 +70,11 @@ internal static Type GetReferencedTypeOnImport(IDataContractSurrogate surrogate, return null; return surrogate.GetReferencedTypeOnImport(typeName, typeNamespace, customData); } -#if !NO_CODEDOM +#if !NO_CODEDOM || UNITY_AOT internal static CodeTypeDeclaration ProcessImportedType(IDataContractSurrogate surrogate, CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) { return surrogate.ProcessImportedType(typeDeclaration, compileUnit); } #endif } -} +} \ No newline at end of file diff --git a/mcs/class/referencesource/System/compmod/system/diagnostics/DiagnosticsConfiguration.cs b/mcs/class/referencesource/System/compmod/system/diagnostics/DiagnosticsConfiguration.cs index 5b5c81384b82..8bfb15ee1d38 100644 --- a/mcs/class/referencesource/System/compmod/system/diagnostics/DiagnosticsConfiguration.cs +++ b/mcs/class/referencesource/System/compmod/system/diagnostics/DiagnosticsConfiguration.cs @@ -168,8 +168,10 @@ internal static SystemDiagnosticsSection SystemDiagnosticsSection { } private static SystemDiagnosticsSection GetConfigSection() { - SystemDiagnosticsSection configSection = (SystemDiagnosticsSection) PrivilegedConfigurationManager.GetSection("system.diagnostics"); - return configSection; + object o = PrivilegedConfigurationManager.GetSection("system.diagnostics"); + if (o is SystemDiagnosticsSection) + return (SystemDiagnosticsSection)o; + return null; } internal static bool IsInitializing() { diff --git a/mcs/class/referencesource/System/net/System/Net/NetworkInformation/IPGlobalProperties.cs b/mcs/class/referencesource/System/net/System/Net/NetworkInformation/IPGlobalProperties.cs index 0b5a43448f12..2e7624968531 100644 --- a/mcs/class/referencesource/System/net/System/Net/NetworkInformation/IPGlobalProperties.cs +++ b/mcs/class/referencesource/System/net/System/Net/NetworkInformation/IPGlobalProperties.cs @@ -12,6 +12,11 @@ namespace System.Net.NetworkInformation public abstract class IPGlobalProperties { +#if UNITY + // defaults to false, but may be replaced by intrinsic in runtime + static bool PlatformNeedsLibCWorkaround { get; } +#endif + public static IPGlobalProperties GetIPGlobalProperties() { #if MONODROID @@ -21,6 +26,10 @@ public static IPGlobalProperties GetIPGlobalProperties() #elif MONO switch (Environment.OSVersion.Platform) { case PlatformID.Unix: +#if UNITY + if (PlatformNeedsLibCWorkaround) + return new UnixNoLibCIPGlobalProperties(); +#endif MibIPGlobalProperties impl = null; if (Directory.Exists (MibIPGlobalProperties.ProcDir)) { impl = new MibIPGlobalProperties (MibIPGlobalProperties.ProcDir); diff --git a/mcs/class/referencesource/System/net/System/Net/UnicodeDecodingConformance.cs b/mcs/class/referencesource/System/net/System/Net/UnicodeDecodingConformance.cs index 5b4ce4502114..2441d13b6650 100644 --- a/mcs/class/referencesource/System/net/System/Net/UnicodeDecodingConformance.cs +++ b/mcs/class/referencesource/System/net/System/Net/UnicodeDecodingConformance.cs @@ -16,7 +16,7 @@ namespace System.Net.Configuration /// See http://www.w3.org/International/questions/qa-escapes#bytheway for more information /// on how Unicode characters in the SMP are supposed to be encoded in HTML. /// -#if !MOBILE +#if !MOBILE || UNITY_AOT public #endif enum UnicodeDecodingConformance diff --git a/mcs/class/referencesource/System/net/System/Net/UnicodeEncodingConformance.cs b/mcs/class/referencesource/System/net/System/Net/UnicodeEncodingConformance.cs index 7ac444e1a162..670954cca7a6 100644 --- a/mcs/class/referencesource/System/net/System/Net/UnicodeEncodingConformance.cs +++ b/mcs/class/referencesource/System/net/System/Net/UnicodeEncodingConformance.cs @@ -16,7 +16,7 @@ namespace System.Net.Configuration /// See http://www.w3.org/International/questions/qa-escapes#bytheway for more information /// on how Unicode characters in the SMP are supposed to be encoded in HTML. /// -#if !MOBILE +#if !MOBILE || UNITY_AOT public #endif enum UnicodeEncodingConformance diff --git a/mcs/class/referencesource/System/net/System/Net/webclient.cs b/mcs/class/referencesource/System/net/System/Net/webclient.cs index 96cf1c91007b..3b6da7ced091 100644 --- a/mcs/class/referencesource/System/net/System/Net/webclient.cs +++ b/mcs/class/referencesource/System/net/System/Net/webclient.cs @@ -1805,20 +1805,9 @@ private void OpenReadOperationCompleted(object arg) { OnOpenReadCompleted((OpenReadCompletedEventArgs)arg); } private void OpenReadAsyncCallback(IAsyncResult result) { -#if MONO - // It can be removed when we are full referencesource - AsyncOperation asyncOp = (AsyncOperation) result.AsyncState; - WebRequest request; - if (result is WebAsyncResult) { - request = (WebRequest) ((WebAsyncResult) result).AsyncObject; - } else { - request = (WebRequest) ((LazyAsyncResult) result).AsyncObject; - } -#else - LazyAsyncResult lazyAsyncResult = (LazyAsyncResult) result; - AsyncOperation asyncOp = (AsyncOperation) lazyAsyncResult.AsyncState; - WebRequest request = (WebRequest) lazyAsyncResult.AsyncObject; -#endif + Tuple userData = (Tuple)result.AsyncState; + WebRequest request = userData.Item1; + AsyncOperation asyncOp = userData.Item2; Stream stream = null; Exception exception = null; try { @@ -1857,7 +1846,7 @@ public void OpenReadAsync(Uri address, object userToken) m_AsyncOp = asyncOp; try { WebRequest request = m_WebRequest = GetWebRequest(GetUri(address)); - request.BeginGetResponse(new AsyncCallback(OpenReadAsyncCallback), asyncOp); + request.BeginGetResponse(new AsyncCallback(OpenReadAsyncCallback), new Tuple(request, asyncOp)); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; @@ -1887,13 +1876,9 @@ private void OpenWriteOperationCompleted(object arg) { OnOpenWriteCompleted((OpenWriteCompletedEventArgs)arg); } private void OpenWriteAsyncCallback(IAsyncResult result) { -#if MONO - var lazyAsyncResult = (WebAsyncResult) result; -#else - LazyAsyncResult lazyAsyncResult = (LazyAsyncResult) result; -#endif - AsyncOperation asyncOp = (AsyncOperation) lazyAsyncResult.AsyncState; - WebRequest request = (WebRequest) lazyAsyncResult.AsyncObject; + Tuple userData = (Tuple)result.AsyncState; + WebRequest request = userData.Item1; + AsyncOperation asyncOp = userData.Item2; WebClientWriteStream stream = null; Exception exception = null; @@ -1943,7 +1928,7 @@ public void OpenWriteAsync(Uri address, string method, object userToken) try { m_Method = method; WebRequest request = m_WebRequest = GetWebRequest(GetUri(address)); - request.BeginGetRequestStream(new AsyncCallback(OpenWriteAsyncCallback), asyncOp); + request.BeginGetRequestStream(new AsyncCallback(OpenWriteAsyncCallback), new Tuple(request, asyncOp)); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; diff --git a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs index 4fc2f7c2078c..10fb4ffe220c 100644 --- a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs +++ b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs @@ -230,7 +230,7 @@ public int ExitCode { get { EnsureState(State.Exited); #if MONO - if (exitCode == -1) + if (exitCode == -1 && !Environment.IsRunningOnWindows) throw new InvalidOperationException ("Cannot get the exit code from a non-child process on Unix"); #endif return exitCode; diff --git a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessModuleCollection.cs b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessModuleCollection.cs index 85d996b827a5..c774c51d173b 100644 --- a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessModuleCollection.cs +++ b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessModuleCollection.cs @@ -54,7 +54,7 @@ public void CopyTo(ProcessModule[] array, int index) { InnerList.CopyTo(array, index); } -#if MOBILE +#if MOBILE && !UNITY [Obsolete ("This API is no longer available", true)] public int Capacity { get { diff --git a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessThreadCollection.cs b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessThreadCollection.cs index 94fa2f43bbd1..80e77f748dd5 100644 --- a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessThreadCollection.cs +++ b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/ProcessThreadCollection.cs @@ -78,7 +78,7 @@ public void CopyTo(ProcessThread[] array, int index) { InnerList.CopyTo(array, index); } -#if MOBILE +#if MOBILE && !UNITY [Obsolete ("This API is no longer available", true)] public int Capacity { get { diff --git a/mcs/class/referencesource/mscorlib/system/AppContext/AppContextSwitches.cs b/mcs/class/referencesource/mscorlib/system/AppContext/AppContextSwitches.cs index 432d81001aa2..79c6bf86ae00 100644 --- a/mcs/class/referencesource/mscorlib/system/AppContext/AppContextSwitches.cs +++ b/mcs/class/referencesource/mscorlib/system/AppContext/AppContextSwitches.cs @@ -15,6 +15,7 @@ internal static class AppContextSwitches public static readonly bool ThrowExceptionIfDisposedCancellationTokenSource = false; public static readonly bool SetActorAsReferenceWhenCopyingClaimsIdentity = false; public static readonly bool NoAsyncCurrentCulture = false; + public static readonly bool PreserveEventListnerObjectIdentity = false; #else private static int _noAsyncCurrentCulture; public static bool NoAsyncCurrentCulture diff --git a/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs b/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs index 44210ced5e3f..2c68adc18381 100644 --- a/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs +++ b/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs @@ -732,9 +732,13 @@ public void InsertRange(int index, IEnumerable collection) { Array.Copy(_items, index+count, _items, index*2, _size-index); } else { +#if MONO + c.CopyTo(_items, index); +#else T[] itemsToInsert = new T[count]; c.CopyTo(itemsToInsert, 0); itemsToInsert.CopyTo(_items, index); +#endif } _size += count; } diff --git a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/activitytracker.cs b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/activitytracker.cs index 22be307d6eea..a77b897e37da 100644 --- a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/activitytracker.cs +++ b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/activitytracker.cs @@ -61,7 +61,7 @@ public void OnStart(string providerName, string activityName, int task, ref Guid if (m_checkedForEnable) return; m_checkedForEnable = true; -#if ES_BUILD_STANDALONE +#if ES_BUILD_STANDALONE || MONO Enable(); // Enable it unconditionally. #else if (System.Threading.Tasks.TplEtwProvider.Log.IsEnabled(EventLevel.Informational, System.Threading.Tasks.TplEtwProvider.Keywords.TasksFlowActivityIds)) @@ -609,7 +609,7 @@ void ActivityChanging(AsyncLocalValueChangedArgs args) #endregion } -#if ES_BUILD_STANDALONE +#if ES_BUILD_STANDALONE || MONO /******************************** SUPPORT *****************************/ /// /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created diff --git a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventprovider.cs b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventprovider.cs index 939d15ee520f..54249f1ce6cc 100644 --- a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventprovider.cs +++ b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventprovider.cs @@ -78,6 +78,7 @@ internal SessionInfo(int sessionIdBit_, int etwSessionId_) [SecurityCritical] UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback; // Trace Callback function + GCHandle m_thisGCHandle; private long m_regHandle; // Trace Registration Handle private byte m_level; // Tracing Level private long m_anyKeywordMask; // Trace Enable Flags @@ -149,13 +150,25 @@ internal EventProvider() internal unsafe void Register(Guid providerGuid) { m_providerId = providerGuid; - uint status; m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack); - status = EventRegister(ref m_providerId, m_etwCallback); - if (status != 0) + if (m_thisGCHandle.IsAllocated) + m_thisGCHandle.Free(); + + m_thisGCHandle = GCHandle.Alloc(this); + + try + { + var status = UnsafeNativeMethods.ManifestEtw.EventRegister(ref providerGuid, m_etwCallback, GCHandle.ToIntPtr(m_thisGCHandle).ToPointer(), ref m_regHandle); + if (status != 0) + { + throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status))); + } + } + catch { - throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status))); + m_thisGCHandle.Free(); + throw; } } @@ -264,22 +277,44 @@ private unsafe void Deregister() { EventUnregister(); m_regHandle = 0; + m_thisGCHandle.Free(); } } + [AttributeUsage(AttributeTargets.Method)] + private sealed class MonoPInvokeCallbackAttribute : Attribute + { + public MonoPInvokeCallbackAttribute(Type t) + { + } + } + + [MonoPInvokeCallback(typeof(UnsafeNativeMethods.ManifestEtw.EtwEnableCallback))] + unsafe static void EtwEnableCallBack( + [In] ref System.Guid sourceId, + [In] int controlCode, + [In] byte setLevel, + [In] long anyKeyword, + [In] long allKeyword, + [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, + [In] void* callbackContext + ) + { + var _this = (EventProvider)GCHandle.FromIntPtr(new IntPtr(callbackContext)).Target; + _this.EtwEnableCallBackImpl(controlCode, setLevel, anyKeyword, allKeyword, filterData); + } + // // // // [System.Security.SecurityCritical] - unsafe void EtwEnableCallBack( - [In] ref System.Guid sourceId, + unsafe void EtwEnableCallBackImpl( [In] int controlCode, [In] byte setLevel, [In] long anyKeyword, [In] long allKeyword, - [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, - [In] void* callbackContext + [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData ) { // This is an optional callback API. We will therefore ignore any failures that happen as a @@ -476,9 +511,17 @@ private unsafe void GetSessionInfo(Action action) fixed (Guid* provider = &m_providerId) { - hr = UnsafeNativeMethods.ManifestEtw.EnumerateTraceGuidsEx(UnsafeNativeMethods.ManifestEtw.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo, - provider, sizeof(Guid), buffer, buffSize, ref buffSize); - } + try + { + hr = UnsafeNativeMethods.ManifestEtw.EnumerateTraceGuidsEx(UnsafeNativeMethods.ManifestEtw.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo, + provider, sizeof(Guid), buffer, buffSize, ref buffSize); + } + catch (DllNotFoundException) + { + // This API isn't available on UWP prior to Windows SDK 16299 + return; + } + } if (hr == 0) break; if (hr != 122 /* ERROR_INSUFFICIENT_BUFFER */) @@ -539,7 +582,7 @@ private unsafe bool GetDataFromController(int etwSessionId, dataStart = 0; if (filterData == null) { -#if !ES_BUILD_PCL +#if !ES_BUILD_PCL && !MONO string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; @@ -1161,13 +1204,6 @@ internal unsafe bool WriteEventRaw( // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available). - [SecurityCritical] - private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback) - { - m_providerId = providerId; - m_etwCallback = enableCallback; - return UnsafeNativeMethods.ManifestEtw.EventRegister(ref providerId, enableCallback, null, ref m_regHandle); - } [SecurityCritical] private uint EventUnregister() diff --git a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventsource.cs b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventsource.cs index 6bc1f76856fb..b8923813894a 100644 --- a/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventsource.cs +++ b/mcs/class/referencesource/mscorlib/system/diagnostics/eventing/eventsource.cs @@ -200,6 +200,12 @@ using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; #endif +#if MONO +using TplEtwProvider = System.Diagnostics.Tracing.TplEtwProvider; +#else +using TplEtwProvider = System.Threading.Tasks.TplEtwProvider; +#endif + #if ES_BUILD_STANDALONE namespace Microsoft.Diagnostics.Tracing #else @@ -551,8 +557,8 @@ public static void SetCurrentThreadActivityId(Guid activityId) } #endif // FEATURE_ACTIVITYSAMPLING } - if (System.Threading.Tasks.TplEtwProvider.Log != null) - System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId); + if (TplEtwProvider.Log != null) + TplEtwProvider.Log.SetActivityId(activityId); } /// @@ -586,8 +592,8 @@ public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActiv // We don't call the activityDying callback here because the caller has declared that // it is not dying. - if (System.Threading.Tasks.TplEtwProvider.Log != null) - System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId); + if (TplEtwProvider.Log != null) + TplEtwProvider.Log.SetActivityId(activityId); } /// @@ -4904,7 +4910,7 @@ internal set } -#if FEATURE_MANAGED_ETW_CHANNELS +#if FEATURE_MANAGED_ETW_CHANNELS || MONO /// /// Gets the channel for the event. /// @@ -5044,7 +5050,7 @@ internal bool IsOpcodeSet /// Event's task: allows logical grouping of events public EventTask Task { get; set; } -#if FEATURE_MANAGED_ETW_CHANNELS +#if FEATURE_MANAGED_ETW_CHANNELS || MONO /// Event's channel: defines an event log as an additional destination for the event public EventChannel Channel { get; set; } #endif @@ -6494,7 +6500,11 @@ private string CreateManifestString() // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to // access BinaryCompatibility. +#if MONO + ArraySortHelper.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer.Default.Compare); +#else ArraySortHelper.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer.Default); +#endif #endif foreach (var ci in cultures) { diff --git a/mcs/class/referencesource/mscorlib/system/exception.cs b/mcs/class/referencesource/mscorlib/system/exception.cs index a539756ee162..81e472443d7d 100644 --- a/mcs/class/referencesource/mscorlib/system/exception.cs +++ b/mcs/class/referencesource/mscorlib/system/exception.cs @@ -1114,6 +1114,9 @@ internal Exception FixRemotingException () return this; } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void ReportUnhandledException(Exception exception); #endif } diff --git a/mcs/class/referencesource/mscorlib/system/reflection/binder.cs b/mcs/class/referencesource/mscorlib/system/reflection/binder.cs index 3d14d37f29c3..dd8d60f2f506 100644 --- a/mcs/class/referencesource/mscorlib/system/reflection/binder.cs +++ b/mcs/class/referencesource/mscorlib/system/reflection/binder.cs @@ -52,7 +52,7 @@ public abstract PropertyInfo SelectProperty(BindingFlags bindingAttr,PropertyInf public abstract void ReorderArgumentArray(ref Object[] args, Object state); -#if !FEATURE_COMINTEROP +#if !FEATURE_COMINTEROP && !UNITY // CanChangeType // This method checks whether the value can be converted into the property type. public virtual bool CanChangeType(Object value,Type type,CultureInfo culture) diff --git a/mcs/class/referencesource/mscorlib/system/rttype.cs b/mcs/class/referencesource/mscorlib/system/rttype.cs index 61b46433b342..42a445bc5cf5 100644 --- a/mcs/class/referencesource/mscorlib/system/rttype.cs +++ b/mcs/class/referencesource/mscorlib/system/rttype.cs @@ -3413,9 +3413,12 @@ public override Type GetInterface(String fullname, bool ignoreCase) #if MONO List list = null; + var nameComparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; foreach (RuntimeType t in GetInterfaces ()) { - if (t.Name != name) - continue; + + if (!String.Equals(t.Name, name, nameComparison)) { + continue; + } if (list == null) list = new List (2); diff --git a/mcs/class/referencesource/mscorlib/system/runtime/interopservices/attributes.cs b/mcs/class/referencesource/mscorlib/system/runtime/interopservices/attributes.cs index a970216007b9..1c5f52bcbb26 100644 --- a/mcs/class/referencesource/mscorlib/system/runtime/interopservices/attributes.cs +++ b/mcs/class/referencesource/mscorlib/system/runtime/interopservices/attributes.cs @@ -1,4 +1,4 @@ -#if MONO_COM +#if MONO_COM || UNITY_AOT #define FEATURE_COMINTEROP #endif // ==++== diff --git a/mcs/class/referencesource/mscorlib/system/runtime/interopservices/windowsruntime/windowsruntimemarshal.cs b/mcs/class/referencesource/mscorlib/system/runtime/interopservices/windowsruntime/windowsruntimemarshal.cs index 15a0c70d64dd..9ef7580c9e30 100644 --- a/mcs/class/referencesource/mscorlib/system/runtime/interopservices/windowsruntime/windowsruntimemarshal.cs +++ b/mcs/class/referencesource/mscorlib/system/runtime/interopservices/windowsruntime/windowsruntimemarshal.cs @@ -1278,7 +1278,7 @@ public static IActivationFactory GetActivationFactory(Type type) if (type == null) throw new ArgumentNullException("type"); -#if FEATURE_COMINTEROP || MONO_COM +#if FEATURE_COMINTEROP || MONO_COM || UNITY_AOT if (type.IsWindowsRuntimeObject && type.IsImport) { return (IActivationFactory)Marshal.GetNativeActivationFactory(type); diff --git a/mcs/class/referencesource/mscorlib/system/threading/synchronizationcontext.cs b/mcs/class/referencesource/mscorlib/system/threading/synchronizationcontext.cs index 98e2f3fb73eb..9e43efdd2811 100644 --- a/mcs/class/referencesource/mscorlib/system/threading/synchronizationcontext.cs +++ b/mcs/class/referencesource/mscorlib/system/threading/synchronizationcontext.cs @@ -252,7 +252,7 @@ public static void SetSynchronizationContext(SynchronizationContext syncContext) ec.SynchronizationContextNoFlow = syncContext; } -#if MOBILE_LEGACY +#if MOBILE_LEGACY && !UNITY_AOT [Obsolete("The method is not supported and will be removed")] public static void SetThreadStaticContext(SynchronizationContext syncContext) { @@ -293,6 +293,11 @@ private static SynchronizationContext GetThreadLocalContext() context = AndroidPlatform.GetDefaultSyncContext (); #endif +#if UNITY_AOT + if (context == null) + context = OSSpecificSynchronizationContext.Get(); +#endif + return context; } @@ -371,4 +376,93 @@ private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, In } #endif } + +#if UNITY_AOT + class OSSpecificSynchronizationContext : SynchronizationContext + { + object m_OSSynchronizationContext; + private static readonly ConditionalWeakTable s_ContextCache = new ConditionalWeakTable(); + + private OSSpecificSynchronizationContext(object osContext) + { + m_OSSynchronizationContext = osContext; + } + + public static OSSpecificSynchronizationContext Get() + { + var osContext = GetOSContext(); + if (osContext == null) + return null; + + return s_ContextCache.GetValue(osContext, _osContext => new OSSpecificSynchronizationContext(_osContext)); + } + + public override SynchronizationContext CreateCopy() + { + return new OSSpecificSynchronizationContext(m_OSSynchronizationContext); + } + + public override void Send(SendOrPostCallback d, object state) + { + throw new NotSupportedException(); + } + + public override void Post(SendOrPostCallback d, object state) + { + var callback = Marshal.GetFunctionPointerForDelegate((InvocationEntryDelegate)InvocationEntry); + var invocationContext = new InvocationContext(d, state); + var invocationContextHandle = GCHandle.Alloc(invocationContext); + PostInternal(m_OSSynchronizationContext, callback, GCHandle.ToIntPtr(invocationContextHandle)); + } + + private delegate void InvocationEntryDelegate(IntPtr arg); + + [MonoPInvokeCallback(typeof(InvocationEntryDelegate))] + private static void InvocationEntry(IntPtr arg) + { + try + { + var invocationContextHandle = GCHandle.FromIntPtr(arg); + var invocationContext = (InvocationContext)invocationContextHandle.Target; + invocationContextHandle.Free(); + invocationContext.Invoke(); + } + catch (Exception e) + { + Exception.ReportUnhandledException(e); + } + } + + [AttributeUsage (AttributeTargets.Method)] + sealed class MonoPInvokeCallbackAttribute : Attribute + { + public MonoPInvokeCallbackAttribute(Type t) + { + } + } + + class InvocationContext + { + private SendOrPostCallback m_Delegate; + private object m_State; + + public InvocationContext(SendOrPostCallback d, object state) + { + m_Delegate = d; + m_State = state; + } + + public void Invoke() + { + m_Delegate(m_State); + } + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private extern static object GetOSContext(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private extern static void PostInternal(object osSynchronizationContext, IntPtr callback, IntPtr arg); + } +#endif } diff --git a/mcs/class/referencesource/mscorlib/system/type.cs b/mcs/class/referencesource/mscorlib/system/type.cs index ef7472f5016d..1f337f0c1fef 100644 --- a/mcs/class/referencesource/mscorlib/system/type.cs +++ b/mcs/class/referencesource/mscorlib/system/type.cs @@ -1249,7 +1249,7 @@ public bool IsCOMObject { get {return IsCOMObjectImpl();} } -#if FEATURE_COMINTEROP || MONO_COM +#if FEATURE_COMINTEROP || MONO_COM || UNITY_AOT internal bool IsWindowsRuntimeObject { [Pure] get { return IsWindowsRuntimeObjectImpl(); } @@ -1313,7 +1313,7 @@ protected virtual bool IsValueTypeImpl() // Protected routine to determine if this class represents a COM object abstract protected bool IsCOMObjectImpl(); -#if FEATURE_COMINTEROP || MONO_COM +#if FEATURE_COMINTEROP || MONO_COM || UNITY_AOT // Protected routine to determine if this class represents a Windows Runtime object virtual internal bool IsWindowsRuntimeObjectImpl() { throw new NotImplementedException(); diff --git a/mcs/class/referencesource/mscorlib/system/typedreference.cs b/mcs/class/referencesource/mscorlib/system/typedreference.cs index 80bef5ab852b..51506733eb7e 100644 --- a/mcs/class/referencesource/mscorlib/system/typedreference.cs +++ b/mcs/class/referencesource/mscorlib/system/typedreference.cs @@ -50,7 +50,7 @@ public static TypedReference MakeTypedReference(Object target, FieldInfo[] flds) if (field == null) throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeFieldInfo")); - if (field.IsInitOnly || field.IsStatic) + if (field.IsStatic) throw new ArgumentException(Environment.GetResourceString("Argument_TypedReferenceInvalidField")); if (targetType != field.GetDeclaringTypeInternal() && !targetType.IsSubclassOf(field.GetDeclaringTypeInternal())) diff --git a/mcs/errors/cs0151-4.cs b/mcs/errors/cs0151-4.cs index 0e45b1a9049d..c9e05589e4dd 100644 --- a/mcs/errors/cs0151-4.cs +++ b/mcs/errors/cs0151-4.cs @@ -1,5 +1,6 @@ // CS0151: A switch expression of type `S1?' cannot be converted to an integral type, bool, char, string, enum or nullable type -// Line: 24 +// Line: 25 +// Compiler options: -langversion:5 using System; diff --git a/mcs/errors/known-issues-net_4_x b/mcs/errors/known-issues-net_4_x index c9ed9317350d..54902e03e7be 100644 --- a/mcs/errors/known-issues-net_4_x +++ b/mcs/errors/known-issues-net_4_x @@ -14,6 +14,9 @@ # Parser problems cs0080.cs +# Undocumented switch governing rules +cs0151-4.cs NO ERROR + # Operators cs0457-2.cs cs0457.cs diff --git a/mcs/mcs/assembly.cs b/mcs/mcs/assembly.cs index aa4c54317a23..96e43e70d999 100644 --- a/mcs/mcs/assembly.cs +++ b/mcs/mcs/assembly.cs @@ -554,7 +554,8 @@ public virtual void Emit () if (prop != null) { AttributeEncoder encoder = new AttributeEncoder (); encoder.EncodeNamedPropertyArgument (prop, new BoolLiteral (Compiler.BuiltinTypes, true, Location.Null)); - SetCustomAttribute (pa.Constructor, encoder.ToArray ()); + SetCustomAttribute (pa.Constructor, encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } } } diff --git a/mcs/mcs/attribute.cs b/mcs/mcs/attribute.cs index 4f520b7487e7..669a9927d0d5 100644 --- a/mcs/mcs/attribute.cs +++ b/mcs/mcs/attribute.cs @@ -1053,8 +1053,10 @@ public void Emit (Dictionary> allEmitted) } byte[] cdata; + List references; if (pos_args == null && named_values == null) { cdata = AttributeEncoder.Empty; + references = null; } else { AttributeEncoder encoder = new AttributeEncoder (); @@ -1127,7 +1129,7 @@ public void Emit (Dictionary> allEmitted) encoder.EncodeEmptyNamedArguments (); } - cdata = encoder.ToArray (); + cdata = encoder.ToArray (out references); } if (!IsConditionallyExcluded (ctor.DeclaringType)) { @@ -1146,6 +1148,8 @@ public void Emit (Dictionary> allEmitted) Error_AttributeEmitError (e.Message); return; } + + context.Module.AddAssemblyReferences (references); } if (!usage_attr.AllowMultiple && allEmitted != null) { @@ -1404,6 +1408,7 @@ public enum EncodedTypeProperties byte[] buffer; int pos; const ushort Version = 1; + List imports; static AttributeEncoder () { @@ -1583,7 +1588,15 @@ public EncodedTypeProperties Encode (TypeSpec type) public void EncodeTypeName (TypeSpec type) { var old_type = type.GetMetaInfo (); - Encode (type.MemberDefinition.IsImported ? old_type.AssemblyQualifiedName : old_type.FullName); + if (type.MemberDefinition.IsImported) { + if (imports == null) + imports = new List (); + + imports.Add (old_type.Assembly); + Encode (old_type.AssemblyQualifiedName); + } else { + Encode (old_type.FullName); + } } public void EncodeTypeName (TypeContainer type) @@ -1664,8 +1677,10 @@ void WriteCompressedValue (int value) Encode (value); } - public byte[] ToArray () + public byte[] ToArray (out List assemblyReferences) { + assemblyReferences = imports; + byte[] buf = new byte[pos]; Array.Copy (buffer, buf, pos); return buf; @@ -1977,7 +1992,8 @@ public void EmitAttribute (FieldBuilder builder, System.Diagnostics.DebuggerBrow encoder.Encode ((int) state); encoder.EncodeEmptyNamedArguments (); - builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } } @@ -2011,7 +2027,8 @@ public void EmitAttribute (AssemblyBuilder builder, System.Diagnostics.Debuggabl encoder.Encode ((int) modes); encoder.EncodeEmptyNamedArguments (); - builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } } @@ -2037,7 +2054,8 @@ public void EmitAttribute (ParameterBuilder builder, decimal value, Location loc encoder.Encode ((uint) bits[0]); encoder.EncodeEmptyNamedArguments (); - builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } public void EmitAttribute (FieldBuilder builder, decimal value, Location loc) @@ -2055,7 +2073,8 @@ public void EmitAttribute (FieldBuilder builder, decimal value, Location loc) encoder.Encode ((uint) bits[0]); encoder.EncodeEmptyNamedArguments (); - builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } } @@ -2079,7 +2098,8 @@ public void EmitAttribute (MethodBuilder builder, StateMachine type) encoder.EncodeTypeName (type); encoder.EncodeEmptyNamedArguments (); - builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + module.AddAssemblyReferences (references); } } diff --git a/mcs/mcs/class.cs b/mcs/mcs/class.cs index da3ec32d6fb0..a2ef29f206cc 100644 --- a/mcs/mcs/class.cs +++ b/mcs/mcs/class.cs @@ -2105,7 +2105,8 @@ void EmitIndexerName () encoder.Encode (GetAttributeDefaultMember ()); encoder.EncodeEmptyNamedArguments (); - TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + Module.AddAssemblyReferences (references); } public override void VerifyMembers () diff --git a/mcs/mcs/convert.cs b/mcs/mcs/convert.cs index f4012fbd2223..2fd5bcc55859 100644 --- a/mcs/mcs/convert.cs +++ b/mcs/mcs/convert.cs @@ -1232,6 +1232,13 @@ public static Expression UserDefinedConversion (ResolveContext rc, Expression so FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); } + if (source_type_expr == source && source_type.IsNullableType) { + operators = MemberCache.GetUserOperator (source_type.TypeArguments [0], Operator.OpType.Implicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + } + if (!implicitOnly) { operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); if (operators != null) { diff --git a/mcs/mcs/field.cs b/mcs/mcs/field.cs index 86bb028defcb..8c6673281436 100644 --- a/mcs/mcs/field.cs +++ b/mcs/mcs/field.cs @@ -542,7 +542,7 @@ void EmitFieldSize (int buffer_size) } ); - fixed_buffer_type.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + fixed_buffer_type.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out _)); #endif // // Don't emit FixedBufferAttribute attribute for private types @@ -559,7 +559,8 @@ void EmitFieldSize (int buffer_size) encoder.Encode (buffer_size); encoder.EncodeEmptyNamedArguments (); - FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray (out var references)); + Module.AddAssemblyReferences (references); } } diff --git a/mcs/mcs/module.cs b/mcs/mcs/module.cs index 00afac6c6042..f990325e137f 100644 --- a/mcs/mcs/module.cs +++ b/mcs/mcs/module.cs @@ -472,6 +472,18 @@ public void AddAttribute (Attribute attr, IMemberContext context) attributes.AddAttribute (attr); } + public void AddAssemblyReferences (List names) + { + if (names == null) + return; + +#if STATIC + foreach (var name in names) { + Builder.__GetAssemblyToken (name); + } +#endif + } + public override void AddTypeContainer (TypeContainer tc) { AddTypeContainerMember (tc); diff --git a/mcs/tests/gtest-647.cs b/mcs/tests/gtest-647.cs new file mode 100644 index 000000000000..4aae641f85f2 --- /dev/null +++ b/mcs/tests/gtest-647.cs @@ -0,0 +1,34 @@ +using System; + +public class Program +{ + public static int Main () + { + int B = default (MyStruct?); + if (MyStruct.counter != 1) + return 1; + + switch (default (MyStruct?)) { + case 0: + break; + default: + return 2; + } + + if (MyStruct.counter != 2) + return 4; + + return 0; + } + + public struct MyStruct + { + public static int counter; + + public static implicit operator int (MyStruct? s) + { + ++counter; + return 0; + } + } +} \ No newline at end of file diff --git a/mcs/tests/ver-il-net_4_x.xml b/mcs/tests/ver-il-net_4_x.xml index 3d4d271f3db2..4180824c10d5 100644 --- a/mcs/tests/ver-il-net_4_x.xml +++ b/mcs/tests/ver-il-net_4_x.xml @@ -11064,7 +11064,7 @@ - 72 + 60 62 @@ -19505,7 +19505,7 @@ - 267 + 255 7 @@ -20144,6 +20144,21 @@ + + + + 99 + + + 7 + + + + + 22 + + + diff --git a/mono/arch/arm64/arm64-codegen.h b/mono/arch/arm64/arm64-codegen.h index 0156b110c06d..9bee54852143 100644 --- a/mono/arch/arm64/arm64-codegen.h +++ b/mono/arch/arm64/arm64-codegen.h @@ -170,7 +170,7 @@ arm_is_bl_disp (void *code, void *target) static G_GNUC_UNUSED inline unsigned int arm_get_disp (void *p, void *target) { - unsigned int disp = ((char*)target - (char*)p) / 4; + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); if (target) g_assert (arm_is_bl_disp (p, target)); @@ -196,7 +196,7 @@ arm_is_disp19 (void *code, void *target) static G_GNUC_UNUSED inline unsigned int arm_get_disp19 (void *p, void *target) { - unsigned int disp = ((char*)target - (char*)p) / 4; + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); if (target) g_assert (arm_is_disp19 (p, target)); @@ -224,7 +224,7 @@ arm_get_disp19 (void *p, void *target) static G_GNUC_UNUSED inline unsigned int arm_get_disp15 (void *p, void *target) { - unsigned int disp = ((char*)target - (char*)p) / 4; + unsigned int disp = (unsigned int)(((char*)target - (char*)p) / 4); return (disp & 0x7fff); } diff --git a/mono/eglib/gdate-unity.c b/mono/eglib/gdate-unity.c new file mode 100644 index 000000000000..c25eda909206 --- /dev/null +++ b/mono/eglib/gdate-unity.c @@ -0,0 +1,8 @@ +#include +#include "Thread-c-api.h" + +void +g_usleep(gulong microseconds) +{ + UnityPalSleep(microseconds/1000); +} diff --git a/mono/eglib/gdir-unity.c b/mono/eglib/gdir-unity.c new file mode 100644 index 000000000000..a3d8eec30e93 --- /dev/null +++ b/mono/eglib/gdir-unity.c @@ -0,0 +1,134 @@ +#include + +#include "Directory-c-api.h" +#include "Error-c-api.h" + +struct _GDir { + UnityPalFindHandle* handle; + gchar* current; + gchar* next; + const gchar* path_for_rewind; +}; + +static gboolean +setup_dir_handle(GDir*dir, const gchar* path, GError **error) +{ + gchar* path_search; + char* result_file_name = NULL; + gint unused_attributes; + UnityPalErrorCode result; + + dir->path_for_rewind = g_strdup (path); + path_search = g_malloc ((strlen(path) + 3)*sizeof(gchar)); + strcpy (path_search, path); +#ifdef G_OS_WIN32 + strcat (path_search, "\\*"); +#else + strcat (path_search, "/*"); +#endif + + dir->handle = UnityPalDirectoryFindHandleNew(path_search); + result = UnityPalDirectoryFindFirstFile(dir->handle, path_search, &result_file_name, &unused_attributes); + if (!UnityPalSuccess(result)) { + if (error) + *error = g_error_new (G_LOG_DOMAIN, g_file_error_from_errno (result), strerror (result)); + g_free (dir); + return FALSE; + } + + while ((strcmp (result_file_name, ".") == 0) || (strcmp (result_file_name, "..") == 0)) { + result = UnityPalDirectoryFindNextFile(dir->handle, &result_file_name, &unused_attributes); + if (!UnityPalSuccess(result)) { + result_file_name = NULL; + break; + } + } + + dir->current = NULL; + dir->next = result_file_name; + return TRUE; +} + +static void close_dir_handle(GDir* dir) +{ + UnityPalDirectoryCloseOSHandle(dir->handle); + UnityPalDirectoryFindHandleDelete(dir->handle); + dir->handle = 0; +} + +GDir * +g_dir_open (const gchar *path, guint flags, GError **error) +{ + GDir *dir; + gboolean success; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + dir = g_new0 (GDir, 1); + + success = setup_dir_handle(dir, path, error); + if (!success) + return NULL; + + return dir; +} + +const gchar * +g_dir_read_name (GDir *dir) +{ + char* result_file_name; + gint unused_attributes; + UnityPalErrorCode result; + + g_return_val_if_fail (dir != NULL && dir->handle != 0, NULL); + + if (dir->current) + g_free (dir->current); + dir->current = NULL; + + dir->current = dir->next; + + if (!dir->current) + return NULL; + + dir->next = NULL; + + do { + result = UnityPalDirectoryFindNextFile(dir->handle, &result_file_name, &unused_attributes); + if (!UnityPalSuccess(result)) { + dir->next = NULL; + return dir->current; + } + } while ((strcmp (result_file_name, ".") == 0) || (strcmp (result_file_name, "..") == 0)); + + dir->next = result_file_name; + return dir->current; +} + +void +g_dir_rewind (GDir *dir) +{ + g_return_if_fail (dir != NULL && dir->handle != NULL); + + close_dir_handle(dir); + setup_dir_handle(dir, dir->path_for_rewind, NULL); +} + +void +g_dir_close (GDir *dir) +{ + g_return_if_fail (dir != NULL && dir->handle != 0); + + if (dir->current) + g_free (dir->current); + dir->current = NULL; + if (dir->next) + g_free (dir->next); + dir->next = NULL; + if (dir->path_for_rewind) + g_free(dir->path_for_rewind); + dir->path_for_rewind = NULL; + close_dir_handle(dir); + g_free (dir); +} diff --git a/mono/eglib/gfile-unity.c b/mono/eglib/gfile-unity.c new file mode 100644 index 000000000000..c7c6edf232f1 --- /dev/null +++ b/mono/eglib/gfile-unity.c @@ -0,0 +1,107 @@ +#include +#include +#include + +#include "File-c-api.h" +#include "Directory-c-api.h" + +gboolean +g_file_get_contents(const gchar *filename, gchar **contents, gsize *length, GError **error) +{ + gchar *str; + int palError; + UnityPalFileStat st; + long offset; + UnityPalFileHandle* handle = NULL; + int nread; + + handle = UnityPalOpen(filename, kFileModeOpen, 0, 0, 0, &palError); + if (handle == NULL) + { + if (error != NULL) + *error = g_error_new(G_LOG_DOMAIN, g_file_error_from_errno(palError), "Error opening file"); + return FALSE; + } + + if (UnityPalGetFileStat(filename, &st, &palError) == 0) + { + if (error != NULL) + *error = g_error_new(G_LOG_DOMAIN, g_file_error_from_errno(palError), "Error getting file attributes"); + UnityPalClose(handle, &palError); + return FALSE; + } + + str = g_malloc(st.length + 1); + offset = 0; + do + { + nread = UnityPalRead(handle, str + offset, st.length - offset, &palError); + if (nread > 0) + { + offset += nread; + } + } + while ((nread > 0 && offset < st.length) || (nread == -1 && errno == EINTR)); + + UnityPalClose(handle, &palError); + str[st.length] = '\0'; + if (length) + { + *length = st.length; + } + *contents = str; + return TRUE; +} + +gchar * +g_get_current_dir(void) +{ + int unused; + return UnityPalDirectoryGetCurrent(&unused); +} + +gboolean +g_file_test(const gchar *filename, GFileTest test) +{ + int palError = 0; + UnityPalFileAttributes attr; + + if (filename == NULL || test == 0) + return FALSE; + + attr = UnityPalGetFileAttributes(filename, &palError); + + if (palError != 0) + return FALSE; + + if ((test & G_FILE_TEST_EXISTS) != 0) + { + return TRUE; + } + + if ((test & G_FILE_TEST_IS_EXECUTABLE) != 0) + { + return UnityPalIsExecutable(filename) ? TRUE : FALSE; + } + + if ((test & G_FILE_TEST_IS_REGULAR) != 0) + { + if (attr & (kFileAttributeDevice | kFileAttributeDirectory)) + return FALSE; + return TRUE; + } + + if ((test & G_FILE_TEST_IS_DIR) != 0) + { + if (attr & kFileAttributeDirectory) + return TRUE; + } + + /* make this last in case it is OR'd with something else */ + if ((test & G_FILE_TEST_IS_SYMLINK) != 0) + { + return FALSE; + } + + return FALSE; +} diff --git a/mono/eglib/glib.h b/mono/eglib/glib.h index 333639f677ab..4babfcede50e 100644 --- a/mono/eglib/glib.h +++ b/mono/eglib/glib.h @@ -14,7 +14,11 @@ #endif #include + +/* inttypes.h is only available from VS2013 */ +#if !defined(_MSC_VER) || (_MSC_VER >= 1800) #include +#endif #include #ifndef EGLIB_NO_REMAP @@ -44,6 +48,10 @@ #define G_END_DECLS #endif +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define inline __inline +#endif + G_BEGIN_DECLS /* @@ -230,6 +238,9 @@ gint g_vasprintf (gchar **ret, const gchar *fmt, va_list ap); #define g_vsprintf vsprintf #define g_vsnprintf vsnprintf +typedef void (*vprintf_func)(const char* msg, va_list args); +void set_vprintf_func(vprintf_func func); + gsize g_strlcpy (gchar *dest, const gchar *src, gsize dest_size); gchar *g_stpcpy (gchar *dest, const char *src); @@ -881,7 +892,11 @@ gboolean g_file_test (const gchar *filename, GFileTest test); #define g_open open #define g_rename rename #define g_stat stat +#ifdef G_OS_WIN32 +#define g_unlink _unlink +#else #define g_unlink unlink +#endif #define g_fopen fopen #define g_lstat lstat #define g_rmdir rmdir diff --git a/mono/eglib/gmisc-unity.c b/mono/eglib/gmisc-unity.c new file mode 100644 index 000000000000..b1d32812e74a --- /dev/null +++ b/mono/eglib/gmisc-unity.c @@ -0,0 +1,77 @@ +#include +#include + +#include "Environment-c-api.h" +#include "Locale-c-api.h" +#include "Path-c-api.h" + +gboolean +g_hasenv(const gchar *variable) +{ + return g_getenv(variable) != NULL; +} + +gchar * +g_getenv(const gchar *variable) +{ + return UnityPalGetEnvironmentVariable(variable); +} + +gboolean +g_setenv(const gchar *variable, const gchar *value, gboolean overwrite) +{ + // This method assumes overwrite is always true. + UnityPalSetEnvironmentVariable(variable, value); + + // No code in Mono actually checks the return value. + return TRUE; +} + +void +g_unsetenv(const gchar *variable) +{ + UnityPalSetEnvironmentVariable(variable, ""); +} + +static gboolean locale_initialized = FALSE; + +gchar* +g_win32_getlocale(void) +{ + if (locale_initialized == FALSE) + { + UnityPalLocaleInitialize(); + locale_initialized = TRUE; + } + + return UnityPalGetLocale(); +} + +gboolean +g_path_is_absolute(const char *filename) +{ + return UnityPalIsAbsolutePath(filename); +} + +const gchar * +g_get_home_dir(void) +{ + return UnityPalGetHomeDirectory(); +} + +const char * +g_get_user_name(void) +{ + return UnityPalGetOsUserName(); +} + +static const char *tmp_dir; + +const gchar * +g_get_tmp_dir(void) +{ + if (tmp_dir == NULL) + tmp_dir = UnityPalGetTempPath(); + + return tmp_dir; +} diff --git a/mono/eglib/goutput.c b/mono/eglib/goutput.c index e785170621ca..3342fb86b920 100644 --- a/mono/eglib/goutput.c +++ b/mono/eglib/goutput.c @@ -31,11 +31,17 @@ #include #include +#ifdef G_OS_WIN32 +#include +#endif + /* The current fatal levels, error is always fatal */ static GLogLevelFlags fatal = G_LOG_LEVEL_ERROR; static GLogFunc default_log_func; static gpointer default_log_func_user_data; static GPrintFunc stdout_handler, stderr_handler; +typedef void (*vprintf_func)(const char* msg, va_list args); +static vprintf_func our_vprintf = vprintf; static void default_stdout_handler (const gchar *string); static void default_stderr_handler (const gchar *string); @@ -231,7 +237,11 @@ g_log_default_handler (const gchar *log_domain, GLogLevelFlags log_level, const if (log_level & fatal) { fflush (stdout); fflush (stderr); +#ifdef G_OS_WIN32 + RaiseException (0xE0000001, EXCEPTION_NONCONTINUABLE, 0, NULL); +#else abort (); +#endif } } @@ -274,3 +284,31 @@ g_set_printerr_handler (GPrintFunc func) return old; } +void wrap_our_vprintf(const gchar *format, ...) +{ + va_list args; + va_start (args, format); + our_vprintf (format, args); + va_end (args); +} + +static void +unity_vprintf_GPrintFunc_adapter (const gchar *string) +{ + wrap_our_vprintf ("%s", string); +} + +static void +unity_vprintf_GLogFunc_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) +{ + wrap_our_vprintf ("%s", message); +} + +// Redirect all stdout output to unity vprintf function +void set_vprintf_func(vprintf_func func) +{ + our_vprintf = func; + g_set_print_handler (unity_vprintf_GPrintFunc_adapter); + g_log_set_default_handler (unity_vprintf_GLogFunc_adapter, NULL); +} + diff --git a/mono/eglib/gpath.c b/mono/eglib/gpath.c index 21e777046034..01113ee0ce69 100644 --- a/mono/eglib/gpath.c +++ b/mono/eglib/gpath.c @@ -251,11 +251,17 @@ g_find_program_in_path (const gchar *program) x = NULL; probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program, NULL); - if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */ +#if !defined(NO_HAVE_ACCESS) +#ifdef G_OS_WIN32 + if (_access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */ +#else + if (access(probe_path, X_OK) == 0) { +#endif g_free (curdir); g_free (p); return probe_path; } +#endif g_free (probe_path); #ifdef G_OS_WIN32 @@ -265,12 +271,14 @@ g_find_program_in_path (const gchar *program) while (suffix_list[listx]) { program_exe = g_strjoin(NULL,program,suffix_list[listx],NULL); probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program_exe, NULL); - if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */ +#if !defined(NO_HAVE_ACCESS) + if (_access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */ g_free (curdir); g_free (p); g_free (program_exe); return probe_path; } +#endif listx++; g_free (probe_path); g_free (program_exe); diff --git a/mono/eglib/gunicode-win32-uwp.c b/mono/eglib/gunicode-win32-uwp.c index ef36ffc17f51..359ed2458431 100644 --- a/mono/eglib/gunicode-win32-uwp.c +++ b/mono/eglib/gunicode-win32-uwp.c @@ -19,9 +19,9 @@ g_get_charset (G_CONST_RETURN char **charset) { if (my_charset == NULL) { static char buf [14]; - CPINFOEXA cp_info; + CPINFOEXW cp_info; - GetCPInfoExA (CP_ACP, 0, &cp_info); + GetCPInfoExW (CP_ACP, 0, &cp_info); sprintf (buf, "CP%u", cp_info.CodePage); my_charset = buf; is_utf8 = FALSE; diff --git a/mono/eglib/gunicode-win32.c b/mono/eglib/gunicode-win32.c index a35cfcd48c12..993583629ca3 100644 --- a/mono/eglib/gunicode-win32.c +++ b/mono/eglib/gunicode-win32.c @@ -7,7 +7,7 @@ #include #include -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) #define CODESET 1 #include @@ -30,7 +30,7 @@ g_get_charset (G_CONST_RETURN char **charset) return is_utf8; } -#else /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */ +#else /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) */ #ifdef _MSC_VER // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty. diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am index 1ea96434ef5a..c0e5065842f9 100644 --- a/mono/metadata/Makefile.am +++ b/mono/metadata/Makefile.am @@ -96,13 +96,24 @@ endif boehm_libraries = $(shared_boehm_libraries) libmonoruntime-static.la endif +if SUPPORT_BDWGC if DISABLE_EXECUTABLES -noinst_LTLIBRARIES = libmonoruntime-config.la $(shared_sgen_libraries) $(shared_boehm_libraries) +shared_bdwgc_libraries = libmonoruntimebdwgc.la else -noinst_LTLIBRARIES = libmonoruntime-config.la $(boehm_libraries) $(sgen_libraries) +if SHARED_MONO +shared_bdwgc_libraries = libmonoruntimebdwgc.la +endif +endif +bdwgc_libraries = $(shared_bdwgc_libraries) libmonoruntimebdwgc-static.la endif -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(LIBGC_CPPFLAGS) $(GLIB_CFLAGS) $(SHARED_CFLAGS) +if DISABLE_EXECUTABLES +noinst_LTLIBRARIES = libmonoruntime-config.la $(shared_sgen_libraries) $(shared_boehm_libraries) $(shared_bdwgc_libraries) +else +noinst_LTLIBRARIES = libmonoruntime-config.la $(boehm_libraries) $(sgen_libraries) $(bdwgc_libraries) +endif + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(GLIB_CFLAGS) $(SHARED_CFLAGS) # # Make sure any prefix changes are updated in the binaries too. @@ -132,8 +143,16 @@ null_gc_sources = \ null-gc-handles.h \ null-gc-handles.c +unity_sources = \ + unity-memory-info.c \ + unity-memory-info.h \ + unity-utils.c \ + unity-utils.h \ + unity-icall.c + common_sources = \ $(platform_sources) \ + $(unity_sources) \ appdomain.c \ domain.c \ appdomain-icalls.h \ @@ -301,6 +320,9 @@ gc_dependent_sources = \ gc.c \ monitor.c +unity_gc_dependent_sources = \ + unity-liveness.c + boehm_sources = \ boehm-gc.c @@ -317,19 +339,28 @@ sgen_sources = \ sgen-mono.c \ sgen-client-mono.h -libmonoruntime_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(null_gc_sources) $(boehm_sources) -libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES) +libmonoruntime_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(unity_gc_dependent_sources) $(null_gc_sources) $(boehm_sources) +libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES) $(LIBGC_CPPFLAGS) libmonoruntime_la_LIBADD = libmonoruntime-config.la -libmonoruntimesgen_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(sgen_sources) +libmonoruntimebdwgc_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(unity_gc_dependent_sources) $(null_gc_sources) $(boehm_sources) +libmonoruntimebdwgc_la_CFLAGS = $(BDWGC_DEFINES) $(LIBGCBDWGC_CPPFLAGS) +libmonoruntimebdwgc_la_LIBADD = libmonoruntime-config.la $(btls_libs) + +libmonoruntimesgen_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(unity_gc_dependent_sources) $(sgen_sources) libmonoruntimesgen_la_CFLAGS = $(SGEN_DEFINES) libmonoruntimesgen_la_LIBADD = libmonoruntime-config.la libmonoruntime_static_la_SOURCES = $(libmonoruntime_la_SOURCES) libmonoruntime_static_la_LDFLAGS = -static -libmonoruntime_static_la_CFLAGS = $(BOEHM_DEFINES) +libmonoruntime_static_la_CFLAGS = $(BOEHM_DEFINES) $(LIBGC_CPPFLAGS) libmonoruntime_static_la_LIBADD = $(bundle_obj) libmonoruntime-config.la +libmonoruntimebdwgc_static_la_SOURCES = $(libmonoruntimebdwgc_la_SOURCES) +libmonoruntimebdwgc_static_la_LDFLAGS = -static +libmonoruntimebdwgc_static_la_CFLAGS = $(BDWGC_DEFINES) $(LIBGCBDWGC_CPPFLAGS) +libmonoruntimebdwgc_static_la_LIBADD = $(bundle_obj) libmonoruntime-config.la $(btls_static_libs) + libmonoruntimesgen_static_la_SOURCES = $(libmonoruntimesgen_la_SOURCES) libmonoruntimesgen_static_la_LDFLAGS = -static libmonoruntimesgen_static_la_CFLAGS = $(SGEN_DEFINES) diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index 304432e39c34..1bd0e83437a8 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -198,6 +198,7 @@ create_domain_objects (MonoDomain *domain) mono_error_assert_ok (&error); mono_field_static_set_value (string_vt, string_empty_fld, empty_str); domain->empty_string = empty_str; + mono_gc_wbarrier_generic_nostore (&domain->empty_string); /* * Create an instance early since we can't do it when there is no memory. @@ -205,6 +206,7 @@ create_domain_objects (MonoDomain *domain) arg = mono_string_new_checked (domain, "Out of memory", &error); mono_error_assert_ok (&error); domain->out_of_memory_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL, &error); + mono_gc_wbarrier_generic_nostore (&domain->out_of_memory_ex); mono_error_assert_ok (&error); /* @@ -214,14 +216,17 @@ create_domain_objects (MonoDomain *domain) arg = mono_string_new_checked (domain, "A null value was found where an object instance was required", &error); mono_error_assert_ok (&error); domain->null_reference_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL, &error); + mono_gc_wbarrier_generic_nostore (&domain->null_reference_ex); mono_error_assert_ok (&error); arg = mono_string_new_checked (domain, "The requested operation caused a stack overflow.", &error); mono_error_assert_ok (&error); domain->stack_overflow_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL, &error); + mono_gc_wbarrier_generic_nostore (&domain->stack_overflow_ex); mono_error_assert_ok (&error); /*The ephemeron tombstone i*/ domain->ephemeron_tombstone = mono_object_new_checked (domain, mono_defaults.object_class, &error); + mono_gc_wbarrier_generic_nostore (&domain->ephemeron_tombstone); mono_error_assert_ok (&error); if (domain != old_domain) { @@ -292,7 +297,9 @@ mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoT ad->data = domain; domain->domain = ad; + mono_gc_wbarrier_generic_nostore (&domain->domain); domain->setup = setup; + mono_gc_wbarrier_generic_nostore (&domain->setup); mono_thread_attach (domain); @@ -626,6 +633,7 @@ mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHa goto_if_nok (error, leave); MONO_HANDLE_SETVAL (ad, data, MonoDomain*, data); data->domain = MONO_HANDLE_RAW (ad); + mono_gc_wbarrier_generic_nostore (&data->domain); data->friendly_name = g_strdup (friendly_name); MONO_PROFILER_RAISE (domain_name, (data, data->friendly_name)); @@ -653,6 +661,7 @@ mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHa goto_if_nok (error, leave); data->setup = MONO_HANDLE_RAW (copy_app_domain_setup (data, setup, error)); + mono_gc_wbarrier_generic_nostore (&data->setup); if (!mono_error_ok (error)) { g_free (data->friendly_name); goto leave; @@ -2072,12 +2081,18 @@ mono_domain_assembly_search (MonoAssemblyName *aname, GSList *tmp; MonoAssembly *ass; gboolean refonly = GPOINTER_TO_UINT (user_data); + const gboolean strong_name = aname->public_key_token[0] != 0; + /* If it's not a strong name, any version that has the right simple + * name is good enough to satisfy the request. .NET Framework also + * ignores case differences in this case. */ + const MonoAssemblyNameEqFlags eq_flags = strong_name ? MONO_ANAME_EQ_IGNORE_CASE : + (MONO_ANAME_EQ_IGNORE_PUBKEY | MONO_ANAME_EQ_IGNORE_VERSION | MONO_ANAME_EQ_IGNORE_CASE); mono_domain_assemblies_lock (domain); for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { ass = (MonoAssembly *)tmp->data; /* Dynamic assemblies can't match here in MS.NET */ - if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname)) + if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal_flags (aname, &ass->aname, eq_flags)) continue; mono_domain_assemblies_unlock (domain); @@ -2175,6 +2190,9 @@ ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomainHandle ad, return refass; } + /* Clear the reference added by mono_image_open_from_data_full above */ + mono_image_close (image); + refass = mono_assembly_get_object_handle (domain, ass, error); if (!MONO_HANDLE_IS_NULL(refass)) MONO_HANDLE_SET (refass, evidence, evidence); @@ -2261,7 +2279,7 @@ ves_icall_System_AppDomain_InternalUnload (gint32 domain_id, MonoError *error) return; MonoException *exc = NULL; - mono_domain_try_unload (domain, (MonoObject**)&exc); + mono_domain_try_unload (domain, (MonoObject**)&exc, NULL); if (exc) mono_error_set_exception_instance (error, exc); } @@ -2585,7 +2603,10 @@ unload_thread_main (void *arg) */ for (i = 0; i < domain->class_vtable_array->len; ++i) zero_static_data ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i)); + /* Boehm does not use remsets, therefore it seems safe to ifdef it out */ +#if !HAVE_BOEHM_GC mono_gc_collect (0); +#endif for (i = 0; i < domain->class_vtable_array->len; ++i) clear_cached_vtable ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i)); deregister_reflection_info_roots (domain); @@ -2604,8 +2625,6 @@ unload_thread_main (void *arg) mono_domain_free (domain, FALSE); - mono_gc_collect (mono_gc_max_generation ()); - mono_atomic_store_release (&data->done, TRUE); unload_data_unref (data); return 0; @@ -2627,7 +2646,7 @@ void mono_domain_unload (MonoDomain *domain) { MonoObject *exc = NULL; - mono_domain_try_unload (domain, &exc); + mono_domain_try_unload (domain, &exc, NULL); } static MonoThreadInfoWaitRet @@ -2643,9 +2662,12 @@ guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertab } /** - * mono_domain_unload: + * mono_domain_try_unload: * \param domain The domain to unload * \param exc Exception information + * \param callback Passes exception information back to caller before domain is unloaded + * + * NOTE: If the callback param is not null the domain unload will continue after executing the callback. * * Unloads an appdomain. Follows the process outlined in: * http://blogs.gotdotnet.com/cbrumme @@ -2662,7 +2684,7 @@ guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertab * process could end up trying to abort the current thread. */ void -mono_domain_try_unload (MonoDomain *domain, MonoObject **exc) +mono_domain_try_unload (MonoDomain *domain, MonoObject **exc, MonoUnityExceptionFunc callback) { MonoError error; MonoThreadHandle *thread_handle; @@ -2708,10 +2730,14 @@ mono_domain_try_unload (MonoDomain *domain, MonoObject **exc) } if (*exc) { - /* Roll back the state change */ - domain->state = MONO_APPDOMAIN_CREATED; - mono_domain_set (caller_domain, FALSE); - return; + if (callback != NULL) + callback (*exc); + else { + /* Roll back the state change */ + domain->state = MONO_APPDOMAIN_CREATED; + mono_domain_set (caller_domain, FALSE); + return; + } } mono_domain_set (caller_domain, FALSE); diff --git a/mono/metadata/appdomain.h b/mono/metadata/appdomain.h index 9d42bdda4903..0f8329147f86 100644 --- a/mono/metadata/appdomain.h +++ b/mono/metadata/appdomain.h @@ -26,6 +26,8 @@ typedef struct _MonoAppDomain MonoAppDomain; typedef struct _MonoJitInfo MonoJitInfo; typedef void (*MonoDomainFunc) (MonoDomain *domain, void* user_data); +typedef void (*MonoDomainAssemblyFunc) (MonoAssembly *assembly, void* user_data); +typedef void (*MonoUnityExceptionFunc) (MonoObject* exc); MONO_API MonoDomain* mono_init (const char *filename); @@ -85,6 +87,9 @@ mono_domain_get_id (MonoDomain *domain); MONO_API const char * mono_domain_get_friendly_name (MonoDomain *domain); +MonoAssembly* +m_domain_get_corlib (MonoDomain *domain); + MONO_API mono_bool mono_domain_set (MonoDomain *domain, mono_bool force); @@ -96,7 +101,7 @@ MONO_API void mono_domain_unload (MonoDomain *domain); MONO_API void -mono_domain_try_unload (MonoDomain *domain, MonoObject **exc); +mono_domain_try_unload (MonoDomain *domain, MonoObject **exc, MonoUnityExceptionFunc callback); MONO_API mono_bool mono_domain_is_unloading (MonoDomain *domain); @@ -108,6 +113,9 @@ mono_domain_from_appdomain (MonoAppDomain *appdomain); MONO_API void mono_domain_foreach (MonoDomainFunc func, void* user_data); +MONO_API void +mono_domain_assembly_foreach (MonoDomain* domain, MonoDomainAssemblyFunc func, void* user_data); + MONO_API MonoAssembly * mono_domain_assembly_open (MonoDomain *domain, const char *name); diff --git a/mono/metadata/assembly-internals.h b/mono/metadata/assembly-internals.h index 65dc09f8712b..3116ffae1229 100644 --- a/mono/metadata/assembly-internals.h +++ b/mono/metadata/assembly-internals.h @@ -9,6 +9,24 @@ #include #include +#include + +/* Flag bits for mono_assembly_names_equal_flags (). */ +typedef enum { + /* Default comparison: all fields must match */ + MONO_ANAME_EQ_NONE = 0x0, + /* Don't compare public key token */ + MONO_ANAME_EQ_IGNORE_PUBKEY = 0x1, + /* Don't compare the versions */ + MONO_ANAME_EQ_IGNORE_VERSION = 0x2, + /* When comparing simple names, ignore case differences */ + MONO_ANAME_EQ_IGNORE_CASE = 0x4, + + MONO_ANAME_EQ_MASK = 0x7 +} MonoAssemblyNameEqFlags; + +gboolean +mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags); MONO_API MonoImage* mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error); diff --git a/mono/metadata/assembly.c b/mono/metadata/assembly.c index 4d4409d96561..3b5c81d75df8 100644 --- a/mono/metadata/assembly.c +++ b/mono/metadata/assembly.c @@ -63,18 +63,6 @@ typedef struct { gboolean framework_facade_assembly; } AssemblyVersionMap; -/* Flag bits for assembly_names_equal_flags (). */ -typedef enum { - /* Default comparison: all fields must match */ - ANAME_EQ_NONE = 0x0, - /* Don't compare public key token */ - ANAME_EQ_IGNORE_PUBKEY = 0x1, - /* Don't compare the versions */ - ANAME_EQ_IGNORE_VERSION = 0x2, - - ANAME_EQ_MASK = 0x3 -} AssemblyNameEqFlags; - /* the default search path is empty, the first slot is replaced with the computed value */ static const char* default_path [] = { @@ -359,9 +347,6 @@ mono_assembly_is_in_gac (const gchar *filanem); static MonoAssembly* prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly); -static gboolean -assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, AssemblyNameEqFlags flags); - /* Assembly name matching */ static gboolean exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name); @@ -441,6 +426,56 @@ mono_set_assemblies_path (const char* path) } } +/** +* mono_set_assemblies_path_null_separated: +* @path: list of paths that contain directories where Mono will look for assemblies +* +* Use this method to override the standard assembly lookup system and +* override any assemblies coming from the GAC. This is the method +* that supports the MONO_PATH variable. +* +* Notice that MONO_PATH and this method are really a very bad idea as +* it prevents the GAC from working and it prevents the standard +* resolution mechanisms from working. Nonetheless, for some debugging +* situations and bootstrapping setups, this is useful to have. +*/ +void +mono_set_assemblies_path_null_separated(const char* path) +{ + char **dest; + + int numPaths = 0; + char* path_count_ptr = path; + while (*path_count_ptr) + { + path_count_ptr += strlen(path_count_ptr) + 1; + numPaths++; + } + dest = g_new(char**, sizeof(char*) * (numPaths + 1)); + + if (assemblies_path) + g_strfreev(assemblies_path); + assemblies_path = dest; + char* current_path = path; + while (*current_path) + { + *dest++ = mono_path_canonicalize(current_path); + current_path += strlen(current_path) + 1; + } + *dest = NULL; + + if (g_getenv("MONO_DEBUG") == NULL) + return; + + char** print_assembly_str = assemblies_path; + while (*print_assembly_str) { + if (**print_assembly_str && !g_file_test(*print_assembly_str, G_FILE_TEST_IS_DIR)) + g_warning("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *print_assembly_str); + + print_assembly_str++; + } +} + static void check_path_env (void) { @@ -635,16 +670,32 @@ check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name) gboolean mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r) { - return assembly_names_equal_flags (l, r, ANAME_EQ_NONE); + return mono_assembly_names_equal_flags (l, r, MONO_ANAME_EQ_NONE); } +/** + * mono_assembly_names_equal_flags: + * \param l first assembly name + * \param r second assembly name + * \param flags flags that affect what is compared. + * + * Compares two \c MonoAssemblyName instances and returns whether they are equal. + * + * This compares the simple names and cultures and optionally the versions and + * public key tokens, depending on the \c flags. + * + * \returns TRUE if both assembly names are equal. + */ gboolean -assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, AssemblyNameEqFlags flags) +mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags) { if (!l->name || !r->name) return FALSE; - if (strcmp (l->name, r->name)) + if ((flags & MONO_ANAME_EQ_IGNORE_CASE) != 0 && g_strcasecmp (l->name, r->name)) + return FALSE; + + if ((flags & MONO_ANAME_EQ_IGNORE_CASE) == 0 && strcmp (l->name, r->name)) return FALSE; if (l->culture && r->culture && strcmp (l->culture, r->culture)) @@ -652,11 +703,11 @@ assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, AssemblyNa if ((l->major != r->major || l->minor != r->minor || l->build != r->build || l->revision != r->revision) && - (flags & ANAME_EQ_IGNORE_VERSION) == 0) + (flags & MONO_ANAME_EQ_IGNORE_VERSION) == 0) if (! ((l->major == 0 && l->minor == 0 && l->build == 0 && l->revision == 0) || (r->major == 0 && r->minor == 0 && r->build == 0 && r->revision == 0))) return FALSE; - if (!l->public_key_token [0] || !r->public_key_token [0] || (flags & ANAME_EQ_IGNORE_PUBKEY) != 0) + if (!l->public_key_token [0] || !r->public_key_token [0] || (flags & MONO_ANAME_EQ_IGNORE_PUBKEY) != 0) return TRUE; if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token)) @@ -777,7 +828,7 @@ compute_base (char *path) return NULL; /* Not a well known Mono executable, we are embedded, cant guess the base */ - if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis")) + if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-bdwgc") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis")) return NULL; *p = 0; @@ -785,7 +836,7 @@ compute_base (char *path) if (p == NULL) return NULL; - if (strcmp (p, "/bin") != 0) + if (strstr (p, "/bin") == 0) return NULL; *p = 0; return path; @@ -3576,13 +3627,13 @@ framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *ca if (vmap) { if (!vmap->framework_facade_assembly) { /* If the wanted name is a framework assembly, it's enough for the name/version/culture to match. If the assembly was remapped, the public key token is likely unrelated. */ - gboolean result = assembly_names_equal_flags (wanted_name, candidate_name, ANAME_EQ_IGNORE_PUBKEY); + gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_PUBKEY); mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring the public key token)", result ? "match, returning TRUE" : "don't match, returning FALSE"); return result; } else { /* For facades, the name and public key token should * match, but the version doesn't matter. */ - gboolean result = assembly_names_equal_flags (wanted_name, candidate_name, ANAME_EQ_IGNORE_VERSION); + gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_VERSION); mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring version)", result ? "match, returning TRUE" : "don't match, returning FALSE"); return result; } diff --git a/mono/metadata/assembly.h b/mono/metadata/assembly.h index 1a6f35198406..a34b5ed6c0e7 100644 --- a/mono/metadata/assembly.h +++ b/mono/metadata/assembly.h @@ -114,6 +114,7 @@ MONO_API void mono_register_machine_config (const char *config_xml); MONO_API void mono_set_rootdir (void); MONO_API void mono_set_dirs (const char *assembly_dir, const char *config_dir); MONO_API void mono_set_assemblies_path (const char* path); +MONO_API void mono_set_assemblies_path_null_separated(const char* path); MONO_END_DECLS #endif diff --git a/mono/metadata/boehm-gc.c b/mono/metadata/boehm-gc.c index 462d3a115257..4eee85fa877e 100644 --- a/mono/metadata/boehm-gc.c +++ b/mono/metadata/boehm-gc.c @@ -40,10 +40,15 @@ #if HAVE_BOEHM_GC +#if !HAVE_BDWGC_GC +#define GC_dirty(x) +#endif + #undef TRUE #undef FALSE #define THREAD_LOCAL_ALLOC 1 #include "private/pthread_support.h" +#include #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) void *pthread_get_stackaddr_np(pthread_t); @@ -55,6 +60,8 @@ void *pthread_get_stackaddr_np(pthread_t); #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20) static gboolean gc_initialized = FALSE; +static gboolean gc_strict_wbarriers = FALSE; +static gboolean gc_dont_gc_env = FALSE; static mono_mutex_t mono_gc_lock; typedef void (*GC_push_other_roots_proc)(void); @@ -108,10 +115,62 @@ mono_gc_warning (char *msg, GC_word arg) static void on_gc_notification (GC_EventType event); static void on_gc_heap_resize (size_t new_size); +#if HAVE_BDWGC_GC +#include + +#define ELEMENT_CHUNK_SIZE 256 +#define VECTOR_PROC_INDEX 6 + +static unsigned GC_roots_proc_index; + +static mse* +GC_roots_proc (word* addr, mse* mark_stack_ptr, mse* mark_stack_limit, word env); + +static mse * GC_gcj_vector_proc (word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env) +{ + MonoArray* a = NULL; + if (env) + { + g_assert (env == 1); + + a = (MonoArray*)GC_base (addr); + } else { + g_assert (addr == GC_base (addr)); + + a = (MonoArray*)addr; + } + + if (!a->max_length) + return mark_stack_ptr; + + mono_array_size_t length = a->max_length; + MonoClass* array_type = a->obj.vtable->klass; + MonoClass *element_type = array_type->element_class; + GC_descr element_desc = element_type->gc_descr; + + g_assert ((element_desc & GC_DS_TAGS) == GC_DS_BITMAP); + g_assert (element_type->valuetype); + + int words_per_element = array_type->sizes.element_size / BYTES_PER_WORD; + word *actual_start = (word *)a->vector; + + /* start at first element or resume from last iteration */ + word *start = env ? addr : actual_start; + /* end at last element or max chunk size */ + word *actual_end = actual_start + length * words_per_element; + + return GC_gcj_vector_mark_proc (mark_stack_ptr, mark_stack_limit, element_desc, start, actual_end, words_per_element); +} + +#endif + void mono_gc_base_init (void) { char *env; + char *params_opts = NULL; + char *debug_opts = NULL; if (gc_initialized) return; @@ -180,25 +239,30 @@ mono_gc_base_init (void) default_push_other_roots = GC_push_other_roots; GC_push_other_roots = mono_push_other_roots; -#if !defined(HOST_ANDROID) - /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */ +#if defined(HAVE_BDWGC_GC) || !defined(HOST_ANDROID) + /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android With Mono's Older Boehm. */ GC_no_dls = TRUE; #endif + + debug_opts = mono_gc_debug_get(); + if (debug_opts) { - if ((env = g_getenv ("MONO_GC_DEBUG"))) { - char **opts = g_strsplit (env, ",", -1); - for (char **ptr = opts; ptr && *ptr; ptr ++) { - char *opt = *ptr; - if (!strcmp (opt, "do-not-finalize")) { - mono_do_not_finalize = 1; - } else if (!strcmp (opt, "log-finalizers")) { - log_finalizers = 1; - } + char **opts = g_strsplit (debug_opts, ",", -1); + for (char **ptr = opts; ptr && *ptr; ptr ++) { + char *opt = *ptr; + if (!strcmp (opt, "do-not-finalize")) { + mono_do_not_finalize = 1; + } else if (!strcmp (opt, "log-finalizers")) { + log_finalizers = 1; } - g_free (env); } + g_strfreev (opts); + g_free (debug_opts); } + /* cache value rather than calling during collection since g_hasenv may take locks and can deadlock */ + gc_dont_gc_env = g_hasenv ("GC_DONT_GC"); + GC_init (); GC_set_warn_proc (mono_gc_warning); @@ -206,12 +270,18 @@ mono_gc_base_init (void) GC_finalizer_notifier = mono_gc_finalize_notify; GC_init_gcj_malloc (5, NULL); +#if HAVE_BDWGC_GC + GC_init_gcj_vector (VECTOR_PROC_INDEX, GC_gcj_vector_proc); + GC_roots_proc_index = GC_new_proc (GC_roots_proc); +#endif GC_allow_register_threads (); - if ((env = g_getenv ("MONO_GC_PARAMS"))) { - char **ptr, **opts = g_strsplit (env, ",", -1); + params_opts = mono_gc_params_get(); + if (params_opts) { + char **ptr, **opts = g_strsplit (params_opts, ",", -1); for (ptr = opts; *ptr; ++ptr) { char *opt = *ptr; + if (g_str_has_prefix (opt, "max-heap-size=")) { size_t max_heap; @@ -230,7 +300,23 @@ mono_gc_base_init (void) } else if (g_str_has_prefix (opt, "toggleref-test")) { register_test_toggleref_callback (); continue; - } else { + } else if (g_str_has_prefix (opt, "incremental=")) { + size_t time_limit; + + opt = strchr (opt, '=') + 1; + if (*opt && mono_gc_parse_environment_string_extract_number (opt, &time_limit)) { + GC_enable_incremental (); + #if HAVE_BDWGC_GC + if (time_limit != 0) + // value is in milliseconds + GC_set_time_limit (time_limit); + #endif + } + continue; + } else if (g_str_has_prefix (opt, "strict-wbarriers")) { + gc_strict_wbarriers = TRUE; + continue; + }else { /* Could be a parameter for sgen */ /* fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n"); @@ -239,8 +325,8 @@ mono_gc_base_init (void) */ } } - g_free (env); g_strfreev (opts); + g_free (params_opts); } mono_thread_callbacks_init (); @@ -256,6 +342,24 @@ mono_gc_base_init (void) gc_initialized = TRUE; } +void +mono_gc_dirty(void **ptr) +{ + GC_dirty (ptr); +} + +void +mono_gc_dirty_range(void **ptr, size_t size) +{ + if (G_UNLIKELY(gc_strict_wbarriers)) + { + for (int i = 0; i < size/sizeof(void*); i++) + GC_dirty(ptr + i); + } + else + GC_dirty (ptr); +} + void mono_gc_base_cleanup (void) { @@ -283,6 +387,20 @@ mono_gc_collect (int generation) GC_gcollect (); } + +int +mono_gc_collect_a_little() +{ + return GC_collect_a_little(); +} + +void mono_gc_start_incremental_collection() +{ +#if HAVE_BDWGC_GC + GC_start_incremental_collection(); +#endif +} + /** * mono_gc_max_generation: * @@ -370,6 +488,47 @@ mono_gc_get_heap_size (void) return GC_get_heap_size (); } +int64_t +mono_gc_get_max_time_slice_ns() +{ +#if HAVE_BDWGC_GC + return GC_get_time_limit_ns(); +#else + return 0; +#endif +} + +void +mono_gc_set_max_time_slice_ns(int64_t maxTimeSlice) +{ +#if HAVE_BDWGC_GC + GC_set_time_limit_ns(maxTimeSlice); +#endif +} + +MonoBoolean +mono_gc_is_incremental() +{ +#if HAVE_BDWGC_GC + return GC_is_incremental_mode(); +#else + return FALSE; +#endif +} + +void +mono_gc_set_incremental(MonoBoolean value) +{ +#if HAVE_BDWGC_GC + if (GC_is_incremental_mode() == value) + return; + if (value) + GC_enable_incremental(); + else + GC_disable_incremental(); +#endif +} + gboolean mono_gc_is_gc_thread (void) { @@ -393,6 +552,14 @@ mono_gc_thread_attach (MonoThreadInfo* info) return info; } +void +mono_gc_thread_detach (MonoThreadInfo *p) +{ +#if HAVE_BDWGC_GC + GC_unregister_my_thread (); +#endif +} + void mono_gc_thread_detach_with_lock (MonoThreadInfo *p) { @@ -486,7 +653,7 @@ on_gc_notification (GC_EventType event) } #endif UnlockedAdd64 (&gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time); - mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10); +// mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10); break; default: break; @@ -580,12 +747,6 @@ mono_gc_deregister_root (char* addr) MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr)); } -static void -push_root (gpointer key, gpointer value, gpointer user_data) -{ - GC_push_all (key, value); -} - static void push_handle_stack (HandleStack* stack) { @@ -604,10 +765,82 @@ push_handle_stack (HandleStack* stack) } } +#ifdef HAVE_BDWGC_GC + +static mse* +GC_roots_proc (word* addr, mse* mark_stack_ptr, mse* mark_stack_limit, word env) +{ + GHashTableIter iter; + guint size = g_hash_table_size (roots); + g_hash_table_iter_init (&iter, roots); + char* start; + char* end; + + word capacity = (word)(mark_stack_limit - mark_stack_ptr) - 1; + word start_index = env; + word remaining = size - start_index; + word skip = start_index; + + /* if we have more items than capacity, push remaining immediately. This allows pushed + * items to be processed on top of stack before we process remainder. If we push remainder + * at top, we have no mark stack space. + */ + if (remaining > capacity) { + capacity--; + mark_stack_ptr++; + mark_stack_ptr->mse_descr.w = GC_MAKE_PROC (GC_roots_proc_index, (start_index + capacity) /* continue processing */); + mark_stack_ptr->mse_start = (ptr_t)0; + } + + while (g_hash_table_iter_next (&iter, (void**)&start, (void**)&end) && capacity > 0) { + void* bottom; + void* top; + + if (skip) { + skip--; + continue; + } + + /* taken from GC_push_all */ + bottom = (void*)(((word)start + ALIGNMENT - 1) & ~(ALIGNMENT - 1)); + top = (void*)((word)end & ~(ALIGNMENT - 1)); + + g_assert ((word)bottom < (word)top); + + mark_stack_ptr++; + + if ((word)mark_stack_ptr >= (word)mark_stack_limit) { + g_error ("Unexpected mark stack overflow\n"); + } + mark_stack_ptr->mse_start = (ptr_t)bottom; + mark_stack_ptr->mse_descr.w = (word)top - (word)bottom; + capacity--; + } + return mark_stack_ptr; +} + +#else + +static void +push_root (gpointer key, gpointer value, gpointer user_data) +{ + GC_push_all (key, value); +} + +#endif + static void mono_push_other_roots (void) { +#ifdef HAVE_BDWGC_GC + if (GC_roots_proc_index) { + GC_mark_stack_top++; + GC_mark_stack_top->mse_descr.w = GC_MAKE_PROC (GC_roots_proc_index, 0 /* continue processing */); + GC_mark_stack_top->mse_start = (ptr_t)0; + } +#else g_hash_table_foreach (roots, push_root, NULL); +#endif FOREACH_THREAD (info) { HandleStack* stack = (HandleStack*)info->handle_stack; if (stack) @@ -622,6 +855,7 @@ mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track) { /* libgc requires that we use HIDE_POINTER... */ *link_addr = (void*)HIDE_POINTER (obj); + mono_gc_dirty (link_addr); if (track) GC_REGISTER_LONG_LINK (link_addr, obj); else @@ -669,7 +903,9 @@ mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size) void* mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size) { - /* libgc has no usable support for arrays... */ + /* libgc has no usable support for arrays... + * but Unity added support with newer bdwgc + * we don't need descriptor though, as arrays have custom mark proc */ return GC_NO_DESCRIPTOR; } @@ -756,10 +992,15 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length) obj->obj.synchronisation = NULL; memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject)); - } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) { - obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable); +#if HAVE_BDWGC_GC + } else if (vtable->klass->element_class->valuetype && + vtable->klass->element_class->gc_descr != GC_NO_DESCRIPTOR && + vtable->domain == mono_get_root_domain () /* && + max_length > 50 */) { + obj = (MonoArray *)GC_gcj_vector_malloc (size, vtable); if (G_UNLIKELY (!obj)) return NULL; +#endif } else { obj = (MonoArray *)GC_MALLOC (size); if (G_UNLIKELY (!obj)) @@ -866,49 +1107,60 @@ void mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value) { *(void**)field_ptr = value; + mono_gc_dirty (field_ptr); } void mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value) { *(void**)slot_ptr = value; + mono_gc_dirty (slot_ptr); } void mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count) { mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer)); + mono_gc_dirty_range (dest_ptr, count * sizeof(gpointer)); } void mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value) { *(void**)ptr = value; + mono_gc_dirty (ptr); } void mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value) { mono_atomic_store_ptr ((volatile gpointer *)ptr, value); + mono_gc_dirty (ptr); } void mono_gc_wbarrier_generic_nostore (gpointer ptr) { + mono_gc_dirty (ptr); } void mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass) { - mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL)); + size_t size = count * mono_class_value_size (klass, NULL); + mono_gc_memmove_atomic (dest, src, size); + mono_gc_dirty_range (dest, size); } void mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src) { /* do not copy the sync state */ - mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject), - mono_object_class (obj)->instance_size - sizeof (MonoObject)); + size_t size = mono_object_class (obj)->instance_size - sizeof (MonoObject); + char * dstPtr = (char*)obj + sizeof (MonoObject); + mono_gc_memmove_aligned (dstPtr, (char*)src + sizeof (MonoObject), + size); + mono_gc_dirty_range ((void**)dstPtr, size); } void @@ -969,6 +1221,10 @@ create_allocator (int atype, int tls_key, gboolean slowpath) g_assert_not_reached (); +#ifdef HAVE_BDWGC_GC + return NULL; +#else + if (atype == ATYPE_FREEPTR) { name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree"; } else if (atype == ATYPE_FREEPTR_FOR_BOX) { @@ -1180,6 +1436,7 @@ create_allocator (int atype, int tls_key, gboolean slowpath) mono_mb_free (mb); return res; +#endif } static MonoMethod* alloc_method_cache [ATYPE_NUM]; @@ -1215,6 +1472,7 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know */ return NULL; +#if 0 if (!SMALL_ENOUGH (klass->instance_size)) return NULL; if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass)) @@ -1246,6 +1504,7 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know */ } return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR); +#endif } MonoMethod* @@ -1288,14 +1547,51 @@ mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant varian guint32 mono_gc_get_managed_allocator_types (void) { - return ATYPE_NUM; + return 0; } +static MonoMethod *write_barrier_conc_method; MonoMethod* mono_gc_get_write_barrier (void) { - g_assert_not_reached (); - return NULL; + MonoMethod *res; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + MonoMethod **write_barrier_method_addr; + WrapperInfo *info; + + write_barrier_method_addr = &write_barrier_conc_method; + + if (*write_barrier_method_addr) + return *write_barrier_method_addr; + + /* Create the IL version of mono_gc_barrier_generic_store () */ + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->params [0] = &mono_defaults.int_class->byval_arg; + + mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore); + mono_mb_emit_byte (mb, MONO_CEE_RET); + + res = mono_mb_create_method (mb, sig, 16); + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + mono_marshal_set_wrapper_info (res, info); + mono_mb_free (mb); + + if (*write_barrier_method_addr) { + /* Already created */ + mono_free_method (res); + } else { + /* double-checked locking */ + mono_memory_barrier (); + *write_barrier_method_addr = res; + } + + return *write_barrier_method_addr; + } #else @@ -1330,11 +1626,48 @@ mono_gc_get_managed_allocator_types (void) return 0; } +static MonoMethod *write_barrier_conc_method; MonoMethod* mono_gc_get_write_barrier (void) { - g_assert_not_reached (); - return NULL; + MonoMethod *res; + MonoMethodBuilder *mb; + MonoMethodSignature *sig; + MonoMethod **write_barrier_method_addr; + WrapperInfo *info; + + write_barrier_method_addr = &write_barrier_conc_method; + + if (*write_barrier_method_addr) + return *write_barrier_method_addr; + + /* Create the IL version of mono_gc_barrier_generic_store () */ + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + sig->ret = &mono_defaults.void_class->byval_arg; + sig->params [0] = &mono_defaults.int_class->byval_arg; + + mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore); + mono_mb_emit_byte (mb, MONO_CEE_RET); + + res = mono_mb_create_method (mb, sig, 16); + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + mono_marshal_set_wrapper_info (res, info); + mono_mb_free (mb); + + if (*write_barrier_method_addr) { + /* Already created */ + mono_free_method (res); + } else { + /* double-checked locking */ + mono_memory_barrier (); + *write_barrier_method_addr = res; + } + + return *write_barrier_method_addr; + } #endif @@ -1367,7 +1700,7 @@ mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) char* mono_gc_get_description (void) { - return g_strdup (DEFAULT_GC_NAME); + return g_strdup ("BDWGC"); } void @@ -1382,10 +1715,20 @@ mono_gc_is_moving (void) return FALSE; } +gboolean +mono_gc_needs_write_barriers(void) +{ +#if HAVE_BDWGC_GC + return GC_is_incremental_mode(); +#else + return FALSE; +#endif +} + gboolean mono_gc_is_disabled (void) { - if (GC_dont_gc || g_hasenv ("GC_DONT_GC")) + if (GC_dont_gc || gc_dont_gc_env) return TRUE; else return FALSE; @@ -1394,7 +1737,8 @@ mono_gc_is_disabled (void) void mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size) { - g_assert_not_reached (); + memcpy (_dest, _src, size); + mono_gc_dirty_range (_dest, size); } void* @@ -1406,7 +1750,6 @@ mono_gc_get_range_copy_func (void) guint8* mono_gc_get_card_table (int *shift_bits, gpointer *card_mask) { - g_assert_not_reached (); return NULL; } @@ -1435,16 +1778,6 @@ mono_gc_get_logfile (void) return NULL; } -void -mono_gc_params_set (const char* options) -{ -} - -void -mono_gc_debug_set (const char* options) -{ -} - void mono_gc_conservatively_scan_area (void *start, void *end) { @@ -1505,7 +1838,11 @@ mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void #ifdef HOST_WIN32 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved) { +#ifdef GC_INSIDE_DLL return GC_DllMain (module_handle, reason, reserved); +#else + return TRUE; +#endif } #endif @@ -1535,7 +1872,9 @@ mono_gc_get_vtable_bits (MonoClass *klass) void mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size) { +#if !HAVE_BDWGC_GC GC_register_altstack (stack, stack_size, altstack, altstack_size); +#endif } int @@ -1574,14 +1913,22 @@ mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker) void mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref) { +#ifndef HAVE_BDWGC_GC if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS) g_error ("GC_toggleref_add failed\n"); +#else + g_assert_not_reached (); +#endif } void mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj)) { +#ifndef HAVE_BDWGC_GC GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref); +#else + g_assert_not_reached (); +#endif } /* Test support code */ @@ -1630,7 +1977,11 @@ mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks) fin_callbacks = *callbacks; +#ifndef HAVE_BDWGC_GC GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier); +#else + g_assert_not_reached (); +#endif } #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT) @@ -1738,6 +2089,7 @@ handle_data_grow (HandleData *handles, gboolean track) gpointer *entries; entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)"); mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size); + mono_gc_dirty_range (entries, new_size * sizeof (*handles->entries)); mono_gc_free_fixed (handles->entries); handles->entries = entries; } @@ -1770,6 +2122,7 @@ alloc_handle (HandleData *handles, MonoObject *obj, gboolean track) mono_gc_weak_link_add (&(handles->entries [slot]), obj, track); } else { handles->entries [slot] = obj; + mono_gc_dirty (handles->entries + slot); } #ifndef DISABLE_PERFCOUNTERS @@ -1887,6 +2240,7 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id; } else { handles->entries [slot] = obj; + mono_gc_dirty (handles->entries + slot); } } else { /* print a warning? */ @@ -1965,6 +2319,7 @@ mono_gchandle_free (guint32 gchandle) mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); } else { handles->entries [slot] = NULL; + mono_gc_dirty (handles->entries + slot); } vacate_slot (handles, slot); } else { @@ -1990,7 +2345,7 @@ mono_gchandle_free_domain (MonoDomain *domain) { guint type; - for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) { + for (type = HANDLE_TYPE_MIN; type <= HANDLE_PINNED; ++type) { guint slot; HandleData *handles = &gc_handles [type]; lock_handles (handles); @@ -2007,6 +2362,7 @@ mono_gchandle_free_domain (MonoDomain *domain) if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) { vacate_slot (handles, slot); handles->entries [slot] = NULL; + mono_gc_dirty (handles->entries + slot); } } } @@ -2021,6 +2377,30 @@ mono_gc_register_obj_with_weak_fields (void *obj) g_error ("Weak fields not supported by boehm gc"); } +void +mono_gc_strong_handle_foreach(GFunc func, gpointer user_data) +{ + int gcHandleTypeIndex; + uint32_t i; + + lock_handles(handles); + + for (gcHandleTypeIndex = HANDLE_NORMAL; gcHandleTypeIndex <= HANDLE_PINNED; gcHandleTypeIndex++) + { + HandleData* handles = &gc_handles[gcHandleTypeIndex]; + + for (i = 0; i < handles->size; i++) + { + if (!slot_occupied(handles, i)) + continue; + if (handles->entries[i] != NULL) + func(handles->entries[i], user_data); + } + } + + unlock_handles(handles); +} + #else MONO_EMPTY_SOURCE_FILE (boehm_gc); diff --git a/mono/metadata/class-inlines.h b/mono/metadata/class-inlines.h index d57d5310cc5a..4b4fb2eb518d 100644 --- a/mono/metadata/class-inlines.h +++ b/mono/metadata/class-inlines.h @@ -93,4 +93,10 @@ mono_class_has_static_metadata (MonoClass *klass) return klass->type_token && !klass->image->dynamic && !mono_class_is_ginst (klass); } +static inline gboolean +m_class_is_initialized (MonoClass* klass) +{ + return klass->inited; +} + #endif diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h index f7d946c0a195..20c887c90f43 100644 --- a/mono/metadata/class-internals.h +++ b/mono/metadata/class-internals.h @@ -75,6 +75,10 @@ struct _MonoMethod { MonoMethodSignature *signature; /* name is useful mostly for debugging */ const char *name; +#ifdef IL2CPP_ON_MONO + void* method_pointer; + void* invoke_pointer; +#endif /* this is used by the inlining algorithm */ unsigned int inline_info:1; unsigned int inline_failure:1; @@ -381,6 +385,8 @@ struct _MonoClass { /* Infrequently used items. See class-accessors.c: InfrequentDataKind for what goes into here. */ MonoPropertyBag infrequent_data; + + void *unity_user_data; }; typedef struct { @@ -1180,6 +1186,7 @@ GENERATE_GET_CLASS_WITH_CACHE_DECL (variant) #endif GENERATE_GET_CLASS_WITH_CACHE_DECL (appdomain_unloaded_exception) +GENERATE_GET_CLASS_WITH_CACHE_DECL (valuetype) extern MonoDefaults mono_defaults; @@ -1260,7 +1267,7 @@ mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) MONO_ MonoException* mono_class_get_exception_for_failure (MonoClass *klass); -char* +UNITY_MONO_API char* mono_type_get_name_full (MonoType *type, MonoTypeNameFormat format); char* @@ -1365,6 +1372,9 @@ mono_class_vtable_full (MonoDomain *domain, MonoClass *klass, MonoError *error); gboolean mono_class_is_assignable_from_slow (MonoClass *target, MonoClass *candidate); +MonoClass* +mono_generic_param_get_base_type (MonoClass *klass); + gboolean mono_class_has_variant_generic_params (MonoClass *klass); diff --git a/mono/metadata/class.c b/mono/metadata/class.c index 82927e85285d..9a1c8020881a 100644 --- a/mono/metadata/class.c +++ b/mono/metadata/class.c @@ -55,6 +55,7 @@ MonoStats mono_stats; gboolean mono_print_vtable = FALSE; gboolean mono_align_small_structs = FALSE; +extern gboolean mono_allow_gc_aware_layout; /* Statistics */ gint32 inflated_classes_size, inflated_methods_size; @@ -85,6 +86,8 @@ static gboolean mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_ static gboolean class_kind_may_contain_generic_instances (MonoTypeKind kind); +GENERATE_GET_CLASS_WITH_CACHE (valuetype, "System", "ValueType"); + /* We use gclass recording to allow recursive system f types to be referenced by a parent. @@ -816,6 +819,14 @@ inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *cont nt->data.generic_class = gclass; return nt; } + case MONO_TYPE_PTR: { + MonoType *nt, *inflated = inflate_generic_type (image, type->data.type, context, error); + if (!inflated || !mono_error_ok (error)) + return NULL; + nt = mono_metadata_type_dup (image, type); + nt->data.type = inflated; + return nt; + } default: return NULL; } @@ -1751,6 +1762,59 @@ type_has_references (MonoClass *klass, MonoType *ftype) return FALSE; } +/** + * mono_class_is_gparam_with_nonblittable_parent: + * \param klass a generic parameter + * + * \returns TRUE if \p klass is definitely not blittable. + * + * A parameter is definitely not blittable if it has the IL 'reference' + * constraint, or if it has a class specified as a parent. If it has an IL + * 'valuetype' constraint or no constraint at all or only interfaces as + * constraints, we return FALSE because the parameter may be instantiated both + * with blittable and non-blittable types. + * + * If the paramter is a generic sharing parameter, we look at its gshared_constraint->blittable bit. + */ +static gboolean +mono_class_is_gparam_with_nonblittable_parent (MonoClass *klass) +{ + MonoType *type = &klass->byval_arg; + g_assert (mono_type_is_generic_parameter (type)); + MonoGenericParam *gparam = type->data.generic_param; + if (mono_generic_param_info (gparam) != NULL) + { + if ((mono_generic_param_info (gparam)->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0) + return TRUE; + if ((mono_generic_param_info (gparam)->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0) + return FALSE; + } + + if (gparam->gshared_constraint) { + MonoClass *constraint_class = mono_class_from_mono_type (gparam->gshared_constraint); + return !constraint_class->blittable; + } + + if (mono_generic_param_owner (gparam)->is_anonymous) + return FALSE; + + /* We could have: T : U, U : Base. So have to follow the constraints. */ + MonoClass *parent_class = mono_generic_param_get_base_type (klass); + g_assert (!MONO_CLASS_IS_INTERFACE_INTERNAL (parent_class)); + /* Parent can only be: System.Object, System.ValueType or some specific base class. + * + * If the parent_class is ValueType, the valuetype constraint would be set, above, so + * we wouldn't get here. + * + * If there was a reference constraint, the parent_class would be System.Object, + * but we would have returned early above. + * + * So if we get here, there is either no base class constraint at all, + * in which case parent_class would be set to System.Object, or there is none at all. + */ + return parent_class != mono_defaults.object_class; +} + /* * mono_class_layout_fields: * @class: a class @@ -1842,9 +1906,14 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ * what the default is for other runtimes. */ /* corlib is missing [StructLayout] directives in many places */ - if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) { + if (mono_allow_gc_aware_layout && (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT)) { if (!klass->valuetype) gc_aware_layout = TRUE; + /* Unity depends on List`1 layout in native code */ + if (klass->image == mono_defaults.corlib && + strcmp (klass->name_space, "System.Collections.Generic") == 0 && + strcmp (klass->name, "List`1") == 0) + gc_aware_layout = FALSE; } /* Compute klass->blittable */ @@ -1863,6 +1932,9 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ if (blittable) { if (field->type->byref || MONO_TYPE_IS_REFERENCE (field->type)) { blittable = FALSE; + } else if (mono_type_is_generic_parameter (field->type) && + mono_class_is_gparam_with_nonblittable_parent (mono_class_from_mono_type (field->type))) { + blittable = FALSE; } else { MonoClass *field_class = mono_class_from_mono_type (field->type); if (field_class) { @@ -1985,9 +2057,6 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ real_size = field_offsets [i] + size; } - /* Make SIMD types as big as a SIMD register since they can be stored into using simd stores */ - if (klass->simd_type) - real_size = MAX (real_size, sizeof (MonoObject) + 16); instance_size = MAX (real_size, instance_size); if (instance_size & (min_align - 1)) { @@ -5099,9 +5168,8 @@ mono_class_init (MonoClass *klass) locked = TRUE; if (klass->inited || mono_class_has_failure (klass)) { - mono_loader_unlock (); /* Somebody might have gotten in before us */ - return !mono_class_has_failure (klass); + goto leave; } UnlockedIncrement (&mono_stats.initialized_class_count); @@ -5823,7 +5891,9 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError if (!strncmp (name, "Vector", 6)) klass->simd_type = !strcmp (name + 6, "2d") || !strcmp (name + 6, "2ul") || !strcmp (name + 6, "2l") || !strcmp (name + 6, "4f") || !strcmp (name + 6, "4ui") || !strcmp (name + 6, "4i") || !strcmp (name + 6, "8s") || !strcmp (name + 6, "8us") || !strcmp (name + 6, "16b") || !strcmp (name + 6, "16sb"); } else if (klass->image->assembly_name && !strcmp (klass->image->assembly_name, "System.Numerics") && !strcmp (nspace, "System.Numerics")) { - if (!strcmp (name, "Vector2") || !strcmp (name, "Vector3") || !strcmp (name, "Vector4")) + /* The JIT can't handle SIMD types with != 16 size yet */ + //if (!strcmp (name, "Vector2") || !strcmp (name, "Vector3") || !strcmp (name, "Vector4")) + if (!strcmp (name, "Vector4")) klass->simd_type = 1; } @@ -7699,7 +7769,7 @@ find_nocase (gpointer key, gpointer value, gpointer user_data) char *name = (char*)key; FindUserData *data = (FindUserData*)user_data; - if (!data->value && (mono_utf8_strcasecmp (name, (char*)data->key) == 0)) + if (!data->value && (NULL == data->key || (mono_utf8_strcasecmp (name, (char*)data->key) == 0))) data->value = value; } @@ -7794,7 +7864,7 @@ mono_class_from_name_case_checked (MonoImage *image, const char *name_space, con continue; n = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]); nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]); - if (mono_utf8_strcasecmp (n, name) == 0 && mono_utf8_strcasecmp (nspace, name_space) == 0) + if (mono_utf8_strcasecmp (n, name) == 0 && (NULL == name_space || (mono_utf8_strcasecmp (nspace, name_space) == 0))) return mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | i, error); } return NULL; @@ -8632,6 +8702,60 @@ mono_class_is_assignable_from_slow (MonoClass *target, MonoClass *candidate) return FALSE; } +/** + * mono_generic_param_get_base_type: + * + * Return the base type of the given generic parameter from its constraints. + * + * Could be another generic parameter, or it could be Object or ValueType. + */ +MonoClass* +mono_generic_param_get_base_type (MonoClass *klass) +{ + MonoType *type = &klass->byval_arg; + g_assert (mono_type_is_generic_argument (type)); + + MonoGenericParam *gparam = type->data.generic_param; + + g_assert (gparam->owner && !gparam->owner->is_anonymous); + + MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints; + + MonoClass *base_class = mono_defaults.object_class; + + if (constraints) { + int i; + for (i = 0; constraints [i]; ++i) { + MonoClass *constraint = constraints[i]; + + if (MONO_CLASS_IS_INTERFACE_INTERNAL (constraint)) + continue; + + MonoType *constraint_type = &constraint->byval_arg; + if (mono_type_is_generic_argument (constraint_type)) { + MonoGenericParam *constraint_param = constraint_type->data.generic_param; + MonoGenericParamInfo *constraint_info = mono_generic_param_info (constraint_param); + if ((constraint_info->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) == 0 && + (constraint_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) == 0) + continue; + } + + base_class = constraint; + } + + } + + if (base_class == mono_defaults.object_class) + { + MonoGenericParamInfo *gparam_info = mono_generic_param_info (gparam); + if ((gparam_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0) { + base_class = mono_class_get_valuetype_class (); + } + } + + return base_class; +} + /** * mono_class_get_cctor: * \param klass A MonoClass pointer @@ -9679,6 +9803,7 @@ mono_field_get_flags (MonoClassField *field) guint32 mono_field_get_offset (MonoClassField *field) { + mono_class_setup_fields(field->parent); return field->offset; } diff --git a/mono/metadata/cominterop.c b/mono/metadata/cominterop.c index 1caa6ca0f992..ed3964dbc5be 100644 --- a/mono/metadata/cominterop.c +++ b/mono/metadata/cominterop.c @@ -1692,17 +1692,6 @@ ves_icall_System_Runtime_InteropServices_Marshal_GetCCW (MonoObject* object, Mon #endif } - -MonoBoolean -ves_icall_System_Runtime_InteropServices_Marshal_IsComObject (MonoObject* object) -{ -#ifndef DISABLE_COM - return (MonoBoolean)cominterop_object_is_rcw (object); -#else - g_assert_not_reached (); -#endif -} - gint32 ves_icall_System_Runtime_InteropServices_Marshal_ReleaseComObjectInternal (MonoObject* object) { @@ -3688,6 +3677,16 @@ ves_icall_System_Runtime_InteropServices_Marshal_QueryInterfaceInternal (gpointe #endif /* DISABLE_COM */ +MonoBoolean +ves_icall_System_Runtime_InteropServices_Marshal_IsComObject (MonoObject* object) +{ +#ifndef DISABLE_COM + return (MonoBoolean)cominterop_object_is_rcw (object); +#else + return FALSE; +#endif +} + MonoString * ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringBSTR (gpointer ptr) { diff --git a/mono/metadata/console-unity.c b/mono/metadata/console-unity.c new file mode 100644 index 000000000000..97376f4c95ad --- /dev/null +++ b/mono/metadata/console-unity.c @@ -0,0 +1,47 @@ +#include +#include "Console-c-api.h" +#include "File-c-api.h" /* required for IAtty */ + +#if defined(PLATFORM_UNITY) + +void +mono_console_init (void) +{ +} + +void +mono_console_handle_async_ops (void) +{ +} + +MonoBoolean +ves_icall_System_ConsoleDriver_Isatty (gpointer handle) +{ + return UnityPalIsatty(handle); +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo) +{ + return UnityPalConsoleSetEcho(want_echo); +} + +MonoBoolean +ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break) +{ + return UnityPalConsoleSetBreak(want_break); +} + +gint32 +ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout) +{ + return UnityPalConsoleInternalKeyAvailable(timeout); +} + +MonoBoolean +ves_icall_System_ConsoleDriver_TtySetup (MonoString *keypad, MonoString *teardown, MonoArray **control_chars, int **size) +{ + return UnityPalConsoleTtySetup(keypad, teardown, control_chars, size); +} + +#endif /* PLATFORM_UNITY */ diff --git a/mono/metadata/custom-attrs.c b/mono/metadata/custom-attrs.c index ffd4d4a43e19..a6c52a958f0f 100644 --- a/mono/metadata/custom-attrs.c +++ b/mono/metadata/custom-attrs.c @@ -223,6 +223,18 @@ load_cattr_value (MonoImage *image, MonoType *t, const char *p, const char *boun g_assert (boundp); error_init (error); + if (type == MONO_TYPE_GENERICINST) { + MonoGenericClass *mgc = t->data.generic_class; + MonoClass *cc = mgc->container_class; + if (mono_class_is_enum (cc)) { + tklass = mono_class_get_element_class (cc); + t = &tklass->byval_arg; + type = t->type; + } else { + g_error ("Unhandled type of generic instance in load_cattr_value: %s", cc->name); + } + } + handle_enum: switch (type) { case MONO_TYPE_U1: @@ -424,6 +436,17 @@ load_cattr_value (MonoImage *image, MonoType *t, const char *p, const char *boun basetype = tklass->byval_arg.type; if (basetype == MONO_TYPE_VALUETYPE && tklass->enumtype) basetype = mono_class_enum_basetype (tklass)->type; + + if (basetype == MONO_TYPE_GENERICINST) { + MonoGenericClass *mgc = tklass->byval_arg.data.generic_class; + MonoClass *cc = mgc->container_class; + if (mono_class_is_enum (cc)) { + basetype = mono_class_get_element_class (cc)->byval_arg.type; + } else { + g_error ("Unhandled type of generic instance in load_cattr_value: %s[]", cc->name); + } + } + switch (basetype) { case MONO_TYPE_U1: @@ -1192,7 +1215,7 @@ create_custom_attr_data (MonoImage *image, MonoCustomAttrEntry *cattr, MonoError HANDLE_FUNCTION_RETURN_OBJ (obj); } -static MonoArray* +MonoArray* mono_custom_attrs_construct_by_type (MonoCustomAttrInfo *cinfo, MonoClass *attr_klass, MonoError *error) { MonoArray *result; diff --git a/mono/metadata/debug-internals.h b/mono/metadata/debug-internals.h index 5ac4f103adf4..1d5e33cf2504 100644 --- a/mono/metadata/debug-internals.h +++ b/mono/metadata/debug-internals.h @@ -84,7 +84,4 @@ mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info); gboolean mono_debug_image_has_debug_info (MonoImage *image); -MonoDebugSourceLocation * -mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain); - #endif /* __DEBUG_INTERNALS_H__ */ diff --git a/mono/metadata/debug-mono-ppdb.c b/mono/metadata/debug-mono-ppdb.c index 4d61efe345a2..badf6b671940 100644 --- a/mono/metadata/debug-mono-ppdb.c +++ b/mono/metadata/debug-mono-ppdb.c @@ -370,6 +370,7 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset) location = g_new0 (MonoDebugSourceLocation, 1); location->source_file = docname; location->row = start_line; + location->column = start_col; location->il_offset = iloffset; return location; @@ -414,6 +415,9 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr method_idx = mono_metadata_token_index (method->token); + if (tables [MONO_TABLE_METHODBODY].rows == 0) + return; + mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE); docidx = cols [MONO_METHODBODY_DOCUMENT]; diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h index 6d8042c07680..1df75583c36d 100644 --- a/mono/metadata/domain-internals.h +++ b/mono/metadata/domain-internals.h @@ -246,6 +246,8 @@ struct _MonoJitInfo { /* Whenever this jit info refers to an interpreter method */ gboolean is_interp:1; + gboolean dbg_ignore : 1; + /* FIXME: Embed this after the structure later*/ gpointer gc_info; /* Currently only used by SGen */ @@ -577,7 +579,7 @@ mono_runtime_get_no_exec (void); void mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name); -gboolean +UNITY_MONO_API gboolean mono_assembly_name_parse (const char *name, MonoAssemblyName *aname); MonoImage *mono_assembly_open_from_bundle (const char *filename, diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 2947589dbb76..4292fb7f7bdc 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -299,16 +299,16 @@ gc_alloc_fixed_non_heap_list (size_t size) if (mono_gc_is_moving ()) return g_malloc0 (size); else - return mono_gc_alloc_fixed (appdomain_list_size * sizeof (void*), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, NULL, "Domain List"); + return mono_gc_alloc_fixed (size, MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, NULL, "Domain List"); } static void gc_free_fixed_non_heap_list (void *ptr) { if (mono_gc_is_moving ()) - return g_free (ptr); + g_free (ptr); else - return mono_gc_free_fixed (ptr); + mono_gc_free_fixed (ptr); } /* * Allocate an id for domain and set domain->domain_id. @@ -453,6 +453,7 @@ mono_domain_create (void) #endif mono_debug_domain_create (domain); + mono_profiler_coverage_domain_init (domain); if (create_domain_hook) create_domain_hook (domain); @@ -853,6 +854,8 @@ mono_cleanup (void) { mono_close_exe_image (); + mono_thread_info_cleanup (); + mono_defaults.corlib = NULL; mono_config_cleanup (); @@ -923,6 +926,7 @@ mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exce SET_APPDOMAIN (domain); SET_APPCONTEXT (domain->default_context); + mono_gc_wbarrier_generic_nostore (&domain->default_context); if (migrate_exception) { thread = mono_thread_internal_current (); @@ -982,6 +986,27 @@ mono_domain_foreach (MonoDomainFunc func, gpointer user_data) gc_free_fixed_non_heap_list (copy); } +MONO_API void +mono_domain_assembly_foreach (MonoDomain* domain, MonoDomainAssemblyFunc func, void* user_data) +{ + MonoAssembly* assembly; + GSList *iter; + + /* Skipping internal assembly builders created by remoting, + as it is done in ves_icall_System_AppDomain_GetAssemblies + */ + mono_domain_assemblies_lock(domain); + for (iter = domain->domain_assemblies; iter; iter = iter->next) + { + assembly = (MonoAssembly *)iter->data; + if (assembly->corlib_internal) + continue; + + func(assembly, user_data); + } + mono_domain_assemblies_unlock(domain); +} + /* FIXME: maybe we should integrate this with mono_assembly_open? */ /** * mono_domain_assembly_open: @@ -1227,6 +1252,8 @@ mono_domain_free (MonoDomain *domain, gboolean force) domain->setup = NULL; + // This crashes in bdwgc because we never register such a root. + // Not sure why/how it works in sgen, or if it is needed? if (mono_gc_is_moving ()) mono_gc_deregister_root ((char*)&(domain->MONO_DOMAIN_FIRST_GC_TRACKED)); @@ -1242,6 +1269,8 @@ mono_domain_free (MonoDomain *domain, gboolean force) if (domain == mono_root_domain) mono_root_domain = NULL; + + mono_profiler_coverage_domain_free(domain); } /** @@ -1293,6 +1322,12 @@ mono_domain_get_friendly_name (MonoDomain *domain) return domain->friendly_name; } +MonoAssembly* +m_domain_get_corlib (MonoDomain *domain) +{ + return domain->domain->mbr.obj.vtable->klass->image->assembly; +} + /* * mono_domain_alloc: * diff --git a/mono/metadata/exception.c b/mono/metadata/exception.c index 61fde6541757..9ee13f95a145 100644 --- a/mono/metadata/exception.c +++ b/mono/metadata/exception.c @@ -1183,3 +1183,10 @@ mono_exception_from_name_four_strings_checked (MonoImage *image, const char *nam return create_exception_four_strings (klass, a1, a2, a3, a4, error); } + +void +ves_icall_System_Exception_ReportUnhandledException(MonoObject *exc) +{ + mono_unhandled_exception (exc); + mono_invoke_unhandled_exception_hook (exc); +} \ No newline at end of file diff --git a/mono/metadata/exception.h b/mono/metadata/exception.h index 6575694c7c50..f53a2c8f4597 100644 --- a/mono/metadata/exception.h +++ b/mono/metadata/exception.h @@ -161,6 +161,9 @@ typedef void (*MonoUnhandledExceptionFunc) (MonoObject *exc, void *user MONO_API void mono_install_unhandled_exception_hook (MonoUnhandledExceptionFunc func, void *user_data); void mono_invoke_unhandled_exception_hook (MonoObject *exc); +void +ves_icall_System_Exception_ReportUnhandledException (MonoObject *exc); + MONO_END_DECLS #endif /* _MONO_METADATA_EXCEPTION_H_ */ diff --git a/mono/metadata/file-mmap-unity.c b/mono/metadata/file-mmap-unity.c new file mode 100644 index 000000000000..cc338f107977 --- /dev/null +++ b/mono/metadata/file-mmap-unity.c @@ -0,0 +1,110 @@ + +#include +#include + +#include +#include "MemoryMappedFile-c-api.h" +#include "File-c-api.h" + +typedef struct { + void *address; + size_t length; +} MmapInstance; + +enum { + BAD_CAPACITY_FOR_FILE_BACKED = 1, + CAPACITY_SMALLER_THAN_FILE_SIZE, + FILE_NOT_FOUND, + FILE_ALREADY_EXISTS, + PATH_TOO_LONG, + COULD_NOT_OPEN, + CAPACITY_MUST_BE_POSITIVE, + INVALID_FILE_MODE, + COULD_NOT_MAP_MEMORY, + ACCESS_DENIED, + CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE +}; + +#ifndef HOST_WIN32 + +typedef struct { + int kind; + int ref_count; + size_t capacity; + char *name; + int fd; +} MmapHandle; + +#endif + +void mono_mmap_close (void *mmap_handle) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); +} + +void mono_mmap_configure_inheritability (void *mmap_handle, gboolean inheritability) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); +} + +void mono_mmap_flush (void *mmap_handle) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); +} + +void *mono_mmap_open_file (MonoString *string, int mode, MonoString *mapName, gint64 *capacity, int access, int options, int *error) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); + return NULL; +} + +void *mono_mmap_open_handle (void *handle, MonoString *mapName, gint64 *capacity, int access, int options, int *error) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); + return NULL; +} + +int mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address) +{ + /* We are dropping access parameter, UnityPAL does not support */ + g_assert (handle); + + MmapInstance *h = g_malloc0 (sizeof (MmapInstance)); + h->length = *size; + + h->address = UnityPalMemoryMappedFileMapWithParams((UnityPalFileHandle*) handle, (size_t) *size, (size_t) offset); + + if (h->address) + { + *mmap_handle = h; + *base_address = (char*) h->address + offset; + return 0; + } + else + { + g_free (h); + return COULD_NOT_MAP_MEMORY; + } +} + +gboolean +mono_mmap_unmap (void *mmap_handle) +{ + g_assert (mmap_handle); + + MmapInstance *h = (MmapInstance *)mmap_handle; + + UnityPalMemoryMappedFileUnmapWithParams(h->address, h->length); + + g_free (h); + + /* UnityPAL does not give any indication of success or failure of an unmap, forced + to always return true */ + + return TRUE; +} diff --git a/mono/metadata/gc-internals.h b/mono/metadata/gc-internals.h index 7c3cb0146663..50665278ff7d 100644 --- a/mono/metadata/gc-internals.h +++ b/mono/metadata/gc-internals.h @@ -54,7 +54,7 @@ } while (0) /* useful until we keep track of gc-references in corlib etc. */ -#define IS_GC_REFERENCE(class,t) (mono_gc_is_moving () ? FALSE : ((t)->type == MONO_TYPE_U && (class)->image == mono_defaults.corlib)) +#define IS_GC_REFERENCE(class,t) (mono_gc_needs_write_barriers() ? FALSE : ((t)->type == MONO_TYPE_U && (class)->image == mono_defaults.corlib)) void mono_object_register_finalizer (MonoObject *obj); void ves_icall_System_GC_InternalCollect (int generation); @@ -90,7 +90,7 @@ extern void mono_gc_set_stack_end (void *stack_end); /* only valid after the RECLAIM_START GC event and before RECLAIM_END * Not exported in public headers, but can be linked to (unsupported). */ -gboolean mono_object_is_alive (MonoObject* obj); +MONO_API gboolean mono_object_is_alive (MonoObject* obj); gboolean mono_gc_is_finalizer_thread (MonoThread *thread); void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj); @@ -125,7 +125,7 @@ void* mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoG void mono_gc_free_fixed (void* addr); /* make sure the gchandle was allocated for an object in domain */ -gboolean mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain); +UNITY_MONO_API gboolean mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain); void mono_gchandle_free_domain (MonoDomain *domain); typedef void (*FinalizerThreadCallback) (gpointer user_data); @@ -140,6 +140,8 @@ MonoGCDescriptor mono_gc_make_descr_for_string (gsize *bitmap, int numbits); void mono_gc_register_obj_with_weak_fields (void *obj); +void mono_gc_strong_handle_foreach(GFunc func, gpointer user_data); + void mono_gc_register_for_finalization (MonoObject *obj, void *user_data); void mono_gc_add_memory_pressure (gint64 value); MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); @@ -278,6 +280,11 @@ void mono_gc_set_desktop_mode (void); */ gboolean mono_gc_is_moving (void); +/* + * Return whenever this GC needs write barriers + */ +gboolean mono_gc_needs_write_barriers (void); + typedef void* (*MonoGCLockedCallbackFunc) (void *data); void* mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data); @@ -358,6 +365,7 @@ gboolean mono_gc_is_critical_method (MonoMethod *method); gpointer mono_gc_thread_attach (THREAD_INFO_TYPE *info); +void mono_gc_thread_detach (THREAD_INFO_TYPE *info); void mono_gc_thread_detach_with_lock (THREAD_INFO_TYPE *info); gboolean mono_gc_thread_in_critical_region (THREAD_INFO_TYPE *info); diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 99ba7244fce6..227b555833e2 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -87,6 +87,12 @@ static MonoCoopCond pending_done_cond; static MonoCoopMutex pending_done_mutex; #endif +static char* gc_params_options; +static char* gc_debug_options; + +#define MONO_GC_PARAMS_NAME "MONO_GC_PARAMS" +#define MONO_GC_DEBUG_NAME "MONO_GC_DEBUG" + static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)); static void reference_queue_proccess_all (void); @@ -161,6 +167,48 @@ coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 return res; } +void +mono_gc_params_set (const char* options) +{ + if (gc_params_options) + g_free (gc_params_options); + + gc_params_options = g_strdup (options); +} + +char * +mono_gc_params_get () +{ + char *env; + if ((env = g_getenv (MONO_GC_PARAMS_NAME)) || gc_params_options) { + char *params_opts = g_strdup_printf ("%s,%s", gc_params_options ? gc_params_options : "", env ? env : ""); + g_free (env); + return params_opts; + } + return NULL; +} + +void +mono_gc_debug_set (const char* options) +{ + if (gc_debug_options) + g_free (gc_debug_options); + + gc_debug_options = g_strdup (options); +} + +char * +mono_gc_debug_get () +{ + char *env; + if ((env = g_getenv (MONO_GC_DEBUG_NAME)) || gc_debug_options) { + char *debug_opts = g_strdup_printf ("%s,%s", gc_debug_options ? gc_debug_options : "", env ? env : ""); + g_free (env); + return debug_opts; + } + return NULL; +} + /* * actually, we might want to queue the finalize requests in a separate thread, * but we need to be careful about the execution domain of the thread... @@ -279,25 +327,6 @@ mono_gc_run_finalize (void *obj, void *data) return; } - /* - * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (), - * create and precompile a wrapper which calls the finalize method using - * a CALLVIRT. - */ - if (log_finalizers) - g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o); - -#ifndef HOST_WASM - if (!domain->finalize_runtime_invoke) { - MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE); - - domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error); - mono_error_assert_ok (&error); /* expect this not to fail */ - } - - RuntimeInvokeFunction runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke; -#endif - mono_runtime_class_init_full (o->vtable, &error); goto_if_nok (&error, unhandled_error); @@ -306,22 +335,20 @@ mono_gc_run_finalize (void *obj, void *data) o->vtable->klass->name_space, o->vtable->klass->name); } - if (log_finalizers) - g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o); + if (finalizer) { + if (log_finalizers) + g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o); - MONO_PROFILER_RAISE (gc_finalizing_object, (o)); + MONO_PROFILER_RAISE (gc_finalizing_object, (o)); -#ifdef HOST_WASM - gpointer params[] = { NULL }; - mono_runtime_try_invoke (finalizer, o, params, &exc, &error); -#else - runtime_invoke (o, NULL, &exc, NULL); -#endif + gpointer params[] = { NULL }; + mono_runtime_try_invoke (finalizer, o, params, &exc, &error); - MONO_PROFILER_RAISE (gc_finalized_object, (o)); + MONO_PROFILER_RAISE (gc_finalized_object, (o)); - if (log_finalizers) - g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o); + if (log_finalizers) + g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o); + } unhandled_error: if (!is_ok (&error)) @@ -708,7 +735,7 @@ static volatile gboolean finished; void mono_gc_finalize_notify (void) { -#ifdef DEBUG +#if defined(DEBUG) && !defined(IL2CPP_ON_MONO) g_message ( "%s: prodding finalizer", __func__); #endif @@ -880,6 +907,18 @@ finalizer_thread (gpointer unused) /* Register a hazard free queue pump callback */ mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big); + /* if GC is disabled, we run no finalizer, but we still run mono_w32process_signal_finished + on the finalizer thread, so that processes can exit. */ + if (mono_gc_is_disabled()) + { + while (!finished) + { + mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE); + mono_w32process_signal_finished(); + } + return 0; + } + while (!finished) { /* Wait to be notified that there's at least one * finaliser to run @@ -947,10 +986,8 @@ mono_gc_init (void) mono_gc_base_init (); - if (mono_gc_is_disabled ()) { + if (mono_gc_is_disabled ()) gc_disabled = TRUE; - return; - } #ifdef TARGET_WIN32 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL); @@ -977,9 +1014,9 @@ mono_gc_cleanup (void) if (mono_gc_is_null ()) return; - + + finished = TRUE; if (!gc_disabled) { - finished = TRUE; if (mono_thread_internal_current () != gc_thread) { int ret; gint64 start; @@ -1038,10 +1075,9 @@ mono_gc_cleanup (void) } gc_thread = NULL; mono_gc_base_cleanup (); - } - - mono_reference_queue_cleanup (); + mono_reference_queue_cleanup (); + } mono_coop_mutex_destroy (&finalizer_mutex); mono_coop_mutex_destroy (&reference_queue_mutex); } diff --git a/mono/metadata/handle.h b/mono/metadata/handle.h index 5355a5b950a6..f4d9650296db 100644 --- a/mono/metadata/handle.h +++ b/mono/metadata/handle.h @@ -286,6 +286,15 @@ mono_thread_info_push_stack_mark (MonoThreadInfo *info, void *mark) return __ret; \ } while (0); } while (0) +#define ICALL_RETURN_OBJ_TYPED(HANDLE,TYPE) \ + do { \ + CLEAR_STACK_WATERMARK \ + CLEAR_ICALL_COMMON \ + void* __ret = (HANDLE == NULL_HANDLE) ? NULL : MONO_HANDLE_RAW (HANDLE); \ + CLEAR_ICALL_FRAME \ + return (TYPE)__ret; \ + } while (0); } while (0) + /* Handle macros/functions */ @@ -356,7 +365,7 @@ void mono_handle_verify (MonoRawHandle handle); #define MONO_HANDLE_CAST(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( VALUE ) -#define MONO_HANDLE_IS_NULL(HANDLE) (MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW(HANDLE) == NULL)) +#define MONO_HANDLE_IS_NULL(HANDLE) (mono_handle_is_null (HANDLE)) /* @@ -482,6 +491,13 @@ Init values to it. */ extern const MonoObjectHandle mono_null_value_handle; +static inline gboolean +mono_handle_is_null (MonoRawHandle handle) +{ + // Double NULL check is required for this to work with NULL_HANDLE. + return !(handle && MONO_HANDLE_SUPPRESS (MONO_HANDLE_RAW((MonoObjectHandle)handle))); +} + static inline void mono_handle_assign (MonoObjectHandleOut dest, MonoObjectHandle src) { @@ -507,7 +523,7 @@ uintptr_t mono_array_handle_length (MonoArrayHandle arr); static inline void mono_handle_array_getref (MonoObjectHandleOut dest, MonoArrayHandle array, uintptr_t index) { - MONO_HANDLE_SUPPRESS (mono_gc_wbarrier_generic_store (&dest->__raw, mono_array_get (MONO_HANDLE_RAW (array),gpointer, index))); + MONO_HANDLE_SUPPRESS (mono_gc_wbarrier_generic_store (&dest->__raw, mono_array_get (MONO_HANDLE_RAW (array),MonoObject*, index))); } #define mono_handle_class(o) MONO_HANDLE_SUPPRESS (mono_object_class (MONO_HANDLE_RAW (MONO_HANDLE_UNSUPPRESS (o)))) diff --git a/mono/metadata/icall-def.h b/mono/metadata/icall-def.h index c7aa0b5766d9..8efe62c3f347 100644 --- a/mono/metadata/icall-def.h +++ b/mono/metadata/icall-def.h @@ -114,6 +114,9 @@ ICALL(KPAIR_4, "_ProtectMachine", ves_icall_Mono_Security_Cryptography_KeyPairPe ICALL(KPAIR_5, "_ProtectUser", ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser) #endif /* !PLATFORM_RO_FS */ +ICALL_TYPE(UNITYTLS, "Mono.Unity.UnityTls", UNITYTLS_1) +ICALL(UNITYTLS_1, "GetUnityTlsInterface", mono_unity_get_unitytls_interface) + ICALL_TYPE(APPDOM, "System.AppDomain", APPDOM_23) HANDLES(ICALL(APPDOM_23, "DoUnhandledException", ves_icall_System_AppDomain_DoUnhandledException)) HANDLES(ICALL(APPDOM_1, "ExecuteAssembly", ves_icall_System_AppDomain_ExecuteAssembly)) @@ -186,6 +189,9 @@ ICALL(CONSOLE_3, "SetBreak", ves_icall_System_ConsoleDriver_SetBreak ) ICALL(CONSOLE_4, "SetEcho", ves_icall_System_ConsoleDriver_SetEcho ) ICALL(CONSOLE_5, "TtySetup", ves_icall_System_ConsoleDriver_TtySetup ) +ICALL_TYPE(TZONE, "System.CurrentSystemTimeZone", TZONE_1) +ICALL(TZONE_1, "GetTimeZoneData", ves_icall_System_CurrentSystemTimeZone_GetTimeZoneData) + ICALL_TYPE(DTIME, "System.DateTime", DTIME_1) ICALL(DTIME_1, "GetSystemTimeAsFileTime", mono_100ns_datetime) @@ -287,6 +293,9 @@ HANDLES(ICALL(ENV_18, "internalGetGacPath", ves_icall_System_Environment_GetGacP HANDLES(ICALL(ENV_19, "internalGetHome", ves_icall_System_Environment_InternalGetHome)) ICALL(ENV_20, "set_ExitCode", mono_environment_exitcode_set) +ICALL_TYPE(EXCEPTION, "System.Exception", EXCEPTION_1) +HANDLES(ICALL(EXCEPTION_1, "ReportUnhandledException", ves_icall_System_Exception_ReportUnhandledException)) + ICALL_TYPE(GC, "System.GC", GC_0) ICALL(GC_0, "GetCollectionCount", mono_gc_collection_count) ICALL(GC_0a, "GetGeneration", mono_gc_get_generation) @@ -383,6 +392,7 @@ ICALL(MONOIO_15, "MoveFile(char*,char*,System.IO.MonoIOError&)", ves_icall_Syste #endif /* !PLATFORM_RO_FS */ ICALL(MONOIO_16, "Open(char*,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare,System.IO.FileOptions,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Open) HANDLES(ICALL(MONOIO_17, "Read(intptr,byte[],int,int,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_Read)) +ICALL(MONOIO_39, "RemapPath(string,string&)", ves_icall_System_IO_MonoIO_RemapPath) #ifndef PLATFORM_RO_FS ICALL(MONOIO_18, "RemoveDirectory(char*,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_RemoveDirectory) ICALL(MONOIO_18M, "ReplaceFile(char*,char*,char*,bool,System.IO.MonoIOError&)", ves_icall_System_IO_MonoIO_ReplaceFile) @@ -463,6 +473,7 @@ HANDLES(ICALL(SOCK_6b, "Duplicate_internal", ves_icall_System_Net_Sockets_Socket HANDLES(ICALL(SOCK_7, "GetSocketOption_arr_internal(intptr,System.Net.Sockets.SocketOptionLevel,System.Net.Sockets.SocketOptionName,byte[]&,int&)", ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal)) HANDLES(ICALL(SOCK_8, "GetSocketOption_obj_internal(intptr,System.Net.Sockets.SocketOptionLevel,System.Net.Sockets.SocketOptionName,object&,int&)", ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal)) HANDLES(ICALL(SOCK_21, "IOControl_internal(intptr,int,byte[],byte[],int&)", ves_icall_System_Net_Sockets_Socket_IOControl_internal)) +ICALL(SOCK_22, "IsProtocolSupported_internal", ves_icall_System_Net_Sockets_Socket_IsProtocolSupported_internal) HANDLES(ICALL(SOCK_9, "Listen_internal(intptr,int,int&)", ves_icall_System_Net_Sockets_Socket_Listen_internal)) HANDLES(ICALL(SOCK_10, "LocalEndPoint_internal(intptr,int,int&)", ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal)) HANDLES(ICALL(SOCK_11, "Poll_internal", ves_icall_System_Net_Sockets_Socket_Poll_internal)) @@ -696,8 +707,8 @@ ICALL(MARSHAL_11, "GetLastWin32Error", ves_icall_System_Runtime_InteropServices_ ICALL(MARSHAL_53, "GetNativeActivationFactory", ves_icall_System_Runtime_InteropServices_Marshal_GetNativeActivationFactory) ICALL(MARSHAL_47, "GetObjectForCCW", ves_icall_System_Runtime_InteropServices_Marshal_GetObjectForCCW) ICALL(MARSHAL_54, "GetRawIUnknownForComObjectNoAddRef", ves_icall_System_Runtime_InteropServices_Marshal_GetRawIUnknownForComObjectNoAddRef) -ICALL(MARSHAL_48, "IsComObject", ves_icall_System_Runtime_InteropServices_Marshal_IsComObject) #endif +ICALL(MARSHAL_48, "IsComObject", ves_icall_System_Runtime_InteropServices_Marshal_IsComObject) HANDLES(ICALL(MARSHAL_12, "OffsetOf", ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf)) HANDLES(ICALL(MARSHAL_13, "Prelink", ves_icall_System_Runtime_InteropServices_Marshal_Prelink)) HANDLES(ICALL(MARSHAL_14, "PrelinkAll", ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll)) @@ -757,6 +768,9 @@ ICALL_TYPE(REMSER, "System.Runtime.Remoting.RemotingServices", REMSER_0) HANDLES(ICALL(REMSER_0, "GetVirtualMethod", ves_icall_Remoting_RemotingServices_GetVirtualMethod)) ICALL(REMSER_1, "InternalExecute", ves_icall_InternalExecute) HANDLES(ICALL(REMSER_2, "IsTransparentProxy", ves_icall_IsTransparentProxy)) +#else +ICALL_TYPE(REMSER, "System.Runtime.Remoting.RemotingServices", REMSER_0) +ICALL(REMSER_0, "IsTransparentProxy", ves_icall_IsTransparentProxy) #endif ICALL_TYPE(RVH, "System.Runtime.Versioning.VersioningHelper", RVH_1) @@ -924,6 +938,10 @@ HANDLES(ICALL(NATIVEC_3, "OpenEvent_internal(string,System.Security.AccessContro ICALL(NATIVEC_4, "ResetEvent_internal", ves_icall_System_Threading_Events_ResetEvent_internal) ICALL(NATIVEC_5, "SetEvent_internal", ves_icall_System_Threading_Events_SetEvent_internal) +ICALL_TYPE(OSSYNCCONTEXT, "System.Threading.OSSpecificSynchronizationContext", OSSYNCCONTEXT_1) +HANDLES(ICALL(OSSYNCCONTEXT_1, "GetOSContext", ves_icall_System_Threading_OSSpecificSynchronizationContext_GetOSContext)) +ICALL(OSSYNCCONTEXT_2, "PostInternal", ves_icall_System_Threading_OSSpecificSynchronizationContext_PostInternal) + ICALL_TYPE(SEMA, "System.Threading.Semaphore", SEMA_1) ICALL(SEMA_1, "CreateSemaphore_internal(int,int,string,int&)", ves_icall_System_Threading_Semaphore_CreateSemaphore_internal) ICALL(SEMA_2, "OpenSemaphore_internal(string,System.Security.AccessControl.SemaphoreRights,int&)", ves_icall_System_Threading_Semaphore_OpenSemaphore_internal) diff --git a/mono/metadata/icall-internals.h b/mono/metadata/icall-internals.h index c172b8404bcb..64b733980538 100644 --- a/mono/metadata/icall-internals.h +++ b/mono/metadata/icall-internals.h @@ -10,6 +10,10 @@ #include #include +// UNITY +guint32 +ves_icall_System_CurrentSystemTimeZone_GetTimeZoneData (guint32 year, MonoArray **data, MonoArray **names, MonoBoolean *daylight_inverted); + // On Windows platform implementation of bellow methods are hosted in separate source file // icall-windows.c or icall-windows-*.c. On other platforms the implementation is still keept // in icall.c still declared as static and in some places even inlined. diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index d91754d9861c..ffec0503c958 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -85,6 +85,7 @@ #include #include #include +#include #include #include #include @@ -1095,6 +1096,9 @@ ves_icall_System_ValueType_InternalGetHashCode (MonoObject *this_obj, MonoArray case MONO_TYPE_I4: result ^= *(gint32*)((guint8*)this_obj + field->offset); break; + case MONO_TYPE_PTR: + result ^= mono_aligned_addr_hash (*(gpointer*)((guint8*)this_obj + field->offset)); + break; case MONO_TYPE_STRING: { MonoString *s; s = *(MonoString**)((guint8*)this_obj + field->offset); @@ -1194,6 +1198,10 @@ ves_icall_System_ValueType_Equals (MonoObject *this_obj, MonoObject *that, MonoA if (*(double*)((guint8*)this_obj + field->offset) != *(double*)((guint8*)that + field->offset)) return FALSE; break; + case MONO_TYPE_PTR: + if (*(gpointer*)((guint8*)this_obj + field->offset) != *(gpointer*)((guint8*)that + field->offset)) + return FALSE; + break; case MONO_TYPE_STRING: { @@ -2471,6 +2479,7 @@ ves_icall_RuntimeType_GetInterfaces (MonoReflectionTypeHandle ref_type, MonoErro g_hash_table_destroy (iface_hash); if (!domain->empty_types) { domain->empty_types = mono_array_new_cached (domain, mono_defaults.runtimetype_class, 0, error); + mono_gc_wbarrier_generic_nostore (&domain->empty_types); goto_if_nok (error, fail); } return MONO_HANDLE_NEW (MonoArray, domain->empty_types); @@ -3941,7 +3950,7 @@ mono_class_get_methods_by_name (MonoClass *klass, const char *name, guint32 bfla compare_func = (ignore_case) ? mono_utf8_strcasecmp : strcmp; /* An optimization for calls made from Delegate:CreateDelegate () */ - if (klass->delegate && name && !strcmp (name, "Invoke") && (bflags == (BFLAGS_Public | BFLAGS_Static | BFLAGS_Instance))) { + if (klass->delegate && klass != mono_defaults.delegate_class && klass != mono_defaults.multicastdelegate_class&& name && !strcmp (name, "Invoke") && (bflags == (BFLAGS_Public | BFLAGS_Static | BFLAGS_Instance))) { method = mono_get_delegate_invoke (klass); g_assert (method); @@ -7050,6 +7059,12 @@ ves_icall_System_Runtime_Activation_ActivationServices_EnableProxyActivation (Mo g_assert_not_reached (); } +ICALL_EXPORT MonoBoolean +ves_icall_IsTransparentProxy (MonoObject *proxy) +{ + return 0; +} + #endif ICALL_EXPORT MonoObjectHandle @@ -7495,14 +7510,9 @@ mono_TypedReference_MakeTypedReferenceInternal (MonoObject *target, MonoArray *f for (i = 0; i < mono_array_length (fields); ++i) { f = mono_array_get (fields, MonoReflectionField*, i); - if (f == NULL) { - mono_set_pending_exception (mono_get_exception_argument_null ("field")); - return res; - } - if (f->field->parent != klass) { - mono_set_pending_exception (mono_get_exception_argument ("field", "")); - return res; - } + + g_assert (f); + if (i == 0) p = (guint8*)target + f->field->offset; else @@ -8365,8 +8375,10 @@ mono_lookup_internal_call_full (MonoMethod *method, mono_bool *uses_handles) tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); siglen = strlen (tmpsig); - if (typelen + mlen + siglen + 6 > sizeof (mname)) + if (typelen + mlen + siglen + 6 > sizeof (mname)) { + g_free (tmpsig); return NULL; + } sigstart [0] = '('; memcpy (sigstart + 1, tmpsig, siglen); sigstart [siglen + 1] = ')'; @@ -8736,3 +8748,15 @@ mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignatu return mono_register_jit_icall_full (func, name, sig, no_wrapper, NULL); } +MonoObjectHandle +ves_icall_System_Threading_OSSpecificSynchronizationContext_GetOSContext () +{ + return NULL_HANDLE; +} + +void +ves_icall_System_Threading_OSSpecificSynchronizationContext_PostInternal (gpointer callback, gpointer arg) +{ + /* This isn't actually reachable since ves_icall_System_Threading_OSSpecificSynchronizationContext_GetOSContext always returns NULL */ + mono_set_pending_exception (mono_get_exception_not_implemented ("System.Threading.InteropServices.OSSpecificSynchronizationContext.PostInternal internal call is not implemented.")); +} diff --git a/mono/metadata/il2cpp-compat-metadata.h b/mono/metadata/il2cpp-compat-metadata.h new file mode 100644 index 000000000000..14ef95b07d8b --- /dev/null +++ b/mono/metadata/il2cpp-compat-metadata.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef RUNTIME_IL2CPP + +#include +#include + +#if defined(_POSIX_VERSION) +#include +#endif + +#define mono_gc_make_root_descr_all_refs il2cpp_mono_gc_make_root_descr_all_refs +#define mono_gc_alloc_fixed il2cpp_mono_gc_alloc_fixed +#define mono_gc_free_fixed il2cpp_gc_free_fixed +#define mono_gc_is_moving il2cpp_mono_gc_is_moving +#define mono_gc_invoke_with_gc_lock il2cpp_mono_gc_invoke_with_gc_lock +#define mono_gc_pthread_create il2cpp_mono_gc_pthread_create +#define mono_gc_register_root_wbarrier il2cpp_mono_gc_register_root_wbarrier +#define mono_gc_wbarrier_generic_store il2cpp_mono_gc_wbarrier_generic_store +#define mono_gc_make_vector_descr il2cpp_mono_gc_make_vector_descr +#define mono_gc_deregister_root il2cpp_mono_gc_deregister_root + +int il2cpp_mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); +SgenDescriptor il2cpp_mono_gc_make_root_descr_all_refs(int numbits); +MonoGCDescriptor il2cpp_mono_gc_make_vector_descr (void); +void* il2cpp_mono_gc_alloc_fixed (size_t size, void* descr, MonoGCRootSource source, void *key, const char *msg); +gboolean il2cpp_mono_gc_is_moving(); +void il2cpp_mono_gc_deregister_root(char* addr); + +typedef void* (*MonoGCLockedCallbackFunc) (void *data); +void* il2cpp_mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data); + +#ifndef HOST_WIN32 +int il2cpp_mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); +#endif + +#endif diff --git a/mono/metadata/image.c b/mono/metadata/image.c index 548e52715e12..bcfa81553acd 100644 --- a/mono/metadata/image.c +++ b/mono/metadata/image.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #ifdef HAVE_UNISTD_H @@ -1359,6 +1360,9 @@ do_mono_image_open (const char *fname, MonoImageOpenStatus *status, MonoCLIImageInfo *iinfo; MonoImage *image; MonoFileMap *filed; + const char *fname_remap; + if (fname_remap = mono_unity_remap_path (fname)) + fname = fname_remap; if ((filed = mono_file_map_open (fname)) == NULL){ if (IS_PORTABILITY_SET) { @@ -1372,6 +1376,7 @@ do_mono_image_open (const char *fname, MonoImageOpenStatus *status, if (filed == NULL) { if (status) *status = MONO_IMAGE_ERROR_ERRNO; + g_free((void*)fname_remap); return NULL; } } @@ -1391,6 +1396,7 @@ do_mono_image_open (const char *fname, MonoImageOpenStatus *status, g_free (image); if (status) *status = MONO_IMAGE_IMAGE_INVALID; + g_free((void*)fname_remap); return NULL; } iinfo = g_new0 (MonoCLIImageInfo, 1); @@ -1404,6 +1410,7 @@ do_mono_image_open (const char *fname, MonoImageOpenStatus *status, image->core_clr_platform_code = mono_security_core_clr_determine_platform_image (image); mono_file_map_close (filed); + g_free((void*)fname_remap); return do_mono_image_load (image, status, care_about_cli, care_about_pecoff); } @@ -2088,10 +2095,6 @@ mono_image_close_except_pools (MonoImage *image) mono_wrapper_caches_free (&image->wrapper_caches); - for (i = 0; i < image->gshared_types_len; ++i) - free_hash (image->gshared_types [i]); - g_free (image->gshared_types); - /* The ownership of signatures is not well defined */ g_hash_table_destroy (image->memberref_signatures); g_hash_table_destroy (image->helper_signatures); diff --git a/mono/metadata/jit-info.c b/mono/metadata/jit-info.c index 3344679e8123..cc04f866005f 100644 --- a/mono/metadata/jit-info.c +++ b/mono/metadata/jit-info.c @@ -196,6 +196,8 @@ jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointe return left; } +/* When changing this method, make sure to also update oop_jit_info_table_find + in mono/metadata/oop.c. */ static MonoJitInfo* jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr) { diff --git a/mono/metadata/loader.c b/mono/metadata/loader.c index edced98b6d9a..8c7a8b7b9235 100644 --- a/mono/metadata/loader.c +++ b/mono/metadata/loader.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1114,21 +1115,26 @@ static MonoDl* cached_module_load (const char *name, int flags, char **err) { MonoDl *res; + const char *name_remap; if (err) *err = NULL; + if (name_remap = mono_unity_remap_path (name)) + name = name_remap; global_loader_data_lock (); if (!global_module_map) global_module_map = g_hash_table_new (g_str_hash, g_str_equal); res = (MonoDl *)g_hash_table_lookup (global_module_map, name); if (res) { global_loader_data_unlock (); + g_free((void*)name_remap); return res; } res = mono_dl_open (name, flags, err); if (res) g_hash_table_insert (global_module_map, g_strdup (name), res); global_loader_data_unlock (); + g_free((void*)name_remap); return res; } @@ -1257,6 +1263,22 @@ mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char } } + if (mono_get_find_plugin_callback ()) + { + const char* unity_new_scope = mono_get_find_plugin_callback () (new_scope); + if (unity_new_scope == NULL) + { + if (exc_class) + { + *exc_class = "DllNotFoundException"; + *exc_arg = new_scope; + } + return NULL; + } + + new_scope = unity_new_scope; + } + /* * Try loading the module using a variety of names */ @@ -2137,6 +2159,10 @@ mono_method_get_marshal_info (MonoMethod *method, MonoMarshalSpec **mspecs) return; } + /* dynamic method added to non-dynamic image */ + if (method->dynamic) + return; + mono_class_init (klass); methodt = &klass->image->tables [MONO_TABLE_METHOD]; diff --git a/mono/metadata/locales.c b/mono/metadata/locales.c index 2ef0d2145923..e527f2a8f280 100644 --- a/mono/metadata/locales.c +++ b/mono/metadata/locales.c @@ -37,6 +37,11 @@ #include #endif +#ifdef HOST_ANDROID +#include +#include +#endif + #undef DEBUG static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2, @@ -522,6 +527,75 @@ get_darwin_locale (void) } #endif +#ifdef HOST_ANDROID +static JavaVM *sJavaVM = NULL; + +JNIEXPORT jint JNI_OnLoad(JavaVM *jvm, void *reserved) +{ + __android_log_print(ANDROID_LOG_INFO, "Mono", "JNI_OnLoad called"); + sJavaVM = jvm; + return JNI_VERSION_1_6; +} + +JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved) +{ + __android_log_print(ANDROID_LOG_INFO, "Mono", "JNI_OnUnload called"); + sJavaVM = NULL; +} + +static gchar* +get_android_locale (void) +{ + static gchar *cached_locale = NULL; + JNIEnv* env = NULL; + jint detached; + jclass localeClass; + + if (cached_locale != NULL) + return g_strdup (cached_locale); + + if (sJavaVM == NULL) + { + __android_log_print(ANDROID_LOG_INFO, "Mono", "Java VM not initialized"); + return NULL; + } + detached = (*sJavaVM)->GetEnv(sJavaVM, (void**)&env, JNI_VERSION_1_2) == JNI_EDETACHED; + if (detached) + { + (*sJavaVM)->AttachCurrentThread(sJavaVM, &env, NULL); + } + + localeClass = (*env)->FindClass(env, "java/util/Locale"); + if (localeClass != NULL) { + jmethodID getDefault = (*env)->GetStaticMethodID(env, localeClass, "getDefault", "()Ljava/util/Locale;"); + if (getDefault != NULL) { + jmethodID toLanguageTag; + jstring tag; + const char *nativeTag; + jobject def = (*env)->CallStaticObjectMethod(env, localeClass, getDefault); + toLanguageTag = (*env)->GetMethodID(env, localeClass, "toLanguageTag", "()Ljava/lang/String;"); + // toLanguageTag is available since API 21 only, so returning default locale for Android 4.4 + if (toLanguageTag != NULL) { + tag = (jstring)(*env)->CallObjectMethod(env, def, toLanguageTag); + nativeTag = (*env)->GetStringUTFChars(env, tag, NULL); + __android_log_print(ANDROID_LOG_INFO, "Mono", "Locale %s", nativeTag); + cached_locale = g_strdup (nativeTag); + (*env)->ReleaseStringUTFChars(env, tag, nativeTag); + } + } + } + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + } + + if (detached) + (*sJavaVM)->DetachCurrentThread(sJavaVM); + + mono_memory_barrier (); + return cached_locale ? g_strdup (cached_locale) : NULL; +} +#endif + static char * get_posix_locale (void) { @@ -561,6 +635,11 @@ get_current_locale_name (void) locale = get_darwin_locale (); if (!locale) locale = get_posix_locale (); +#elif defined HOST_ANDROID + __android_log_print(ANDROID_LOG_INFO, "MONO", "Getting locale"); + locale = get_android_locale(); + if (!locale) + locale = get_posix_locale(); #else locale = get_posix_locale (); #endif diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index d2859202fbe6..f8e08f5913d4 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -1616,9 +1616,11 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv mono_mb_emit_byte (mb, CEE_STIND_REF); break; } - case MONO_MARSHAL_CONV_ARRAY_LPARRAY: - g_error ("Structure field of type %s can't be marshalled as LPArray", mono_class_from_mono_type (type)->name); + case MONO_MARSHAL_CONV_ARRAY_LPARRAY: { + char *msg = g_strdup_printf ("Structure field of type %s can't be marshalled as LPArray", mono_class_from_mono_type (type)->name); + mono_mb_emit_exception_marshal_directive (mb, msg); break; + } #ifndef DISABLE_COM case MONO_MARSHAL_CONV_OBJECT_INTERFACE: @@ -1823,6 +1825,10 @@ emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv if (type->type == MONO_TYPE_SZARRAY) { eklass = type->data.klass; + } else if (type->type == MONO_TYPE_ARRAY) { + eklass = type->data.array->eklass; + if (!eklass->blittable) + g_assert_not_reached (); } else { g_assert_not_reached (); } @@ -2275,12 +2281,20 @@ emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_obje mono_mb_emit_byte (mb, mono_type_to_stind (ftype)); } break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (ftype)) { + char *msg = g_strdup_printf ("Generic type %s cannot be marshaled as field in a struct.", + mono_type_full_name (ftype)); + mono_mb_emit_exception_marshal_directive (mb, msg); + break; + } + /* fall through */ case MONO_TYPE_VALUETYPE: { int src_var, dst_var; MonoType *etype; int len; - if (ftype->data.klass->enumtype) { + if (t == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) { ftype = mono_class_enum_basetype (ftype->data.klass); goto handle_enum; } @@ -2298,7 +2312,7 @@ emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_obje if (get_fixed_buffer_attr (info->fields [i].field, &etype, &len)) { emit_fixed_buf_conv (mb, ftype, etype, len, to_object, &usize); } else { - emit_struct_conv (mb, ftype->data.klass, to_object); + emit_struct_conv (mb, mono_class_from_mono_type (ftype), to_object); } /* restore the old src pointer */ @@ -3930,6 +3944,11 @@ get_runtime_invoke_type (MonoType *t, gboolean ret) if (t->byref) { if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) return t; + + /* The result needs loaded indirectly */ + if (ret) + return t; + /* Can't share this with 'I' as that needs another indirection */ return &mono_defaults.int_class->this_arg; } @@ -4137,8 +4156,18 @@ emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method, } if (sig->ret->byref) { - /* fixme: */ - g_assert_not_reached (); + int ldind_op; + /* perform indirect load and return by value */ + MonoType* ret_byval = &mono_class_from_mono_type (sig->ret)->byval_arg; + g_assert (!ret_byval->byref); + ldind_op = mono_type_to_ldind (ret_byval); + /* taken from similar code in mini-generic-sharing.c + * we need to use mono_mb_emit_op to add method data when loading + * a structure since method-to-ir needs this data for wrapper methods */ + if (ldind_op == CEE_LDOBJ) + mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (ret_byval)); + else + mono_mb_emit_byte (mb, ldind_op); } switch (sig->ret->type) { @@ -9808,6 +9837,11 @@ get_virtual_stelemref_kind (MonoClass *element_class) return STELEMREF_OBJECT; if (is_monomorphic_array (element_class)) return STELEMREF_SEALED_CLASS; + + /* magic ifaces requires aditional checks for when the element type is an array */ + if (MONO_CLASS_IS_INTERFACE (element_class) && element_class->is_array_special_interface) + return STELEMREF_COMPLEX; + /* Compressed interface bitmaps require code that is quite complex, so don't optimize for it. */ if (MONO_CLASS_IS_INTERFACE (element_class) && !mono_class_has_variant_generic_params (element_class)) #ifdef COMPRESSED_INTERFACE_BITMAP diff --git a/mono/metadata/mempool.c b/mono/metadata/mempool.c index be5af1083f22..fba66778148d 100644 --- a/mono/metadata/mempool.c +++ b/mono/metadata/mempool.c @@ -434,3 +434,19 @@ mono_mempool_get_bytes_allocated (void) { return UnlockedRead64 (&total_bytes_allocated); } + +void +mono_mempool_foreach_block(MonoMemPool* pool, mono_mempool_block_proc callback, void* user_data) +{ + MonoMemPool *current = pool; + + while (current) + { + gpointer end = (guint8*)current + current->size; + callback(current, end, user_data); + current = current->next; + } +} + + + diff --git a/mono/metadata/mempool.h b/mono/metadata/mempool.h index 1abc25cc406e..7e75cd138cab 100644 --- a/mono/metadata/mempool.h +++ b/mono/metadata/mempool.h @@ -41,6 +41,11 @@ mono_mempool_strdup (MonoMemPool *pool, const char *s); MONO_API uint32_t mono_mempool_get_allocated (MonoMemPool *pool); +typedef void(*mono_mempool_block_proc)(void* start, void* end, void* user_data); + +MONO_API void +mono_mempool_foreach_block(MonoMemPool* pool, mono_mempool_block_proc callback, void* user_data); + MONO_END_DECLS #endif diff --git a/mono/metadata/metadata-internals.h b/mono/metadata/metadata-internals.h index e849706e19e9..d14ef8488d86 100644 --- a/mono/metadata/metadata-internals.h +++ b/mono/metadata/metadata-internals.h @@ -195,6 +195,9 @@ struct _MonoImage { /* If the raw data was allocated from a source such as mmap, the allocator may store resource tracking information here. */ void *raw_data_handle; char *raw_data; +#ifdef IL2CPP_ON_MONO + void* il2cpp_codegen_handle; +#endif guint32 raw_data_len; guint8 raw_buffer_used : 1; guint8 raw_data_allocated : 1; @@ -403,11 +406,6 @@ struct _MonoImage { /* Maps malloc-ed char* pinvoke scope -> malloced-ed char* filename */ GHashTable *pinvoke_scope_filenames; - /* Indexed by MonoGenericParam pointers */ - GHashTable **gshared_types; - /* The length of the above array */ - int gshared_types_len; - /* The loader used to load this image */ MonoImageLoader *loader; @@ -447,6 +445,11 @@ typedef struct { MonoWrapperCaches wrapper_caches; + /* Indexed by MonoGenericParam pointers */ + GHashTable **gshared_types; + /* The length of the above array */ + int gshared_types_len; + mono_mutex_t lock; /* @@ -621,6 +624,8 @@ typedef struct { char *aot_options; } MonoAotCacheConfig; +typedef void(*MonoImageSetFunc) (MonoImageSet *imageSet, void* user_data); + #define MONO_SIZEOF_METHOD_SIGNATURE (sizeof (struct _MonoMethodSignature) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) static inline gboolean @@ -722,6 +727,14 @@ mono_image_set_unlock (MonoImageSet *set); char* mono_image_set_strdup (MonoImageSet *set, const char *s); +void mono_metadata_image_set_foreach(MonoImageSetFunc func, gpointer user_data); + +MonoImageSet * +mono_metadata_get_image_set_for_type (MonoType *type); + +MonoImageSet * +mono_metadata_merge_image_sets (MonoImageSet *set1, MonoImageSet *set2); + #define mono_image_set_new0(image,type,size) ((type *) mono_image_set_alloc0 (image, sizeof (type)* (size))) gboolean @@ -964,5 +977,10 @@ mono_loader_get_strict_strong_names (void); char* mono_signature_get_managed_fmt_string (MonoMethodSignature *sig); +gboolean +mono_type_in_image (MonoType *type, MonoImage *image); + +#define MONO_CLASS_IS_INTERFACE_INTERNAL(c) ((mono_class_get_flags (c) & TYPE_ATTRIBUTE_INTERFACE) || mono_type_is_generic_parameter (&c->byval_arg)) + #endif /* __MONO_METADATA_INTERNALS_H__ */ diff --git a/mono/metadata/metadata-verify.c b/mono/metadata/metadata-verify.c index 94ecae8b9414..99fb4638d564 100644 --- a/mono/metadata/metadata-verify.c +++ b/mono/metadata/metadata-verify.c @@ -3967,7 +3967,7 @@ mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list) { VerifyContext ctx; - if (!mono_verifier_is_enabled_for_image (image)) + if (!mono_verifier_is_enabled_for_image (image) && !mono_verifier_is_enabled_for_pe_only()) return TRUE; init_verify_context (&ctx, image, error_list != NULL); diff --git a/mono/metadata/metadata.c b/mono/metadata/metadata.c index b2d75f335d80..f51a074731f9 100644 --- a/mono/metadata/metadata.c +++ b/mono/metadata/metadata.c @@ -2394,13 +2394,25 @@ type_in_image (MonoType *type, MonoImage *image) return signature_in_image (type->data.method, image); case MONO_TYPE_VAR: case MONO_TYPE_MVAR: - return image == get_image_for_generic_param (type->data.generic_param); + if (image == get_image_for_generic_param (type->data.generic_param)) + return TRUE; + else if (type->data.generic_param->gshared_constraint) { + type = type->data.generic_param->gshared_constraint; + goto retry; + } + return FALSE; default: /* At this point, we should've avoided all potential allocations in mono_class_from_mono_type () */ return image == mono_class_from_mono_type (type)->image; } } +gboolean +mono_type_in_image (MonoType *type, MonoImage *image) +{ + return type_in_image (type, image); +} + static inline void image_sets_lock (void) { @@ -2618,6 +2630,12 @@ delete_image_set (MonoImageSet *set) if (set->ptr_cache) g_hash_table_destroy (set->ptr_cache); + for (i = 0; i < set->gshared_types_len; ++i) { + if (set->gshared_types [i]) + g_hash_table_destroy (set->gshared_types [i]); + } + g_free (set->gshared_types); + mono_wrapper_caches_free (&set->wrapper_caches); image_sets_lock (); @@ -2849,6 +2867,9 @@ collect_type_images (MonoType *type, CollectData *data) { MonoImage *image = get_image_for_generic_param (type->data.generic_param); add_image (image, data); + type = type->data.generic_param->gshared_constraint; + if (type) + goto retry; break; } case MONO_TYPE_CLASS: @@ -3096,19 +3117,25 @@ mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericConte } MonoImageSet * -mono_metadata_get_image_set_for_class (MonoClass *klass) +mono_metadata_get_image_set_for_type (MonoType *type) { MonoImageSet *set; CollectData image_set_data; collect_data_init (&image_set_data); - collect_type_images (&klass->byval_arg, &image_set_data); + collect_type_images (type, &image_set_data); set = get_image_set (image_set_data.images, image_set_data.nimages); collect_data_free (&image_set_data); return set; } +MonoImageSet * +mono_metadata_get_image_set_for_class (MonoClass *klass) +{ + return mono_metadata_get_image_set_for_type (&klass->byval_arg); +} + MonoImageSet * mono_metadata_get_image_set_for_method (MonoMethodInflated *method) { @@ -3123,6 +3150,29 @@ mono_metadata_get_image_set_for_method (MonoMethodInflated *method) return set; } +MonoImageSet * +mono_metadata_merge_image_sets (MonoImageSet *set1, MonoImageSet *set2) +{ + MonoImage **images = g_newa (MonoImage*, set1->nimages + set2->nimages); + + /* Add images from set1 */ + memcpy (images, set1->images, sizeof (MonoImage*) * set1->nimages); + + int nimages = set1->nimages; + // FIXME: Quaratic + /* Add images from set2 */ + for (int i = 0; i < set2->nimages; ++i) { + int j; + for (j = 0; j < set1->nimages; ++j) { + if (set2->images [i] == set1->images [j]) + break; + } + if (j == set1->nimages) + images [nimages ++] = set2->images [i]; + } + return get_image_set (images, nimages); +} + static gboolean type_is_gtd (MonoType *type) { @@ -4788,6 +4838,7 @@ mono_metadata_localscope_from_methoddef (MonoImage *meta, guint32 index) static void mono_backtrace (int limit) { +#ifndef _MSC_VER void *array[limit]; char **names; int i; @@ -4797,6 +4848,7 @@ mono_backtrace (int limit) g_print ("\t%s\n", names [i]); } g_free (names); +#endif } #endif @@ -5072,6 +5124,66 @@ mono_metadata_generic_class_is_valuetype (MonoGenericClass *gclass) return gclass->container_class->valuetype; } +typedef struct +{ + MonoGenericClassFunc func; + gpointer user_data; +} GenericClassForeachData; + + +static void +generic_class_foreach_callback(gpointer key, gpointer value, gpointer user_data) +{ + GenericClassForeachData* data = (GenericClassForeachData*)user_data; + data->func(key, data->user_data); +} + +void +mono_metadata_generic_class_foreach(MonoGenericClassFunc func, void* user_data) +{ + GenericClassForeachData data; + guint i; + + data.func = func; + data.user_data = user_data; + + for(i = 0; i < HASH_TABLE_SIZE; ++i) + { + MonoImageSet* imageSet = img_set_cache[i]; + + if (imageSet == NULL || imageSet->gclass_cache == NULL) + continue; + + mono_image_set_lock(imageSet); + + mono_conc_hashtable_foreach(imageSet->gclass_cache, generic_class_foreach_callback, &data); + + mono_image_set_unlock(imageSet); + } +} + +void +mono_metadata_image_set_foreach(MonoImageSetFunc func, gpointer user_data) +{ + GenericClassForeachData data; + guint i; + + data.func = func; + data.user_data = user_data; + + for (i = 0; i < HASH_TABLE_SIZE; ++i) + { + MonoImageSet* imageSet = img_set_cache[i]; + + if (imageSet == NULL) + continue; + + mono_image_set_lock(imageSet); + func(imageSet, user_data); + mono_image_set_unlock(imageSet); + } +} + static gboolean _mono_metadata_generic_class_equal (const MonoGenericClass *g1, const MonoGenericClass *g2, gboolean signature_only) { @@ -6599,6 +6711,18 @@ mono_type_is_byref (MonoType *type) return type->byref; } +/** + * mono_type_get_attrs: + * @type: the MonoType operated on + * + * Returns: the param attributes. + */ +uint32_t +mono_type_get_attrs (MonoType *type) +{ + return type->attrs; +} + /** * mono_type_get_type: * \param type the \c MonoType operated on @@ -6640,6 +6764,13 @@ mono_type_get_class (MonoType *type) return type->data.klass; } +MonoGenericClass* +m_type_get_generic_class (MonoType *type) +{ + /* FIXME: review the runtime users before adding the assert here */ + return type->data.generic_class; +} + /** * mono_type_get_array_type: * \param type the \c MonoType operated on @@ -6728,6 +6859,11 @@ mono_type_is_pointer (MonoType *type) mono_bool mono_type_is_reference (MonoType *type) { + /* NOTE: changing this function to return TRUE more often may have + * consequences for generic sharing in the AOT compiler. In + * particular, returning TRUE for generic parameters with a 'class' + * constraint may cause crashes. + */ return (type && (((type->type == MONO_TYPE_STRING) || (type->type == MONO_TYPE_SZARRAY) || (type->type == MONO_TYPE_CLASS) || (type->type == MONO_TYPE_OBJECT) || (type->type == MONO_TYPE_ARRAY)) || diff --git a/mono/metadata/metadata.h b/mono/metadata/metadata.h index 874852c9b076..b4a75a2ff051 100644 --- a/mono/metadata/metadata.h +++ b/mono/metadata/metadata.h @@ -332,9 +332,14 @@ typedef enum { MONO_PARSE_FIELD } MonoParseTypeMode; +typedef void(*MonoGenericClassFunc) (MonoGenericClass *genericClass, void* user_data); + MONO_API mono_bool mono_type_is_byref (MonoType *type); +MONO_API uint32_t +mono_type_get_attrs (MonoType *type); + MONO_API int mono_type_get_type (MonoType *type); @@ -346,6 +351,9 @@ mono_type_get_signature (MonoType *type); MONO_API MonoClass* mono_type_get_class (MonoType *type); +MonoGenericClass* +m_type_get_generic_class (MonoType *type); + MONO_API MonoArrayType* mono_type_get_array_type (MonoType *type); @@ -423,6 +431,7 @@ MONO_API int mono_type_stack_size (MonoType *type, MONO_API mono_bool mono_type_generic_inst_is_valuetype (MonoType *type); MONO_API mono_bool mono_metadata_generic_class_is_valuetype (MonoGenericClass *gclass); +MONO_API void mono_metadata_generic_class_foreach(MonoGenericClassFunc func, void* user_data); MONO_API unsigned int mono_metadata_type_hash (MonoType *t1); MONO_API mono_bool mono_metadata_type_equal (MonoType *t1, MonoType *t2); diff --git a/mono/metadata/mono-debug.h b/mono/metadata/mono-debug.h index 66365a01c60f..29bbcaa9a80c 100644 --- a/mono/metadata/mono-debug.h +++ b/mono/metadata/mono-debug.h @@ -201,6 +201,9 @@ mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset); MONO_API MonoDebugSourceLocation * mono_debug_lookup_source_location (MonoMethod *method, uint32_t address, MonoDomain *domain); +MONO_API MonoDebugSourceLocation * +mono_debug_lookup_source_location_by_il (MonoMethod *method, uint32_t il_offset, MonoDomain *domain); + MONO_API int32_t mono_debug_il_offset_from_address (MonoMethod *method, MonoDomain *domain, uint32_t native_offset); diff --git a/mono/metadata/mono-gc.h b/mono/metadata/mono-gc.h index 455209aa8dc3..dbf2971efc20 100644 --- a/mono/metadata/mono-gc.h +++ b/mono/metadata/mono-gc.h @@ -104,14 +104,20 @@ typedef enum { } MonoGCHandleType; MONO_API void mono_gc_collect (int generation); +MONO_API int mono_gc_collect_a_little (); MONO_API int mono_gc_max_generation (void); MONO_API int mono_gc_get_generation (MonoObject *object); MONO_API int mono_gc_collection_count (int generation); MONO_API int64_t mono_gc_get_used_size (void); MONO_API int64_t mono_gc_get_heap_size (void); +MONO_API int64_t mono_gc_get_max_time_slice_ns (); +MONO_API void mono_gc_set_max_time_slice_ns (int64_t maxTimeSlice); MONO_API MonoBoolean mono_gc_pending_finalizers (void); +MONO_API MonoBoolean mono_gc_is_incremental (void); +MONO_API void mono_gc_set_incremental(MonoBoolean value); MONO_API void mono_gc_finalize_notify (void); MONO_API int mono_gc_invoke_finalizers (void); +MONO_API void mono_gc_start_incremental_collection(); /* heap walking is only valid in the pre-stop-world event callback */ MONO_API int mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data); diff --git a/mono/metadata/mono-hash.c b/mono/metadata/mono-hash.c index b62947b83415..73d623d573e7 100644 --- a/mono/metadata/mono-hash.c +++ b/mono/metadata/mono-hash.c @@ -32,10 +32,13 @@ #include #include "mono-hash.h" #include "metadata/gc-internals.h" +#include "metadata/il2cpp-compat-metadata.h" #include #include #include +void il2cpp_mono_gc_wbarrier_generic_store (void* ptr, MonoObject* value); + gint32 mono_g_hash_table_max_chain_length; struct _MonoGHashTable { diff --git a/mono/metadata/null-gc.c b/mono/metadata/null-gc.c index 6e35625cbeb9..7f91ee38c0ad 100644 --- a/mono/metadata/null-gc.c +++ b/mono/metadata/null-gc.c @@ -55,6 +55,11 @@ mono_gc_collect (int generation) { } +void +mono_gc_start_incremental_collection() +{ +} + int mono_gc_max_generation (void) { @@ -91,6 +96,28 @@ mono_gc_get_heap_size (void) return 2*1024*1024; } +int64_t +mono_gc_get_max_time_slice_ns() +{ + return 0; +} + +void +mono_gc_set_max_time_slice_ns(int64_t maxTimeSlice) +{ +} + +MonoBoolean +mono_gc_is_incremental() +{ + return FALSE; +} + +void +mono_gc_set_incremental(MonoBoolean value) +{ +} + gboolean mono_gc_is_gc_thread (void) { @@ -296,6 +323,11 @@ mono_gc_thread_attach (MonoThreadInfo* info) return info; } +void +mono_gc_thread_detach (MonoThreadInfo *p) +{ +} + void mono_gc_thread_detach_with_lock (MonoThreadInfo *p) { @@ -403,6 +435,12 @@ mono_gc_is_moving (void) return FALSE; } +gboolean +mono_gc_needs_write_barriers(void) +{ + return FALSE; +} + gboolean mono_gc_is_disabled (void) { @@ -453,16 +491,6 @@ mono_gc_get_logfile (void) return NULL; } -void -mono_gc_params_set (const char* options) -{ -} - -void -mono_gc_debug_set (const char* options) -{ -} - void mono_gc_conservatively_scan_area (void *start, void *end) { diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h index cb42761d6c20..ed7bdd3f1888 100644 --- a/mono/metadata/object-internals.h +++ b/mono/metadata/object-internals.h @@ -1690,7 +1690,7 @@ typedef enum { MonoRuntimeUnhandledExceptionPolicy mono_runtime_unhandled_exception_policy_get (void); -void +UNITY_MONO_API void mono_runtime_unhandled_exception_policy_set (MonoRuntimeUnhandledExceptionPolicy policy); void diff --git a/mono/metadata/object.c b/mono/metadata/object.c index 3d69dca594e3..6f0d4c03cc21 100644 --- a/mono/metadata/object.c +++ b/mono/metadata/object.c @@ -1993,9 +1993,11 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro * vtable field in MonoObject, since we can no longer assume the * vtable is reachable by other roots after the appdomain is unloaded. */ - if (!mono_gc_is_moving () && domain != mono_get_root_domain () && !mono_dont_free_domains) +#if HAVE_BOEHM_GC + if (domain != mono_get_root_domain () && !mono_dont_free_domains) vt->gc_descr = MONO_GC_DESCRIPTOR_NULL; else +#endif vt->gc_descr = klass->gc_descr; gc_bits = mono_gc_get_vtable_bits (klass); @@ -2084,6 +2086,10 @@ mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoErro /* it's a pointer type: add check */ g_assert ((fklass->byval_arg.type == MONO_TYPE_PTR) || (fklass->byval_arg.type == MONO_TYPE_FNPTR)); *t = *(char *)data; + /* This is not needed by sgen, as it does not seem ++ to need write barriers for uncollectable objects (like the vtables storing static ++ fields), but it is needed for incremental boehm. */ + mono_gc_wbarrier_generic_nostore (t); } continue; } @@ -3294,6 +3300,11 @@ mono_field_static_set_value (MonoVTable *vt, MonoClassField *field, void *value) dest = (char*)mono_vtable_get_static_field_data (vt) + field->offset; } mono_copy_value (field->type, dest, value, FALSE); + /* This is not needed by sgen, as it does not seem + to need write barriers for uncollectable objects (like the vtables storing static ++ fields), but it is needed for incremental boehm. */ + if (field->offset == -1) + mono_gc_wbarrier_generic_nostore (dest); } /** @@ -5196,7 +5207,9 @@ mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, if (mono_class_is_nullable (method->klass)) { /* Need to create a boxed vtype instead */ +#ifndef IL2CPP_ON_MONO g_assert (!obj); +#endif if (!params) return NULL; @@ -5690,7 +5703,7 @@ mono_array_full_copy (MonoArray *src, MonoArray *dest) static void array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size) { - if (mono_gc_is_moving ()) { + if (mono_gc_needs_write_barriers()) { if (klass->element_class->valuetype) { if (klass->element_class->has_references) mono_value_copy_array (dest, 0, mono_array_addr_with_size_fast (src, 0, 0), mono_array_length (src)); @@ -6283,6 +6296,24 @@ mono_string_new_len_checked (MonoDomain *domain, const char *text, guint length, return o; } +static MonoString* +mono_string_new_internal (MonoDomain *domain, const char *text) +{ + MonoError error; + MonoString *res = NULL; + res = mono_string_new_checked (domain, text, &error); + if (!is_ok (&error)) { + /* Mono API compatability: assert on Out of Memory errors, + * return NULL otherwise (most likely an invalid UTF-8 byte + * sequence). */ + if (mono_error_get_error_code (&error) == MONO_ERROR_OUT_OF_MEMORY) + mono_error_assert_ok (&error); + else + mono_error_cleanup (&error); + } + return res; +} + /** * mono_string_new: * \param text a pointer to a UTF-8 string @@ -6293,11 +6324,7 @@ mono_string_new_len_checked (MonoDomain *domain, const char *text, guint length, MonoString* mono_string_new (MonoDomain *domain, const char *text) { - MonoError error; - MonoString *res = NULL; - res = mono_string_new_checked (domain, text, &error); - mono_error_assert_ok (&error); - return res; + return mono_string_new_internal (domain, text); } /** @@ -6370,16 +6397,7 @@ mono_string_new_wrapper (const char *text) { MONO_REQ_GC_UNSAFE_MODE; - MonoDomain *domain = mono_domain_get (); - - if (text) { - MonoError error; - MonoString *result = mono_string_new_checked (domain, text, &error); - mono_error_assert_ok (&error); - return result; - } - - return NULL; + return mono_string_new_internal (mono_domain_get (), text); } /** @@ -6429,7 +6447,7 @@ mono_value_box_checked (MonoDomain *domain, MonoClass *klass, gpointer value, Mo size = size - sizeof (MonoObject); - if (mono_gc_is_moving ()) { + if (mono_gc_needs_write_barriers()) { g_assert (size == mono_class_value_size (klass, NULL)); mono_gc_wbarrier_value_copy ((char *)res + sizeof (MonoObject), value, 1, klass); } else { diff --git a/mono/metadata/oop.c b/mono/metadata/oop.c new file mode 100644 index 000000000000..f58fa1389851 --- /dev/null +++ b/mono/metadata/oop.c @@ -0,0 +1,434 @@ +/* + * oop.c: These functions allow us to access the MonoDomain internals for purposes of post-mortem + * inspection by another process. All data is immutable: these calls are guaranteed to have no + * side-effects. These routines are not thread safe. This does not work with AOT modules. + * + * Author: + * Pete Lewis + * + * Copyright 2017 Unity Technologies (http://www.unity3d.com) + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _M_X64 +#include +extern GList* g_dynamic_function_table_begin; +extern SRWLOCK g_dynamic_function_table_lock; +#endif + +// petele: todo: move this structure into a mono header +typedef struct _MonoStackFrameDetails +{ + char* methodName; + size_t methodNameLen; + char* className; + size_t classNameLen; + char* assemblyName; + size_t assemblyNameLen; +} MonoStackFrameDetails; + +typedef gboolean(*ReadMemoryCallback)(void* buffer, gsize* read, const void* address, gsize size, void* userdata); +typedef gboolean(*ReadExceptionCallback)(const void* address, gsize size, void* userdata); + +typedef struct _OutOfProcessMono +{ + ReadMemoryCallback readMemory; + ReadExceptionCallback readException; + void* userData; +} OutOfProcessMono; + +static OutOfProcessMono g_oop = { NULL, NULL }; + +#define OFFSET_MEMBER(type, base, member) ((gpointer)((gchar*)(base) + offsetof(type, member))) + +void read_exception(const void* address, gsize size) +{ + g_assert(g_oop.readException); + g_oop.readException(address, size, g_oop.userData); +} + +gsize read_memory(void* buffer, const void* address, gsize size) +{ + if (!buffer || !size) + return 0; + + gsize read = 0; + if (!g_oop.readMemory || !g_oop.readMemory(buffer, &read, address, size, g_oop.userData)) { + read_exception(address, size); + } + + return read; +} + +// Read a null-terminated string out-of-process +gsize read_nt_string(char* buffer, gsize max_size, const void* address) +{ + if (!buffer || !max_size) + return 0; + + if (!g_oop.readMemory) { + read_exception(address, 1); + return 0; + } + + gsize read = 0; + if (!g_oop.readMemory(buffer, &read, address, max_size, g_oop.userData)) { + // Failed to read, but just because we may not have read max_size, we still + // might be OK if at least one character was read (i.e. the null-terminator) + if (read == 0) + read_exception(address, 1); + } + + // Ensure there's a null-terminator + buffer[min(read, max_size-1)] = '\0'; + + return read; +} + +gpointer read_pointer(const void* address) +{ + gpointer ptr = NULL; + read_memory(&ptr, address, sizeof(ptr)); + return ptr; +} + +gint64 read_qword(const void* address) +{ + gint64 v = 0; + read_memory(&v, address, sizeof(v)); + return v; +} + +gint32 read_dword(const void* address) +{ + gint32 v = 0; + read_memory(&v, address, sizeof(v)); + return v; +} + +GList* read_glist_next(GList* list) { return (GList*) read_pointer(OFFSET_MEMBER(GList, list, next)); } +gpointer read_glist_data(GList* list) { return read_pointer(OFFSET_MEMBER(GList, list, data)); } + +MONO_API void +mono_unity_oop_init( + ReadMemoryCallback rmcb, + ReadExceptionCallback recb, + void* userdata) +{ + g_oop.readMemory = rmcb; + g_oop.readException = recb; + g_oop.userData = userdata; +} + +MONO_API void +mono_unity_oop_shutdown(void) +{ + memset(&g_oop, 0, sizeof(g_oop)); +} + +#ifdef _M_X64 +gboolean TryAcquireSpinWait(PSRWLOCK lock, unsigned int spinWait) +{ + do + { + if (TryAcquireSRWLockExclusive(&g_dynamic_function_table_lock)) + return TRUE; + } while (spinWait--); + + return FALSE; +} +#endif + +MONO_API GList* +mono_unity_lock_dynamic_function_access_tables64(unsigned int spinWait) +{ +#ifdef _M_X64 + if (spinWait >= 0x7fffffff) { + AcquireSRWLockExclusive(&g_dynamic_function_table_lock); + } + else if (!TryAcquireSpinWait(&g_dynamic_function_table_lock, spinWait)) { + return NULL; + } + return g_dynamic_function_table_begin; +#else + return NULL; +#endif +} + +MONO_API void +mono_unity_unlock_dynamic_function_access_tables64(void) +{ +#ifdef _M_X64 + ReleaseSRWLockExclusive(&g_dynamic_function_table_lock); +#else + return NULL; +#endif +} + +MONO_API GList* +mono_unity_oop_iterate_dynamic_function_access_tables64( + GList* current) +{ +#ifdef _M_X64 + if (current != NULL) + return read_glist_next(current); + else + return NULL; +#else + return NULL; +#endif +} + +MONO_API gboolean +mono_unity_oop_get_dynamic_function_access_table64( + GList* tableEntry, + gsize* moduleStart, + gsize* moduleEnd, + void** functionTable, + gsize* functionTableSize) +{ +#ifdef _M_X64 + if (!tableEntry || !moduleStart || !moduleEnd || !functionTable || !functionTableSize) + return FALSE; + + const DynamicFunctionTableEntry* entry = read_glist_data(tableEntry); + *moduleStart = read_qword(OFFSET_MEMBER(DynamicFunctionTableEntry, entry, begin_range)); + *moduleEnd = read_qword(OFFSET_MEMBER(DynamicFunctionTableEntry, entry, end_range)); + *functionTable = read_pointer(OFFSET_MEMBER(DynamicFunctionTableEntry, entry, rt_funcs)); + *functionTableSize = read_dword(OFFSET_MEMBER(DynamicFunctionTableEntry, entry, rt_funcs_max_count)); + + return TRUE; +#else + return FALSE; +#endif +} + +static int oop_jit_info_table_index( + const MonoJitInfoTableChunk** chunks, // non-local + int num_chunks, + const gint8* addr) +{ + static const int error = 0x7fffffff; + + int left = 0, right = num_chunks; + + g_assert(left < right); + + do { + const MonoJitInfoTableChunk* chunkPtr; + const gint8* last_code_end; + int pos = (left + right) / 2; + + chunkPtr = read_pointer(chunks + pos); + if (chunkPtr == NULL) + return error; + + last_code_end = read_pointer(OFFSET_MEMBER(MonoJitInfoTableChunk, chunkPtr, last_code_end)); + if (last_code_end == NULL) + return error; + + if (addr < last_code_end) + right = pos; + else + left = pos + 1; + } while (left < right); + g_assert(left == right); + + if (left >= num_chunks) + return num_chunks - 1; + return left; +} + +static int +oop_jit_info_table_chunk_index( + const MonoJitInfo** chunk_data, + int num_elements, + const gint8 *addr) +{ + const MonoJitInfo* ji; + int left = 0, right = num_elements; + + while (left < right) { + int pos = (left + right) / 2; + + const gint8 *code_start; + const gint8 *code_end; + int code_size; + + ji = chunk_data[pos]; + + code_start = (const gint8*)read_pointer(OFFSET_MEMBER(MonoJitInfo, ji, code_start)); + code_size = read_dword(OFFSET_MEMBER(MonoJitInfo, ji, code_size)); + code_end = code_start + code_size; + + if (addr < code_end) + right = pos; + else + left = pos + 1; + } + + g_assert(left == right); + + return left; +} + +/* This method is an out-of-process version of jit_info_table_find. */ +static const MonoJitInfo* +oop_jit_info_table_find( + const MonoDomain *domain, + const char *addr, + gboolean allow_trampolines) +{ + const MonoJitInfoTable* tablePtr; + const MonoJitInfoTableChunk** chunkListPtr; + MonoJitInfoTableChunk chunk; + MonoJitInfo ji; + int chunk_pos, pos; + + // Get the domain's jit_info_table pointer. + tablePtr = read_pointer(OFFSET_MEMBER(MonoDomain, domain, jit_info_table)); + if (tablePtr == NULL) + return NULL; + + int num_chunks = read_dword(OFFSET_MEMBER(MonoJitInfoTable, tablePtr, num_chunks)); + + // Get the chunk array + chunkListPtr = (const MonoJitInfoTableChunk**)OFFSET_MEMBER(MonoJitInfoTable, tablePtr, chunks); + + chunk_pos = oop_jit_info_table_index(chunkListPtr, num_chunks, (const gint8*)addr); + if (chunk_pos > num_chunks) + return NULL; + + // read the entire chunk + read_memory(&chunk, read_pointer(chunkListPtr + chunk_pos), sizeof(MonoJitInfoTableChunk)); + + pos = oop_jit_info_table_chunk_index((const MonoJitInfo**)chunk.data, chunk.num_elements, addr); + if (pos > chunk.num_elements) + return NULL; + + /* We now have a position that's very close to that of the + first element whose end address is higher than the one + we're looking for. If we don't have the exact position, + then we have a position below that one, so we'll just + search upward until we find our element. */ + do { + read_memory(&chunk, read_pointer(chunkListPtr + chunk_pos), sizeof(MonoJitInfoTableChunk)); + + while (pos < chunk.num_elements) { + read_memory(&ji, chunk.data[pos], sizeof(ji)); + + ++pos; + + if (ji.d.method == NULL) { + continue; + } + if ((gint8*)addr >= (gint8*)ji.code_start + && (gint8*)addr < (gint8*)ji.code_start + ji.code_size) { + if (ji.is_trampoline && !allow_trampolines) { + return NULL; + } + return chunk.data[pos-1]; + } + + /* If we find a non-tombstone element which is already + beyond what we're looking for, we have to end the + search. */ + if ((gint8*)addr < (gint8*)ji.code_start) + return NULL; + } + + ++chunk_pos; + pos = 0; + } while (chunk_pos < num_chunks); + + return NULL; +} + +MONO_API int +mono_unity_oop_get_stack_frame_details( + const MonoDomain* domain, + const void* frameAddress, + MonoStackFrameDetails* frameDetails) +{ + const MonoJitInfo* ji; + + ji = oop_jit_info_table_find(domain, (const char*)frameAddress, FALSE); + if (ji) + { + const MonoMethod* method = read_pointer(OFFSET_MEMBER(MonoJitInfo, ji, d.method)); + const MonoClass* klass = read_pointer(OFFSET_MEMBER(MonoMethod, method, klass)); + const MonoImage* image = read_pointer(OFFSET_MEMBER(MonoClass, klass, image)); + size_t classNameLen = max(frameDetails->classNameLen, 256); + char* className = (char*)malloc(classNameLen); + char* nsName = (char*)malloc(classNameLen); + + frameDetails->methodNameLen = read_nt_string( + frameDetails->methodName, + frameDetails->methodNameLen, + read_pointer(OFFSET_MEMBER(MonoMethod, method, name))); + + if (frameDetails->className && frameDetails->classNameLen > 0) { + read_nt_string( + nsName, + classNameLen, + read_pointer(OFFSET_MEMBER(MonoClass, klass, name_space))); + + read_nt_string( + className, + classNameLen, + read_pointer(OFFSET_MEMBER(MonoClass, klass, name))); + + if (*nsName) { + frameDetails->classNameLen = sprintf_s( + frameDetails->className, + frameDetails->classNameLen, + "%s.%s", + nsName, + className); + } else { + frameDetails->classNameLen = sprintf_s( + frameDetails->className, + frameDetails->classNameLen, + "%s", + className); + } + } + + frameDetails->assemblyNameLen = read_nt_string( + frameDetails->assemblyName, + frameDetails->assemblyNameLen, + read_pointer(OFFSET_MEMBER(MonoImage, image, assembly_name))); + + free(className); + free(nsName); + + return TRUE; + } + + return FALSE; +} diff --git a/mono/metadata/profiler-events.h b/mono/metadata/profiler-events.h index 5e933d0c9fbf..c603ca224323 100644 --- a/mono/metadata/profiler-events.h +++ b/mono/metadata/profiler-events.h @@ -95,6 +95,7 @@ MONO_PROFILER_EVENT_1(gc_finalized_object, GCFinalizedObject, MonoObject *, obje MONO_PROFILER_EVENT_5(gc_root_register, RootRegister, const mono_byte *, start, uintptr_t, size, MonoGCRootSource, source, const void *, key, const char *, name) MONO_PROFILER_EVENT_1(gc_root_unregister, RootUnregister, const mono_byte *, start) MONO_PROFILER_EVENT_3(gc_roots, GCRoots, uint64_t, count, const mono_byte *const *, addresses, MonoObject *const *, objects) +MONO_PROFILER_EVENT_2(fileio, FileIO, uint64_t, kind, uint64_t, size) MONO_PROFILER_EVENT_1(monitor_contention, MonitorContention, MonoObject *, object) MONO_PROFILER_EVENT_1(monitor_failed, MonitorFailed, MonoObject *, object) diff --git a/mono/metadata/profiler-private.h b/mono/metadata/profiler-private.h index 3c0fe896c4ff..08c8288a298d 100644 --- a/mono/metadata/profiler-private.h +++ b/mono/metadata/profiler-private.h @@ -51,7 +51,7 @@ typedef struct { gboolean code_coverage; mono_mutex_t coverage_mutex; - GHashTable *coverage_hash; + MonoDomainCoverage *coverage_domains; MonoProfilerHandle sampling_owner; MonoSemType sampling_semaphore; @@ -59,6 +59,7 @@ typedef struct { guint32 sample_freq; gboolean allocations; + gboolean fileio; gboolean call_contexts; void (*context_enable) (void); @@ -111,7 +112,18 @@ mono_profiler_installed (void) return !!mono_profiler_state.profilers; } -MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries); +MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoDomain* domain, MonoMethod *method, guint32 entries); + +struct _MonoDomainCoverage +{ + MonoDomain* domain; + GHashTable *coverage_hash; + mono_mutex_t mutex; + MonoDomainCoverage *next; +}; + +void mono_profiler_coverage_domain_init (MonoDomain* domain); +void mono_profiler_coverage_domain_free (MonoDomain* domain); struct _MonoProfilerCallContext { /* diff --git a/mono/metadata/profiler.c b/mono/metadata/profiler.c index 69c7e8030b32..52e6984d47c5 100644 --- a/mono/metadata/profiler.c +++ b/mono/metadata/profiler.c @@ -134,6 +134,7 @@ load_profiler_from_installation (const char *libname, const char *name, const ch * This function may \b only be called by embedders prior to running managed * code. */ +#ifndef RUNTIME_IL2CPP void mono_profiler_load (const char *desc) { @@ -167,6 +168,7 @@ mono_profiler_load (const char *desc) g_free (mname); } +#endif /** * mono_profiler_create: @@ -238,6 +240,7 @@ mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerClean * This function may \b only be called from a profiler's init function or prior * to running managed code. */ +#ifndef RUNTIME_IL2CPP mono_bool mono_profiler_enable_coverage (void) { @@ -245,13 +248,13 @@ mono_profiler_enable_coverage (void) return FALSE; mono_os_mutex_init (&mono_profiler_state.coverage_mutex); - mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL); if (!mono_debug_enabled ()) mono_debug_init (MONO_DEBUG_FORMAT_MONO); return mono_profiler_state.code_coverage = TRUE; } +#endif /** * mono_profiler_set_coverage_filter_callback: @@ -275,17 +278,89 @@ mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfi } static void -coverage_lock (void) +coverage_domains_lock (void) { mono_os_mutex_lock (&mono_profiler_state.coverage_mutex); } static void -coverage_unlock (void) +coverage_domains_unlock (void) { mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex); } +static MonoDomainCoverage * +get_coverage_for_domain(MonoDomain* domain) +{ + coverage_domains_lock(); + MonoDomainCoverage* cov = mono_profiler_state.coverage_domains; + while (cov) + { + if (cov->domain == domain) + break; + cov = cov->next; + } + coverage_domains_unlock(); + return cov; +} + +void +mono_profiler_coverage_domain_init(MonoDomain* domain) +{ + if (!mono_profiler_state.code_coverage) + return; + + MonoDomainCoverage* cov = g_new0(MonoDomainCoverage, 1); + cov->domain = domain; + cov->coverage_hash = g_hash_table_new(NULL, NULL); + mono_os_mutex_init(&cov->mutex); + + coverage_domains_lock(); + cov->next = mono_profiler_state.coverage_domains; + mono_profiler_state.coverage_domains = cov; + coverage_domains_unlock(); +} + +void +mono_profiler_coverage_domain_free(MonoDomain* domain) +{ + if (!mono_profiler_state.code_coverage) + return; + + coverage_domains_lock(); + + MonoDomainCoverage* cov = mono_profiler_state.coverage_domains; + MonoDomainCoverage** prev = &mono_profiler_state.coverage_domains; + while (cov) + { + if (cov->domain == domain) + break; + + prev = &cov->next; + cov = cov->next; + } + + if (cov != NULL) + { + *prev = cov->next; + + GHashTableIter iter; + g_hash_table_iter_init (&iter, cov->coverage_hash); + + MonoProfilerCoverageInfo *info; + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) + g_free (info); + + g_hash_table_destroy(cov->coverage_hash); + + mono_os_mutex_destroy(&cov->mutex); + + g_free(cov); + } + + coverage_domains_unlock(); +} + /** * mono_profiler_get_coverage_data: * @@ -299,6 +374,7 @@ coverage_unlock (void) * * This function is \b not async safe. */ +#ifndef RUNTIME_IL2CPP mono_bool mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb) { @@ -308,11 +384,13 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) return FALSE; - coverage_lock (); + MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get()); + + mono_os_mutex_lock(&domain->mutex); - MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method); + MonoProfilerCoverageInfo *info = g_hash_table_lookup (domain->coverage_hash, method); - coverage_unlock (); + mono_os_mutex_unlock(&domain->mutex); MonoError error; MonoMethodHeader *header = mono_method_get_header_checked (method, &error); @@ -403,8 +481,136 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, return TRUE; } +typedef struct +{ + MonoProfilerCoverageCallback cb; + MonoProfilerHandle handle; +} InvokeCallbackInfo; + +static void invoke_coverage_callback_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data) +{ + InvokeCallbackInfo* invokeInfo = (InvokeCallbackInfo*) user_data; + MonoMethod* method = (MonoMethod*)key; + MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value; + + MonoError error; + MonoMethodHeader *header = mono_method_get_header_checked (method, &error); + mono_error_assert_ok (&error); + + guint32 size; + + const unsigned char *start = mono_method_header_get_code (header, &size, NULL); + const unsigned char *end = start + size; + MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); + + for (guint32 i = 0; i < info->entries; i++) { + guchar *cil_code = info->data [i].cil_code; + + if (cil_code && cil_code >= start && cil_code < end) { + guint32 offset = cil_code - start; + + MonoProfilerCoverageData data = { + .method = method, + .il_offset = offset, + .counter = info->data [i].count, + .line = 1, + .column = 1, + }; + + if (minfo) { + MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset); + + if (loc) { + data.file_name = g_strdup (loc->source_file); + data.line = loc->row; + data.column = loc->column; + + mono_debug_free_source_location (loc); + } + } + + invokeInfo->cb (invokeInfo->handle->prof, &data); + + g_free ((char *) data.file_name); + } + } + + mono_metadata_free_mh (header); +} + +mono_bool +mono_profiler_get_all_coverage_data(MonoProfilerHandle handle, MonoProfilerCoverageCallback cb) +{ + if (!mono_profiler_state.code_coverage) + return FALSE; + + InvokeCallbackInfo info; + info.cb = cb; + info.handle = handle; + + MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get()); + + mono_os_mutex_lock(&domain->mutex); + + g_hash_table_foreach (domain->coverage_hash, invoke_coverage_callback_for_hashtable_entry, &info); + + mono_os_mutex_unlock(&domain->mutex); + + return TRUE; +} + +mono_bool +mono_profiler_reset_coverage(MonoMethod* method) +{ + if (!mono_profiler_state.code_coverage) + return FALSE; + + if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + return FALSE; + + MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get()); + + mono_os_mutex_lock(&domain->mutex); + + MonoProfilerCoverageInfo *info = g_hash_table_lookup (domain->coverage_hash, method); + + mono_os_mutex_unlock(&domain->mutex); + + if (!info) + return TRUE; + + for (guint32 i = 0; i < info->entries; i++) + info->data[i].count = 0; + + return TRUE; +} + +static void reset_coverage_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data) +{ + MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value; + + for (guint32 i = 0; i < info->entries; i++) + info->data[i].count = 0; +} + +void mono_profiler_reset_all_coverage() +{ + if (!mono_profiler_state.code_coverage) + return; + + MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get()); + + mono_os_mutex_lock(&domain->mutex); + + g_hash_table_foreach (domain->coverage_hash, reset_coverage_for_hashtable_entry, NULL); + + mono_os_mutex_unlock(&domain->mutex); +} + +#endif + MonoProfilerCoverageInfo * -mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries) +mono_profiler_coverage_alloc (MonoDomain *domain, MonoMethod *method, guint32 entries) { if (!mono_profiler_state.code_coverage) return FALSE; @@ -424,15 +630,17 @@ mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries) if (!cover) return NULL; - coverage_lock (); + MonoDomainCoverage* covdomain = get_coverage_for_domain(domain); + + mono_os_mutex_lock(&covdomain->mutex); MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries); info->entries = entries; - g_hash_table_insert (mono_profiler_state.coverage_hash, method, info); + g_hash_table_insert (covdomain->coverage_hash, method, info); - coverage_unlock (); + mono_os_mutex_unlock(&covdomain->mutex); return info; } @@ -553,12 +761,20 @@ mono_profiler_sampling_thread_wait (void) mono_bool mono_profiler_enable_allocations (void) { - if (mono_profiler_state.startup_done) +#ifndef RUNTIME_IL2CPP + if (mono_gc_get_managed_allocator_types () > 0 && mono_profiler_state.startup_done) return FALSE; +#endif return mono_profiler_state.allocations = TRUE; } +mono_bool +mono_profiler_enable_fileio (void) +{ + return mono_profiler_state.fileio = TRUE; +} + /** * mono_profiler_set_call_instrumentation_filter_callback: * @@ -823,21 +1039,6 @@ mono_profiler_cleanup (void) g_free (cur); } - if (mono_profiler_state.code_coverage) { - mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex); - - GHashTableIter iter; - - g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash); - - MonoProfilerCoverageInfo *info; - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) - g_free (info); - - g_hash_table_destroy (mono_profiler_state.coverage_hash); - } - if (mono_profiler_state.sampling_owner) mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore); } @@ -944,6 +1145,7 @@ typedef void (*MonoLegacyProfileAllocFunc) (MonoLegacyProfiler *prof, MonoObject typedef void (*MonoLegacyProfileMethodFunc) (MonoLegacyProfiler *prof, MonoMethod *method); typedef void (*MonoLegacyProfileExceptionFunc) (MonoLegacyProfiler *prof, MonoObject *object); typedef void (*MonoLegacyProfileExceptionClauseFunc) (MonoLegacyProfiler *prof, MonoMethod *method, int clause_type, int clause_num); +typedef void (*MonoLegacyProfileFileIOFunc) (MonoLegacyProfiler *prof, int kind, int size); struct _MonoProfiler { MonoProfilerHandle handle; @@ -954,6 +1156,7 @@ struct _MonoProfiler { MonoLegacyProfileGCResizeFunc gc_heap_resize; MonoLegacyProfileJitResult jit_end2; MonoLegacyProfileAllocFunc allocation; + MonoLegacyProfileFileIOFunc fileio; MonoLegacyProfileMethodFunc enter; MonoLegacyProfileMethodFunc leave; MonoLegacyProfileExceptionFunc throw_callback; @@ -970,6 +1173,7 @@ MONO_API void mono_profiler_install_jit_end (MonoLegacyProfileJitResult end); MONO_API void mono_profiler_set_events (int flags); MONO_API void mono_profiler_install_allocation (MonoLegacyProfileAllocFunc callback); MONO_API void mono_profiler_install_enter_leave (MonoLegacyProfileMethodFunc enter, MonoLegacyProfileMethodFunc fleave); +MONO_API void mono_profiler_install_fileio (MonoLegacyProfileFileIOFunc callback); MONO_API void mono_profiler_install_exception (MonoLegacyProfileExceptionFunc throw_callback, MonoLegacyProfileMethodFunc exc_method_leave, MonoLegacyProfileExceptionClauseFunc clause_callback); static void @@ -1063,10 +1267,49 @@ mono_profiler_install_jit_end (MonoLegacyProfileJitResult end) } } + +static MonoProfilerCallInstrumentationFlags +call_instrumentation_filter_callback (MonoProfiler *prof, MonoMethod *method) +{ + return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; +} + +typedef enum +{ + MONO_PROFILE_NONE = 0, + MONO_PROFILE_APPDOMAIN_EVENTS = 1 << 0, + MONO_PROFILE_ASSEMBLY_EVENTS = 1 << 1, + MONO_PROFILE_MODULE_EVENTS = 1 << 2, + MONO_PROFILE_CLASS_EVENTS = 1 << 3, + MONO_PROFILE_JIT_COMPILATION = 1 << 4, + MONO_PROFILE_INLINING = 1 << 5, + MONO_PROFILE_EXCEPTIONS = 1 << 6, + MONO_PROFILE_ALLOCATIONS = 1 << 7, + MONO_PROFILE_GC = 1 << 8, + MONO_PROFILE_THREADS = 1 << 9, + MONO_PROFILE_REMOTING = 1 << 10, + MONO_PROFILE_TRANSITIONS = 1 << 11, + MONO_PROFILE_ENTER_LEAVE = 1 << 12, + MONO_PROFILE_COVERAGE = 1 << 13, + MONO_PROFILE_INS_COVERAGE = 1 << 14, + MONO_PROFILE_STATISTICAL = 1 << 15, + MONO_PROFILE_FILEIO = 1 << 16 +} LegacyMonoProfileFlags; + void mono_profiler_set_events (int flags) { + if (flags & MONO_PROFILE_ENTER_LEAVE) + mono_profiler_set_call_instrumentation_filter_callback (current->handle, call_instrumentation_filter_callback); + else + mono_profiler_set_call_instrumentation_filter_callback (current->handle, NULL); /* Do nothing. */ + + if (flags & MONO_PROFILE_ALLOCATIONS) + mono_profiler_enable_allocations (); + + if (flags & MONO_PROFILE_FILEIO) + mono_profiler_enable_fileio (); } static void @@ -1084,6 +1327,21 @@ mono_profiler_install_allocation (MonoLegacyProfileAllocFunc callback) mono_profiler_set_gc_allocation_callback (current->handle, allocation_cb); } +static void +fileio_cb (MonoProfiler *prof, uint64_t kind, uint64_t size) +{ + prof->fileio (prof->profiler, kind, size); +} + +void +mono_profiler_install_fileio (MonoLegacyProfileFileIOFunc callback) +{ + current->fileio = callback; + + if (callback) + mono_profiler_set_fileio_callback (current->handle, fileio_cb); +} + static void enter_cb (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *context) { diff --git a/mono/metadata/profiler.h b/mono/metadata/profiler.h index 11ddd4b71d48..f49115001af4 100644 --- a/mono/metadata/profiler.h +++ b/mono/metadata/profiler.h @@ -38,12 +38,20 @@ typedef struct { uint32_t column; } MonoProfilerCoverageData; +typedef struct _MonoDomainCoverage MonoDomainCoverage; + typedef mono_bool (*MonoProfilerCoverageFilterCallback) (MonoProfiler *prof, MonoMethod *method); typedef void (*MonoProfilerCoverageCallback) (MonoProfiler *prof, const MonoProfilerCoverageData *data); MONO_API mono_bool mono_profiler_enable_coverage (void); MONO_API void mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb); +#ifndef RUNTIME_IL2CPP MONO_API mono_bool mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb); +MONO_API mono_bool mono_profiler_get_all_coverage_data (MonoProfilerHandle handle, MonoProfilerCoverageCallback cb); + +MONO_API mono_bool mono_profiler_reset_coverage (MonoMethod* method); +MONO_API void mono_profiler_reset_all_coverage (void); +#endif typedef enum { /** @@ -68,6 +76,7 @@ MONO_API mono_bool mono_profiler_set_sample_mode (MonoProfilerHandle handle, Mon MONO_API mono_bool mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq); MONO_API mono_bool mono_profiler_enable_allocations (void); +MONO_API mono_bool mono_profiler_enable_fileio (void); typedef struct _MonoProfilerCallContext MonoProfilerCallContext; diff --git a/mono/metadata/reflection.c b/mono/metadata/reflection.c index 6ee435419744..e5c0ec8a3601 100644 --- a/mono/metadata/reflection.c +++ b/mono/metadata/reflection.c @@ -534,7 +534,10 @@ mono_type_get_object_checked (MonoDomain *domain, MonoType *type, MonoError *err mono_g_hash_table_insert (domain->type_hash, type, res); if (type->type == MONO_TYPE_VOID) + { domain->typeof_void = (MonoObject*)res; + mono_gc_wbarrier_generic_nostore (&domain->typeof_void); + } mono_domain_unlock (domain); mono_loader_unlock (); @@ -1084,6 +1087,16 @@ mono_param_get_objects_internal (MonoDomain *domain, MonoMethod *method, MonoCla return res; } + /* Wrapper methods are exposed in stack traces. + * The logic in param_objects_construct cannot handle some wrappers. + * Get the underlying method if it's available. + */ + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) { + MonoMethod* wrapped_method = mono_marshal_method_from_wrapper (method); + if (wrapped_method) + method = wrapped_method; + } + /* Note: the cache is based on the address of the signature into the method * since we already cache MethodInfos with the method as keys. */ diff --git a/mono/metadata/reflection.h b/mono/metadata/reflection.h index bcb4574cac56..70c039a2bd6e 100644 --- a/mono/metadata/reflection.h +++ b/mono/metadata/reflection.h @@ -90,6 +90,7 @@ MONO_RT_EXTERNAL_ONLY MONO_API MonoCustomAttrInfo* mono_reflection_get_custom_attrs_info (MonoObject *obj); MONO_RT_EXTERNAL_ONLY MONO_API MonoArray* mono_custom_attrs_construct (MonoCustomAttrInfo *cinfo); +MONO_API MonoArray* mono_custom_attrs_construct_by_type (MonoCustomAttrInfo *cinfo, MonoClass *attr_klass, MonoError *error); MONO_RT_EXTERNAL_ONLY MONO_API MonoCustomAttrInfo* mono_custom_attrs_from_index (MonoImage *image, uint32_t idx); MONO_RT_EXTERNAL_ONLY diff --git a/mono/metadata/security-manager.h b/mono/metadata/security-manager.h index d9f49ceb510c..d92e169d2164 100644 --- a/mono/metadata/security-manager.h +++ b/mono/metadata/security-manager.h @@ -57,7 +57,7 @@ MonoMethod* mono_get_context_capture_method (void); MonoSecurityManager* mono_security_manager_get_methods (void); /* Security mode */ -void mono_security_set_mode (MonoSecurityMode mode); +UNITY_MONO_API void mono_security_set_mode (MonoSecurityMode mode); MonoSecurityMode mono_security_get_mode (void); /* internal calls */ diff --git a/mono/metadata/sgen-mono.c b/mono/metadata/sgen-mono.c index 800b8c87989f..2a2f624b1837 100644 --- a/mono/metadata/sgen-mono.c +++ b/mono/metadata/sgen-mono.c @@ -2439,6 +2439,11 @@ sgen_client_thread_attach (SgenThreadInfo* info) info->client_info.info.handle_stack = mono_handle_stack_alloc (); } +void +mono_gc_thread_detach (SgenThreadInfo *info) +{ +} + void mono_gc_thread_detach_with_lock (SgenThreadInfo *info) { @@ -2777,6 +2782,12 @@ mono_gc_is_moving (void) return TRUE; } +gboolean +mono_gc_needs_write_barriers(void) +{ + return TRUE; +} + gboolean mono_gc_is_disabled (void) { @@ -2808,6 +2819,12 @@ mono_gc_collect (int generation) sgen_gc_collect (generation); } +void +mono_gc_start_incremental_collection() +{ + +} + int mono_gc_collection_count (int generation) { @@ -2826,6 +2843,28 @@ mono_gc_get_heap_size (void) return (int64_t)sgen_gc_get_total_heap_allocation (); } +int64_t +mono_gc_get_max_time_slice_ns() +{ + return 0; +} + +MonoBoolean +mono_gc_is_incremental() +{ + return FALSE; +} + +void +mono_gc_set_incremental(MonoBoolean value) +{ +} + +void +mono_gc_set_max_time_slice_ns(int64_t maxTimeSlice) +{ +} + MonoGCDescriptor mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker) { diff --git a/mono/metadata/threadpool-io.c b/mono/metadata/threadpool-io.c index b3153db22345..4f338bdde12f 100644 --- a/mono/metadata/threadpool-io.c +++ b/mono/metadata/threadpool-io.c @@ -172,6 +172,7 @@ selector_thread_wakeup_drain_pipes (void) { gchar buffer [128]; gint received; + static gint warnings_issued = 0; for (;;) { #if !defined(HOST_WIN32) @@ -179,8 +180,13 @@ selector_thread_wakeup_drain_pipes (void) if (received == 0) break; if (received == -1) { - if (errno != EINTR && errno != EAGAIN) - g_warning ("selector_thread_wakeup_drain_pipes: read () failed, error (%d) %s\n", errno, g_strerror (errno)); + if (errno != EINTR && errno != EAGAIN) { + // limit amount of spam we write + if (warnings_issued < 100) { + g_warning ("selector_thread_wakeup_drain_pipes: read () failed, error (%d) %s\n", errno, g_strerror (errno)); + warnings_issued++; + } + } break; } #else @@ -188,8 +194,13 @@ selector_thread_wakeup_drain_pipes (void) if (received == 0) break; if (received == SOCKET_ERROR) { - if (WSAGetLastError () != WSAEINTR && WSAGetLastError () != WSAEWOULDBLOCK) - g_warning ("selector_thread_wakeup_drain_pipes: recv () failed, error (%d) %s\n", WSAGetLastError ()); + if (WSAGetLastError () != WSAEINTR && WSAGetLastError () != WSAEWOULDBLOCK) { + // limit amount of spam we write + if (warnings_issued < 100) { + g_warning ("selector_thread_wakeup_drain_pipes: recv () failed, error (%d)\n", WSAGetLastError ()); + warnings_issued++; + } + } break; } #endif @@ -714,4 +725,4 @@ mono_threadpool_io_remove_domain_jobs (MonoDomain *domain) g_assert_not_reached (); } -#endif +#endif \ No newline at end of file diff --git a/mono/metadata/threadpool.c b/mono/metadata/threadpool.c index 7bf9a28edaf7..ebc915b8497f 100644 --- a/mono/metadata/threadpool.c +++ b/mono/metadata/threadpool.c @@ -432,7 +432,11 @@ mono_threadpool_begin_invoke (MonoDomain *domain, MonoObject *target, MonoMethod error_init (error); - message = mono_method_call_message_new (method, params, mono_get_delegate_invoke (method->klass), (params != NULL) ? (&async_callback) : NULL, (params != NULL) ? (&state) : NULL, error); + MonoMethod* invoke = NULL; + if (mono_class_is_delegate(method->klass->parent)) + invoke = mono_get_delegate_invoke (method->klass); + + message = mono_method_call_message_new (method, params, invoke, (params != NULL) ? (&async_callback) : NULL, (params != NULL) ? (&state) : NULL, error); return_val_if_nok (error, NULL); async_call = (MonoAsyncCall*) mono_object_new_checked (domain, async_call_klass, error); diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h index 2feb0ba4f848..2fcf55787083 100644 --- a/mono/metadata/threads-types.h +++ b/mono/metadata/threads-types.h @@ -209,7 +209,7 @@ gboolean mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadS void mono_thread_init_apartment_state (void); void mono_thread_cleanup_apartment_state (void); -void mono_threads_set_shutting_down (void); +UNITY_MONO_API void mono_threads_set_shutting_down (void); gunichar2* mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len); @@ -218,11 +218,11 @@ void mono_thread_self_abort (void); void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error); -void mono_thread_suspend_all_other_threads (void); +UNITY_MONO_API void mono_thread_suspend_all_other_threads (void); gboolean mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout); -void mono_thread_push_appdomain_ref (MonoDomain *domain); -void mono_thread_pop_appdomain_ref (void); +UNITY_MONO_API void mono_thread_push_appdomain_ref (MonoDomain *domain); +UNITY_MONO_API void mono_thread_pop_appdomain_ref (void); gboolean mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain); MonoException* mono_thread_request_interruption (mono_bool running_managed); @@ -267,4 +267,10 @@ mono_thread_internal_describe (MonoInternalThread *internal, GString *str); gboolean mono_thread_internal_is_current (MonoInternalThread *internal); +MonoObjectHandle +ves_icall_System_Threading_OSSpecificSynchronizationContext_GetOSContext (); + +void +ves_icall_System_Threading_OSSpecificSynchronizationContext_PostInternal (gpointer callback, gpointer arg); + #endif /* _MONO_METADATA_THREADS_TYPES_H_ */ diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 205e733936b9..ab0320546a52 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -55,6 +55,8 @@ #include #include +#include + #ifdef HAVE_SIGNAL_H #include #endif @@ -199,6 +201,7 @@ static gint32 thread_interruption_requested = 0; static MonoOSEvent background_change_event; static gboolean shutting_down = FALSE; +gboolean unity_shutting_down = FALSE; static gint32 managed_thread_id_counter = 0; @@ -520,6 +523,7 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M g_assert (!*current_thread_ptr); *current_thread_ptr = current; + mono_gc_wbarrier_generic_nostore (current_thread_ptr); } static MonoThread* @@ -558,6 +562,10 @@ create_internal_thread_object (void) thread->apartment_state = ThreadApartmentState_Unknown; thread->managed_id = get_next_managed_thread_id (); + /* + * Boehm incremental is not actually "moving", it does not need the thread_pinning_ref. + * But having it causes problems when unregistering the root after domain reload. + */ if (mono_gc_is_moving ()) { thread->thread_pinning_ref = thread; MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Pinning Reference"); @@ -879,7 +887,11 @@ mono_thread_detach_internal (MonoInternalThread *thread) mono_thread_cleanup_fn (thread_get_tid (thread)); mono_memory_barrier (); - + + /* + * Boehm incremental is not actually "moving", it does not need the thread_pinning_ref. + * But having it causes problems when unregistering the root after domain reload. + */ if (mono_gc_is_moving ()) { MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref); thread->thread_pinning_ref = NULL; @@ -1398,6 +1410,7 @@ ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj) internal->state = ThreadState_Unstarted; mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL); + mono_gc_wbarrier_generic_nostore (&this_obj->internal_thread); } MonoThread * @@ -1778,6 +1791,7 @@ mono_thread_current (void) if (!*current_thread_ptr) { g_assert (domain != mono_get_root_domain ()); *current_thread_ptr = create_thread_object (domain, internal); + mono_gc_wbarrier_generic_nostore (current_thread_ptr); } return *current_thread_ptr; } @@ -1795,6 +1809,7 @@ mono_thread_current_for_thread (MonoInternalThread *internal) if (!*current_thread_ptr) { g_assert (domain != mono_get_root_domain ()); *current_thread_ptr = create_thread_object (domain, internal); + mono_gc_wbarrier_generic_nostore (current_thread_ptr); } return *current_thread_ptr; } @@ -1983,6 +1998,13 @@ ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 n return map_native_wait_result_to_managed (ret, numhandles); } +/* + * UWP doesn't support SignalObjectAndWait until SDK version 15063. Our minspec currently is 10240. + * Since we don't care about running Mono runtime for now, let's just disable this icall and reevaluate + * in some months when we have to get libmono with IL2CPP up & running + */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + gint32 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error) { @@ -2010,6 +2032,8 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, return map_native_wait_result_to_managed (ret, 1); } +#endif + gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location) { return mono_atomic_inc_i32 (location); @@ -3034,14 +3058,14 @@ thread_detach (MonoThreadInfo *info) g_assert (info); + mono_gc_thread_detach (info); + if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle)) return; internal = (MonoInternalThread*) mono_gchandle_get_target (gchandle); g_assert (internal); - mono_gchandle_free (gchandle); - mono_thread_detach_internal (internal); } @@ -3208,7 +3232,7 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user) /* Ignore background threads, we abort them later */ /* Do not lock here since it is not needed and the caller holds threads_lock */ - if (thread->state & ThreadState_Background) { + if (thread->state & ThreadState_Background || unity_shutting_down) { THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); return; /* just leave, ignore */ } @@ -3579,6 +3603,97 @@ get_thread_dump (MonoThreadInfo *info, gpointer ud) return MonoResumeThread; } +typedef struct QuickDumpUserData { + char *bufp; + char *maxp; + const MonoUnityCallstackOptions *opts; + size_t prefix_len; +} QuickDumpUserData; + +static void +append_quick (QuickDumpUserData *ud, const char *str) +{ + int len = (int)strlen (str); + int remain = (int)(ud->maxp - ud->bufp); + int copylen = len; + if (copylen > remain) + copylen = remain; + memcpy (ud->bufp, str, copylen); + ud->bufp += copylen; +} + +static mono_bool +collect_frame_text (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void *data) +{ + QuickDumpUserData *ud = (QuickDumpUserData *)data; + + if (managed && method) { + char *method_name = mono_method_full_name (method, TRUE); + append_quick (ud, method_name); + g_free (method_name); + + gboolean skip_lines = FALSE; + + for (int fi = 0, fcount = ud->opts->filter_count; !skip_lines && fi < fcount; ++fi) { + const MonoUnityCallstackFilter *f = &ud->opts->line_filters[fi]; + + if (0 == strcmp (method->klass->name, f->class_name)) { + if (0 == strcmp (method->klass->name_space, f->name_space)) { + skip_lines = !f->method_name || 0 == strcmp (method->name, f->method_name); + } + } + } + + if (!skip_lines) { + MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); + if (minfo) { + + MonoDebugSourceLocation *src_loc = mono_debug_method_lookup_location (minfo, il_offset); + + if (src_loc && src_loc->source_file) { + char buf[512]; + strncpy (buf, src_loc->source_file, sizeof buf); + buf[(sizeof buf) - 1] = '\0'; + + for (char *p = buf; *p; ++p) { + if (*p == '\\') + *p = '/'; + } + + const char *output_ptr = buf; + size_t slen = strlen (buf); + if (slen > ud->prefix_len) { + if (0 == memcmp (buf, ud->opts->path_prefix_filter, ud->prefix_len)) + output_ptr += ud->prefix_len; + } + + append_quick (ud, " (at "); + append_quick (ud, output_ptr); + append_quick (ud, ":"); + char num_buf[32]; + snprintf (num_buf, sizeof num_buf, "%d", src_loc->row); + append_quick (ud, num_buf); + append_quick (ud, ")"); + } + mono_debug_free_source_location (src_loc); + } + } + append_quick (ud, "\n"); + } + + return ud->bufp == ud->maxp; +} + +MONO_API int +mono_unity_managed_callstack (unsigned char *buffer, int bufferSize, const MonoUnityCallstackOptions *opts) +{ + QuickDumpUserData ud = {(char *)buffer, (char *)buffer + bufferSize - 1, opts, strlen (opts->path_prefix_filter)}; + + mono_stack_walk (collect_frame_text, &ud); + + return (int)(ud.bufp - (char *)buffer); +} + typedef struct { int nthreads, max_threads; MonoInternalThread **threads; @@ -4127,10 +4242,13 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_ if (mono_gc_user_markers_supported ()) static_data [i] = g_malloc0 (static_data_size [i]); else + { static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL, threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC, alloc_key, threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields"); + mono_gc_wbarrier_generic_nostore (static_data + i); + } } } @@ -4209,6 +4327,7 @@ context_adjust_static_data (MonoAppContext *ctx) if (context_static_info.offset || context_static_info.idx > 0) { guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0); mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE); + mono_gc_wbarrier_generic_nostore (&ctx->static_data); ctx->data->static_data = ctx->static_data; } } @@ -4238,6 +4357,7 @@ alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user) guint32 offset = GPOINTER_TO_UINT (user); mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE); + mono_gc_wbarrier_generic_nostore (&ctx->static_data); ctx->data->static_data = ctx->static_data; } @@ -4848,7 +4968,7 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud) gboolean protected_wrapper; gboolean running_managed; - if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info))) + if (mono_unity_get_enable_handler_block_guards () && mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info))) return MonoResumeThread; /*someone is already interrupting it*/ @@ -5369,3 +5489,24 @@ mono_thread_internal_is_current (MonoInternalThread *internal) g_assert (internal); return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid)); } + +MonoException* mono_unity_thread_check_exception() +{ + MonoInternalThread *thread = mono_thread_internal_current(); + MonoThread *sys_thread = mono_thread_current(); + + lock_thread(thread); + + if (sys_thread->pending_exception) { + MonoException *exc; + + exc = sys_thread->pending_exception; + sys_thread->pending_exception = NULL; + + unlock_thread(thread); + return exc; + } + + unlock_thread(thread); + return NULL; +} diff --git a/mono/metadata/unity-icall.c b/mono/metadata/unity-icall.c new file mode 100644 index 000000000000..03ec5b6dc3aa --- /dev/null +++ b/mono/metadata/unity-icall.c @@ -0,0 +1,225 @@ +/** + * \file + * Unity icall support for Mono. + * + * Copyright 2016 Unity + * Licensed under the MIT license. See LICENSE file in the project root for full license information. +*/ +#include +#include +#include "mono/utils/mono-compiler.h" +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +/* + * Magic number to convert a time which is relative to + * Jan 1, 1970 into a value which is relative to Jan 1, 0001. + */ +#define EPOCH_ADJUST ((guint64)62135596800LL) + +/* + * Magic number to convert FILETIME base Jan 1, 1601 to DateTime - base Jan, 1, 0001 + */ +#define FILETIME_ADJUST ((guint64)504911232000000000LL) + +#ifdef PLATFORM_WIN32 +/* convert a SYSTEMTIME which is of the form "last thursday in october" to a real date */ +static void +convert_to_absolute_date(SYSTEMTIME *date) +{ +#define IS_LEAP(y) ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) + static int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + static int leap_days_in_month[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + /* from the calendar FAQ */ + int a = (14 - date->wMonth) / 12; + int y = date->wYear - a; + int m = date->wMonth + 12 * a - 2; + int d = (1 + y + y/4 - y/100 + y/400 + (31*m)/12) % 7; + + /* d is now the day of the week for the first of the month (0 == Sunday) */ + + int day_of_week = date->wDayOfWeek; + + /* set day_in_month to the first day in the month which falls on day_of_week */ + int day_in_month = 1 + (day_of_week - d); + if (day_in_month <= 0) + day_in_month += 7; + + /* wDay is 1 for first weekday in month, 2 for 2nd ... 5 means last - so work that out allowing for days in the month */ + date->wDay = day_in_month + (date->wDay - 1) * 7; + if (date->wDay > (IS_LEAP(date->wYear) ? leap_days_in_month[date->wMonth - 1] : days_in_month[date->wMonth - 1])) + date->wDay -= 7; +} +#endif + +#ifndef PLATFORM_WIN32 +/* + * Return's the offset from GMT of a local time. + * + * tm is a local time + * t is the same local time as seconds. + */ +static int +gmt_offset(struct tm *tm, time_t t) +{ +#if defined (HAVE_TM_GMTOFF) + return tm->tm_gmtoff; +#else + struct tm g; + time_t t2; + g = *gmtime(&t); + g.tm_isdst = tm->tm_isdst; + t2 = mktime(&g); + return (int)difftime(t, t2); +#endif +} +#endif +/* + * This is heavily based on zdump.c from glibc 2.2. + * + * * data[0]: start of daylight saving time (in DateTime ticks). + * * data[1]: end of daylight saving time (in DateTime ticks). + * * data[2]: utcoffset (in TimeSpan ticks). + * * data[3]: additional offset when daylight saving (in TimeSpan ticks). + * * name[0]: name of this timezone when not daylight saving. + * * name[1]: name of this timezone when daylight saving. + * + * FIXME: This only works with "standard" Unix dates (years between 1900 and 2100) while + * the class library allows years between 1 and 9999. + * + * Returns true on success and zero on failure. + */ +guint32 +ves_icall_System_CurrentSystemTimeZone_GetTimeZoneData (guint32 year, MonoArray **data, MonoArray **names, MonoBoolean *daylight_inverted) +{ + MonoError error; +#ifndef PLATFORM_WIN32 + MonoDomain *domain = mono_domain_get (); + struct tm start, tt; + time_t t; + + long int gmtoff_start; + long int gmtoff; + int is_transitioned = 0, day; + char tzone [64]; + + MONO_CHECK_ARG_NULL (data, FALSE); + MONO_CHECK_ARG_NULL (names, FALSE); + + mono_gc_wbarrier_generic_store (data, (MonoObject*) mono_array_new_checked (domain, mono_defaults.int64_class, 4, &error)); + mono_gc_wbarrier_generic_store (names, (MonoObject*) mono_array_new_checked (domain, mono_defaults.string_class, 2, &error)); + + /* + * no info is better than crashing: we'll need our own tz data + * to make this work properly, anyway. The range is probably + * reduced to 1970 .. 2037 because that is what mktime is + * guaranteed to support (we get into an infinite loop + * otherwise). + */ + + memset (&start, 0, sizeof (start)); + + start.tm_mday = 1; + start.tm_year = year-1900; + + t = mktime (&start); + + if ((year < 1970) || (year > 2037) || (t == -1)) { + t = time (NULL); + tt = *localtime (&t); + strftime (tzone, sizeof (tzone), "%Z", &tt); + mono_array_setref ((*names), 0, mono_string_new_checked (domain, tzone, &error)); + mono_array_setref ((*names), 1, mono_string_new_checked (domain, tzone, &error)); + *daylight_inverted = 0; + return 1; + } + + *daylight_inverted = start.tm_isdst; + + gmtoff = gmt_offset (&start, t); + gmtoff_start = gmtoff; + + /* For each day of the year, calculate the tm_gmtoff. */ + for (day = 0; day < 365; day++) { + + t += 3600*24; + tt = *localtime (&t); + + /* Daylight saving starts or ends here. */ + if (gmt_offset (&tt, t) != gmtoff) { + struct tm tt1; + time_t t1; + + /* Try to find the exact hour when daylight saving starts/ends. */ + t1 = t; + do { + t1 -= 3600; + tt1 = *localtime (&t1); + } while (gmt_offset (&tt1, t1) != gmtoff); + + /* Try to find the exact minute when daylight saving starts/ends. */ + do { + t1 += 60; + tt1 = *localtime (&t1); + } while (gmt_offset (&tt1, t1) == gmtoff); + t1+=gmtoff; + strftime (tzone, sizeof (tzone), "%Z", &tt); + + /* Write data, if we're already in daylight saving, we're done. */ + if (is_transitioned) { + if (!start.tm_isdst) + mono_array_setref ((*names), 0, mono_string_new_checked (domain, tzone, &error)); + else + mono_array_setref ((*names), 1, mono_string_new_checked (domain, tzone, &error)); + + mono_array_set ((*data), gint64, 1, ((gint64)t1 + EPOCH_ADJUST) * 10000000L); + return 1; + } else { + if (!start.tm_isdst) + mono_array_setref ((*names), 1, mono_string_new_checked (domain, tzone, &error)); + else + mono_array_setref ((*names), 0, mono_string_new_checked (domain, tzone, &error)); + + mono_array_set ((*data), gint64, 0, ((gint64)t1 + EPOCH_ADJUST) * 10000000L); + is_transitioned = 1; + } + + /* This is only set once when we enter daylight saving. */ + if (*daylight_inverted == 0) { + mono_array_set ((*data), gint64, 2, (gint64)gmtoff * 10000000L); + mono_array_set ((*data), gint64, 3, (gint64)(gmt_offset (&tt, t) - gmtoff) * 10000000L); + } else { + mono_array_set ((*data), gint64, 2, (gint64)(gmtoff_start + (gmt_offset (&tt, t) - gmtoff)) * 10000000L); + mono_array_set ((*data), gint64, 3, (gint64)(gmtoff - gmt_offset (&tt, t)) * 10000000L); + } + + + gmtoff = gmt_offset (&tt, t); + } + } + + if (!is_transitioned) { + strftime (tzone, sizeof (tzone), "%Z", &tt); + mono_array_setref ((*names), 0, mono_string_new_checked (domain, tzone, &error)); + mono_array_setref ((*names), 1, mono_string_new_checked (domain, tzone, &error)); + mono_array_set ((*data), gint64, 0, 0); + mono_array_set ((*data), gint64, 1, 0); + mono_array_set ((*data), gint64, 2, (gint64) gmtoff * 10000000L); + mono_array_set ((*data), gint64, 3, 0); + *daylight_inverted = 0; + } + + return 1; +#else + //On Windows, we should always load timezones in managed + return 0; +#endif +} diff --git a/mono/metadata/unity-liveness.c b/mono/metadata/unity-liveness.c new file mode 100644 index 000000000000..7f6c3df8f670 --- /dev/null +++ b/mono/metadata/unity-liveness.c @@ -0,0 +1,674 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _LivenessState LivenessState; + +typedef struct _GPtrArray custom_growable_array; +#define array_at_index(array,index) (array)->pdata[(index)] + +#if defined(HAVE_SGEN_GC) +void sgen_stop_world (int generation); +void sgen_restart_world (int generation); +#elif defined(HAVE_BOEHM_GC) +#ifdef HAVE_BDWGC_GC +extern void GC_stop_world_external(); +extern void GC_start_world_external(); +#else +void GC_stop_world_external() +{ + g_assert_not_reached (); +} +void GC_start_world_external() +{ + g_assert_not_reached (); +} +#endif +#else +#error need to implement liveness GC API +#endif + +custom_growable_array* array_create_and_initialize (guint capacity) +{ + custom_growable_array* array = g_ptr_array_sized_new(capacity); + array->len = 0; + return array; +} + +gboolean array_is_full(custom_growable_array* array) +{ + return g_ptr_array_capacity(array) == array->len; +} + +void array_destroy (custom_growable_array* array) +{ + g_ptr_array_free(array, TRUE); +} + +void array_push_back(custom_growable_array* array, gpointer value) +{ + g_assert(!array_is_full(array)); + array->pdata[array->len] = value; + array->len++; +} + +gpointer array_pop_back(custom_growable_array* array) +{ + array->len--; + return array->pdata[array->len]; +} + +void array_clear(custom_growable_array* array) +{ + array->len = 0; +} + +void array_grow(custom_growable_array* array) +{ + int oldlen = array->len; + g_ptr_array_set_size(array, g_ptr_array_capacity(array)*2); + array->len = oldlen; +} + +typedef void (*register_object_callback)(gpointer* arr, int size, void* callback_userdata); +typedef void (*WorldStateChanged)(); +struct _LivenessState +{ + gint first_index_in_all_objects; + custom_growable_array* all_objects; + + MonoClass* filter; + + custom_growable_array* process_array; + guint initial_alloc_count; + + void* callback_userdata; + + register_object_callback filter_callback; + WorldStateChanged onWorldStartCallback; + WorldStateChanged onWorldStopCallback; + guint traverse_depth; // track recursion. Prevent stack overflow by limiting recurion +}; + +/* number of sub elements of an array to process before recursing + * we take a depth first approach to use stack space rather than re-allocating + * processing array which requires restarting world to ensure allocator lock is not held +*/ +const int kArrayElementsPerChunk = 256; + +/* how far we recurse processing array elements before we stop. Prevents stack overflow */ +const int kMaxTraverseRecursionDepth = 128; + +/* Liveness calculation */ +MONO_API LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback); +MONO_API void mono_unity_liveness_stop_gc_world (LivenessState* state); +MONO_API void mono_unity_liveness_finalize (LivenessState* state); +MONO_API void mono_unity_liveness_start_gc_world (LivenessState* state); +MONO_API void mono_unity_liveness_free_struct (LivenessState* state); + +MONO_API LivenessState* mono_unity_liveness_calculation_begin (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onStartWorldCallback, WorldStateChanged onStopWorldCallback); +MONO_API void mono_unity_liveness_calculation_end (LivenessState* state); + +MONO_API void mono_unity_liveness_calculation_from_root (MonoObject* root, LivenessState* state); +MONO_API void mono_unity_liveness_calculation_from_statics (LivenessState* state); + +#define MARK_OBJ(obj) \ + do { \ + (obj)->vtable = (MonoVTable*)(((gsize)(obj)->vtable) | (gsize)1); \ + } while (0) + +#define CLEAR_OBJ(obj) \ + do { \ + (obj)->vtable = (MonoVTable*)(((gsize)(obj)->vtable) & ~(gsize)1); \ + } while (0) + +#define IS_MARKED(obj) \ + (((gsize)(obj)->vtable) & (gsize)1) + +#define GET_VTABLE(obj) \ + ((MonoVTable*)(((gsize)(obj)->vtable) & ~(gsize)1)) + + +void mono_filter_objects(LivenessState* state); + +void mono_reset_state(LivenessState* state) +{ + state->first_index_in_all_objects = state->all_objects->len; + array_clear(state->process_array); +} + +void array_safe_grow(LivenessState* state, custom_growable_array* array) +{ + // if all_objects run out of space, run through list + // clear bit in vtable, start the world, reallocate, stop the world and continue + int i; + for (i = 0; i < state->all_objects->len; i++) + { + MonoObject* object = array_at_index(state->all_objects,i); + CLEAR_OBJ(object); + } + mono_unity_liveness_start_gc_world(state); + array_grow(array); + mono_unity_liveness_stop_gc_world (state); + for (i = 0; i < state->all_objects->len; i++) + { + MonoObject* object = array_at_index(state->all_objects,i); + MARK_OBJ(object); + } +} + +static gboolean should_process_value (MonoObject* val, MonoClass* filter) +{ + MonoClass* val_class = GET_VTABLE(val)->klass; + if (filter && + !mono_class_has_parent (val_class, filter)) + return FALSE; + + return TRUE; +} + +static void mono_traverse_array (MonoArray* array, LivenessState* state); +static void mono_traverse_object (MonoObject* object, LivenessState* state); +static void mono_traverse_gc_desc (MonoObject* object, LivenessState* state); +static void mono_traverse_objects (LivenessState* state); + +static void mono_traverse_generic_object( MonoObject* object, LivenessState* state ) +{ +#ifdef HAVE_SGEN_GC + gsize gc_desc = 0; +#else + gsize gc_desc = (gsize)(GET_VTABLE(object)->gc_descr); +#endif + + if (gc_desc & (gsize)1) + mono_traverse_gc_desc (object, state); + else if (GET_VTABLE(object)->klass->rank) + mono_traverse_array ((MonoArray*)object, state); + else + mono_traverse_object (object, state); +} + + +static gboolean mono_add_process_object (MonoObject* object, LivenessState* state) +{ + if (object && !IS_MARKED(object)) + { + gboolean has_references = GET_VTABLE(object)->klass->has_references; + if(has_references || should_process_value(object,state->filter)) + { + if (array_is_full(state->all_objects)) + array_safe_grow(state, state->all_objects); + array_push_back(state->all_objects, object); + MARK_OBJ(object); + } + // Check if klass has further references - if not skip adding + if (has_references) + { + if(array_is_full(state->process_array)) + array_safe_grow(state, state->process_array); + array_push_back(state->process_array, object); + return TRUE; + } + } + + return FALSE; +} + +static gboolean mono_field_can_contain_references(MonoClassField* field) +{ + if (MONO_TYPE_ISSTRUCT(field->type)) + return TRUE; + if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) + return FALSE; + if (field->type->type == MONO_TYPE_STRING) + return FALSE; + return MONO_TYPE_IS_REFERENCE(field->type); +} + +static gboolean mono_traverse_object_internal (MonoObject* object, gboolean isStruct, MonoClass* klass, LivenessState* state) +{ + int i; + MonoClassField *field; + MonoClass *p; + gboolean added_objects = FALSE; + + g_assert (object); + + // subtract the added offset for the vtable. This is added to the offset even though it is a struct + if(isStruct) + object--; + + for (p = klass; p != NULL; p = p->parent) + { + if (p->size_inited == 0) + continue; + for (i = 0; i < mono_class_get_field_count (p); i++) + { + field = &p->fields[i]; + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + + if(!mono_field_can_contain_references(field)) + continue; + + if (MONO_TYPE_ISSTRUCT(field->type)) + { + char* offseted = (char*)object; + offseted += field->offset; + if (field->type->type == MONO_TYPE_GENERICINST) + { + g_assert(field->type->data.generic_class->cached_class); + added_objects |= mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.generic_class->cached_class, state); + } + else + added_objects |= mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.klass, state); + continue; + } + + if (field->offset == -1) { + g_assert_not_reached (); + } else { + MonoObject* val = NULL; + MonoVTable *vtable = NULL; + mono_field_get_value (object, field, &val); + added_objects |= mono_add_process_object (val, state); + } + } + } + + return added_objects; +} + +static void mono_traverse_object (MonoObject* object, LivenessState* state) +{ + mono_traverse_object_internal (object, FALSE, GET_VTABLE(object)->klass, state); +} + +static void mono_traverse_gc_desc (MonoObject* object, LivenessState* state) +{ +#define WORDSIZE ((int)sizeof(gsize)*8) + int i = 0; + gsize mask = (gsize)(GET_VTABLE(object)->gc_descr); + + g_assert (mask & (gsize)1); + + for (i = 0; i < WORDSIZE-2; i++) + { + gsize offset = ((gsize)1 << (WORDSIZE - 1 - i)); + if (mask & offset) + { + MonoObject* val = *(MonoObject**)(((char*)object) + i * sizeof(void*)); + mono_add_process_object(val, state); + } + } +} + +static void mono_traverse_objects (LivenessState* state) +{ + int i = 0; + MonoObject* object = NULL; + + state->traverse_depth++; + while (state->process_array->len > 0) + { + object = array_pop_back(state->process_array); + mono_traverse_generic_object(object, state); + } + state->traverse_depth--; +} + +static gboolean should_traverse_objects (size_t index, gint32 recursion_depth) +{ + // Add kArrayElementsPerChunk objects at a time and then traverse + return ((index + 1) & (kArrayElementsPerChunk - 1)) == 0 && + recursion_depth < kMaxTraverseRecursionDepth; +} + +static void mono_traverse_array (MonoArray* array, LivenessState* state) +{ + size_t i = 0; + gboolean has_references; + MonoObject* object = (MonoObject*)array; + MonoClass* element_class; + size_t elementClassSize; + size_t array_length; + + g_assert (object); + + + + element_class = GET_VTABLE(object)->klass->element_class; + has_references = !mono_class_is_valuetype(element_class); + g_assert(element_class->size_inited != 0); + + for (i = 0; i < mono_class_get_field_count (element_class); i++) + { + has_references |= mono_field_can_contain_references(&element_class->fields[i]); + } + + if (!has_references) + return; + + array_length = mono_array_length (array); + if (element_class->valuetype) + { + size_t items_processed = 0; + elementClassSize = mono_class_array_element_size (element_class); + for (i = 0; i < array_length; i++) + { + MonoObject* object = (MonoObject*)mono_array_addr_with_size (array, elementClassSize, i); + if (mono_traverse_object_internal (object, 1, element_class, state)) + items_processed++; + + if(should_traverse_objects (items_processed, state->traverse_depth)) + mono_traverse_objects(state); + } + } + else + { + size_t items_processed = 0; + for (i = 0; i < array_length; i++) + { + MonoObject* val = mono_array_get(array, MonoObject*, i); + if (mono_add_process_object (val, state)) + items_processed++; + + if (should_traverse_objects (items_processed, state->traverse_depth)) + mono_traverse_objects(state); + } + } +} + + +void mono_filter_objects(LivenessState* state) +{ + gpointer filtered_objects[64]; + gint num_objects = 0; + + int i = state->first_index_in_all_objects; + for ( ; i < state->all_objects->len; i++) + { + MonoObject* object = state->all_objects->pdata[i]; + if (should_process_value (object, state->filter)) + filtered_objects[num_objects++] = object; + if (num_objects == 64) + { + state->filter_callback(filtered_objects, 64, state->callback_userdata); + num_objects = 0; + } + } + + if (num_objects != 0) + state->filter_callback(filtered_objects, num_objects, state->callback_userdata); +} + +/** + * mono_unity_liveness_calculation_from_statics: + * + * Returns an array of MonoObject* that are reachable from the static roots + * in the current domain and derive from @filter (if not NULL). + */ +void mono_unity_liveness_calculation_from_statics(LivenessState* liveness_state) +{ + int i, j; + MonoDomain* domain = mono_domain_get(); + + mono_reset_state(liveness_state); + + + for (i = 0; i < domain->class_vtable_array->len; ++i) + { + MonoVTable* vtable = (MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i); + MonoClass* klass = vtable->klass; + MonoClassField *field; + if (!klass) + continue; + if (!klass->has_static_refs) + continue; + if (klass->image == mono_defaults.corlib) + continue; + if (klass->size_inited == 0) + continue; + for (j = 0; j < mono_class_get_field_count (klass); j++) + { + field = &klass->fields[j]; + if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + continue; + if(!mono_field_can_contain_references(field)) + continue; + // shortcut check for special statics + if (field->offset == -1) + continue; + + if (MONO_TYPE_ISSTRUCT(field->type)) + { + char* offseted = (char*)mono_vtable_get_static_field_data (vtable); + offseted += field->offset; + if (field->type->type == MONO_TYPE_GENERICINST) + { + g_assert(field->type->data.generic_class->cached_class); + mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.generic_class->cached_class, liveness_state); + } + else + { + mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.klass, liveness_state); + } + } + else + { + MonoError error; + MonoObject* val = NULL; + + mono_field_static_get_value_checked (mono_class_vtable (domain, klass), field, &val, &error); + + if (val && mono_error_ok (&error)) + { + mono_add_process_object(val, liveness_state); + } + mono_error_cleanup (&error); + } + } + } + mono_traverse_objects (liveness_state); + //Filter objects and call callback to register found objects + mono_filter_objects(liveness_state); +} + +void mono_unity_liveness_add_object_callback(gpointer* objs, gint count, void* arr) +{ + int i; + GPtrArray* objects = (GPtrArray*)arr; + for (i = 0; i < count; i++) + { + if (g_ptr_array_capacity(objects) > objects->len) + objects->pdata[objects->len++] = objs[i]; + } +} + +/** + * mono_unity_liveness_calculation_from_statics_managed: + * + * Returns a gchandle to an array of MonoObject* that are reachable from the static roots + * in the current domain and derive from type retrieved from @filter_handle (if not NULL). + */ +gpointer mono_unity_liveness_calculation_from_statics_managed(gpointer filter_handle, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback) +{ + int i = 0; + MonoArray *res = NULL; + MonoReflectionType* filter_type = (MonoReflectionType*)mono_gchandle_get_target (GPOINTER_TO_UINT(filter_handle)); + MonoClass* filter = NULL; + GPtrArray* objects = NULL; + LivenessState* liveness_state = NULL; + MonoError* error = NULL; + + if (filter_type) + filter = mono_class_from_mono_type (filter_type->type); + + objects = g_ptr_array_sized_new(1000); + objects->len = 0; + + liveness_state = mono_unity_liveness_calculation_begin (filter, 1000, mono_unity_liveness_add_object_callback, (void*)objects, onWorldStartCallback, onWorldStopCallback); + + mono_unity_liveness_calculation_from_statics (liveness_state); + + mono_unity_liveness_calculation_end (liveness_state); + + res = mono_array_new_checked (mono_domain_get (), filter ? filter: mono_defaults.object_class, objects->len, error); + for (i = 0; i < objects->len; ++i) { + MonoObject* o = g_ptr_array_index (objects, i); + mono_array_setref (res, i, o); + } + g_ptr_array_free (objects, TRUE); + + + return (gpointer)mono_gchandle_new ((MonoObject*)res, FALSE); + +} + +/** + * mono_unity_liveness_calculation_from_root: + * + * Returns an array of MonoObject* that are reachable from @root + * in the current domain and derive from @filter (if not NULL). + */ +void mono_unity_liveness_calculation_from_root (MonoObject* root, LivenessState* liveness_state) +{ + mono_reset_state (liveness_state); + + array_push_back (liveness_state->process_array,root); + + mono_traverse_objects (liveness_state); + + //Filter objects and call callback to register found objects + mono_filter_objects (liveness_state); +} + +/** + * mono_unity_liveness_calculation_from_root_managed: + * + * Returns a gchandle to an array of MonoObject* that are reachable from the static roots + * in the current domain and derive from type retrieved from @filter_handle (if not NULL). + */ +gpointer mono_unity_liveness_calculation_from_root_managed(gpointer root_handle, gpointer filter_handle, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback) +{ + int i = 0; + MonoArray *res = NULL; + MonoReflectionType* filter_type = (MonoReflectionType*)mono_gchandle_get_target (GPOINTER_TO_UINT(filter_handle)); + MonoObject* root = mono_gchandle_get_target (GPOINTER_TO_UINT(root_handle)); + MonoClass* filter = NULL; + GPtrArray* objects = NULL; + LivenessState* liveness_state = NULL; + MonoError* error = NULL; + + objects = g_ptr_array_sized_new(1000); + objects->len = 0; + + if (filter_type) + filter = mono_class_from_mono_type (filter_type->type); + + liveness_state = mono_unity_liveness_calculation_begin (filter, 1000, mono_unity_liveness_add_object_callback, (void*)objects, onWorldStartCallback, onWorldStopCallback); + + mono_unity_liveness_calculation_from_root (root, liveness_state); + + mono_unity_liveness_calculation_end (liveness_state); + + res = mono_array_new_checked (mono_domain_get (), filter ? filter: mono_defaults.object_class, objects->len, error); + for (i = 0; i < objects->len; ++i) { + MonoObject* o = g_ptr_array_index (objects, i); + mono_array_setref (res, i, o); + } + + g_ptr_array_free (objects, TRUE); + + return (gpointer)mono_gchandle_new ((MonoObject*)res, FALSE); +} + +LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback) +{ + LivenessState* state = NULL; + + // construct liveness_state; + // allocate memory for the following structs + // all_objects: contains a list of all referenced objects to be able to clean the vtable bits after the traversal + // process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage + // if all_objects run out of space, run through list, add objects that match the filter, clear bit in vtable and then clear the array. + + state = g_new0(LivenessState, 1); + max_count = max_count < 1000 ? 1000 : max_count; + state->all_objects = array_create_and_initialize(max_count*4); + state->process_array = array_create_and_initialize (max_count); + + state->first_index_in_all_objects = 0; + state->filter = filter; + state->traverse_depth = 0; + + state->callback_userdata = callback_userdata; + state->filter_callback = callback; + state->onWorldStartCallback = onWorldStartCallback; + state->onWorldStopCallback = onWorldStopCallback; + + return state; +} + +void mono_unity_liveness_finalize (LivenessState* state) +{ + int i; + for (i = 0; i < state->all_objects->len; i++) + { + MonoObject* object = g_ptr_array_index(state->all_objects,i); + CLEAR_OBJ(object); + } +} + +void mono_unity_liveness_free_struct (LivenessState* state) +{ + //cleanup the liveness_state + array_destroy(state->all_objects); + array_destroy(state->process_array); + g_free(state); +} + +void mono_unity_liveness_stop_gc_world (LivenessState* state) +{ + state->onWorldStopCallback(); +#if defined(HAVE_SGEN_GC) + sgen_stop_world (1); +#elif defined(HAVE_BOEHM_GC) + GC_stop_world_external (); +#else +#error need to implement liveness GC API +#endif +} + +void mono_unity_liveness_start_gc_world (LivenessState* state) +{ +#if defined(HAVE_SGEN_GC) + sgen_restart_world (1); +#elif defined(HAVE_BOEHM_GC) + GC_start_world_external (); +#else +#error need to implement liveness GC API +#endif + state->onWorldStartCallback(); +} + +LivenessState* mono_unity_liveness_calculation_begin (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback) +{ + LivenessState* state = mono_unity_liveness_allocate_struct (filter, max_count, callback, callback_userdata, onWorldStartCallback, onWorldStopCallback); + mono_unity_liveness_stop_gc_world (state); + // no allocations can happen beyond this point + return state; +} + +void mono_unity_liveness_calculation_end (LivenessState* state) +{ + mono_unity_liveness_finalize(state); + mono_unity_liveness_start_gc_world(state); + mono_unity_liveness_free_struct(state); +} diff --git a/mono/metadata/unity-memory-info.c b/mono/metadata/unity-memory-info.c new file mode 100644 index 000000000000..a7dab80c797b --- /dev/null +++ b/mono/metadata/unity-memory-info.c @@ -0,0 +1,807 @@ +#include +#include +#include "unity-memory-info.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_BDWGC_GC + +#include "external/bdwgc/include/gc.h" + +typedef struct CollectMetadataContext +{ + GHashTable *allTypes; + int currentIndex; + MonoMetadataSnapshot* metadata; +} CollectMetadataContext; + +static void +ContextRecurseClassData (CollectMetadataContext *context, MonoClass *klass) +{ + gpointer orig_key, value; + gpointer iter = NULL; + MonoClassField *field = NULL; + int fieldCount; + + /* use g_hash_table_lookup_extended as it returns boolean to indicate if value was found. + * If we use g_hash_table_lookup it returns the value which we were comparing to NULL. The problem is + * that 0 is a valid class index and was confusing our logic. + */ + if (klass->inited && !g_hash_table_lookup_extended (context->allTypes, klass, &orig_key, &value)) { + g_hash_table_insert (context->allTypes, klass, GINT_TO_POINTER (context->currentIndex++)); + + fieldCount = mono_class_num_fields (klass); + + if (fieldCount > 0) { + while ((field = mono_class_get_fields (klass, &iter))) { + MonoClass *fieldKlass = mono_class_from_mono_type (field->type); + + if (fieldKlass != klass) + ContextRecurseClassData (context, fieldKlass); + } + } + } +} + +static void +CollectHashMapClass (gpointer key, gpointer value, gpointer user_data) +{ + CollectMetadataContext *context = (CollectMetadataContext *)user_data; + MonoClass *klass = (MonoClass *)value; + ContextRecurseClassData (context, klass); +} + +static void +CollectHashMapListClasses (gpointer key, gpointer value, gpointer user_data) +{ + CollectMetadataContext *context = (CollectMetadataContext *)user_data; + GSList *list = (GSList *)value; + + while (list != NULL) { + MonoClass *klass = (MonoClass *)list->data; + ContextRecurseClassData (context, klass); + + list = g_slist_next (list); + } +} + +static void +CollectGenericClass (MonoGenericClass *genericClass, gpointer user_data) +{ + CollectMetadataContext *context = (CollectMetadataContext *)user_data; + + if (genericClass->cached_class != NULL) + ContextRecurseClassData (context, genericClass->cached_class); +} + +static void +CollectImageMetaData (MonoImage *image, gpointer value, CollectMetadataContext *context) +{ + int i; + MonoTableInfo *tdef = &image->tables[MONO_TABLE_TYPEDEF]; + GSList *list; + + if (image->dynamic) { + GHashTableIter iter; + gpointer key; + MonoDynamicImage *dynamicImage = (MonoDynamicImage *)image; + g_hash_table_iter_init (&iter, dynamicImage->typeref); + + while (g_hash_table_iter_next (&iter, &key, NULL)) { + MonoType *monoType = (MonoType *)key; + MonoClass *klass = mono_class_from_mono_type (monoType); + + if (klass) + ContextRecurseClassData (context, klass); + } + } + + /* Some classes are only in this list. + They are added in reflection_setup_internal_class_internal. + */ + list = image->reflection_info_unregister_classes; + + while (list) { + MonoClass *klass = (MonoClass *)list->data; + + if (klass) + ContextRecurseClassData (context, klass); + + list = list->next; + } + + for (i = 1; i < tdef->rows; ++i) { + MonoClass *klass; + MonoError error; + + guint32 token = (i + 1) | MONO_TOKEN_TYPE_DEF; + + klass = mono_class_get_checked (image, token, &error); + + if (klass) + ContextRecurseClassData (context, klass); + } + + if (image->array_cache) + g_hash_table_foreach (image->array_cache, CollectHashMapListClasses, context); + + if (image->szarray_cache) + g_hash_table_foreach (image->szarray_cache, CollectHashMapClass, context); + + if (image->ptr_cache) + g_hash_table_foreach (image->ptr_cache, CollectHashMapClass, context); +} + +static int +FindClassIndex (GHashTable *hashTable, MonoClass *klass) +{ + gpointer orig_key, value; + + if (!g_hash_table_lookup_extended (hashTable, klass, &orig_key, &value)) + return -1; + + return GPOINTER_TO_INT (value); +} + +static void +AddMetadataType (gpointer key, gpointer value, gpointer user_data) +{ + MonoClass *klass = (MonoClass *)key; + + int index = GPOINTER_TO_INT (value); + CollectMetadataContext *context = (CollectMetadataContext *)user_data; + MonoMetadataSnapshot *metadata = context->metadata; + MonoMetadataType *type = &metadata->types[index]; + + if (klass->rank > 0) { + type->flags = (MonoMetadataTypeFlags) (kArray | (kArrayRankMask & (klass->rank << 16))); + type->baseOrElementTypeIndex = FindClassIndex (context->allTypes, mono_class_get_element_class (klass)); + } else { + gpointer iter = NULL; + int fieldCount = 0; + MonoClassField *field; + MonoClass *baseClass; + MonoVTable *vtable; + void *statics_data; + + type->flags = (klass->valuetype || klass->byval_arg.type == MONO_TYPE_PTR) ? kValueType : kNone; + type->fieldCount = 0; + fieldCount = mono_class_num_fields (klass); + if (fieldCount > 0) { + type->fields = g_new (MonoMetadataField, fieldCount); + + while ((field = mono_class_get_fields (klass, &iter))) { + MonoMetadataField *metaField = &type->fields[type->fieldCount]; + MonoClass *typeKlass = mono_class_from_mono_type (field->type); + + metaField->typeIndex = FindClassIndex (context->allTypes, typeKlass); + + // This will happen if fields type is not initialized + // It's OK to skip it, because it means the field is guaranteed to be null on any object + if (metaField->typeIndex == -1) { + continue; + } + + // literals have no actual storage, and are not relevant in this context. + if ((field->type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0) + continue; + + metaField->isStatic = (field->type->attrs & FIELD_ATTRIBUTE_STATIC) != 0; + + metaField->offset = field->offset; + metaField->name = field->name; + type->fieldCount++; + } + } + + vtable = mono_class_try_get_vtable (mono_domain_get (), klass); + statics_data = vtable ? mono_vtable_get_static_field_data (vtable) : NULL; + + type->staticsSize = statics_data ? mono_class_data_size (klass) : 0; + type->statics = NULL; + + if (type->staticsSize > 0) { + type->statics = g_new0 (uint8_t, type->staticsSize); + memcpy (type->statics, statics_data, type->staticsSize); + } + + baseClass = mono_class_get_parent (klass); + type->baseOrElementTypeIndex = baseClass ? FindClassIndex (context->allTypes, baseClass) : -1; + } + + type->assemblyName = mono_class_get_image (klass)->assembly->aname.name; + type->name = mono_type_get_name_full (&klass->byval_arg, MONO_TYPE_NAME_FORMAT_IL); + type->typeInfoAddress = (uint64_t)klass; + type->size = (klass->valuetype) != 0 ? (mono_class_instance_size (klass) - sizeof (MonoObject)) : mono_class_instance_size (klass); +} + +static void CollectMetadata(MonoMetadataSnapshot* metadata, GHashTable* monoImages) +{ + CollectMetadataContext context; + + context.allTypes = g_hash_table_new(NULL, NULL); + context.currentIndex = 0; + context.metadata = metadata; + + g_hash_table_foreach(monoImages, (GHFunc)CollectImageMetaData, &context); + + mono_metadata_generic_class_foreach(CollectGenericClass, &context); + + metadata->typeCount = g_hash_table_size(context.allTypes); + metadata->types = g_new0(MonoMetadataType, metadata->typeCount); + + g_hash_table_foreach(context.allTypes, AddMetadataType, &context); + + g_hash_table_destroy(context.allTypes); +} + +static void MonoMemPoolNumChunksCallback(void* start, void* end, void* user_data) +{ + int* count = (int*)user_data; + (*count)++; +} + +static int MonoMemPoolNumChunks(MonoMemPool* pool) +{ + int count = 0; + mono_mempool_foreach_block(pool, MonoMemPoolNumChunksCallback, &count); + return count; +} + +typedef struct SectionIterationContext +{ + MonoManagedMemorySection* currentSection; +} SectionIterationContext; + +static void AllocateMemoryForSection(void* context, void* sectionStart, void* sectionEnd) +{ + ptrdiff_t sectionSize; + + SectionIterationContext* ctx = (SectionIterationContext*)context; + MonoManagedMemorySection* section = ctx->currentSection; + + section->sectionStartAddress = (uint64_t)sectionStart; + sectionSize = (uint8_t*)(sectionEnd)-(uint8_t*)(sectionStart); + + section->sectionSize = (uint32_t)(sectionSize); + section->sectionBytes = g_new(uint8_t, section->sectionSize); + + ctx->currentSection++; +} + +static void AllocateMemoryForMemPoolChunk(void* chunkStart, void* chunkEnd, void* context) +{ + AllocateMemoryForSection(context, chunkStart, chunkEnd); +} + +static void CopyHeapSection(void* context, void* sectionStart, void* sectionEnd) +{ + SectionIterationContext* ctx = (SectionIterationContext*)(context); + MonoManagedMemorySection* section = ctx->currentSection; + + g_assert(section->sectionStartAddress == (uint64_t)(sectionStart)); + g_assert(section->sectionSize == (uint8_t*)(sectionEnd)-(uint8_t*)(sectionStart)); + memcpy(section->sectionBytes, sectionStart, section->sectionSize); + + ctx->currentSection++; +} + +static void CopyMemPoolChunk(void* chunkStart, void* chunkEnd, void* context) +{ + CopyHeapSection(context, chunkStart, chunkEnd); +} + +static void IncrementCountForImageMemPoolNumChunks(MonoImage *image, gpointer* value, void *user_data) +{ + int* count = (int*)user_data; + (*count) += MonoMemPoolNumChunks(image->mempool); +} + +static int MonoImagesMemPoolNumChunks(GHashTable* monoImages) +{ + int count = 0; + + g_hash_table_foreach(monoImages, (GHFunc)IncrementCountForImageMemPoolNumChunks, &count); + return count; +} + +static void AllocateMemoryForMemPool(MonoMemPool* pool, void *user_data) +{ + mono_mempool_foreach_block(pool, AllocateMemoryForMemPoolChunk, user_data); +} + +static void AllocateMemoryForImageMemPool(MonoImage *image, gpointer value, void *user_data) +{ + AllocateMemoryForMemPool(image->mempool, user_data); +} + +static void CopyMemPool(MonoMemPool *pool, SectionIterationContext *context) +{ + mono_mempool_foreach_block(pool, CopyMemPoolChunk, context); +} + +static void CopyImageMemPool(MonoImage *image, gpointer value, SectionIterationContext *context) +{ + CopyMemPool(image->mempool, context); +} + +static void AllocateMemoryForImageClassCache(MonoImage *image, gpointer *value, void *user_data) +{ + AllocateMemoryForSection(user_data, image->class_cache.table, ((uint8_t*)image->class_cache.table) + image->class_cache.size); +} + +static void CopyImageClassCache(MonoImage *image, gpointer value, SectionIterationContext *context) +{ + CopyHeapSection(context, image->class_cache.table, ((uint8_t*)image->class_cache.table) + image->class_cache.size); +} + +static void IncrementCountForImageSetMemPoolNumChunks(MonoImageSet *imageSet, void *user_data) +{ + int* count = (int*)user_data; + (*count) += MonoMemPoolNumChunks(imageSet->mempool); +} + +static int MonoImageSetsMemPoolNumChunks() +{ + int count = 0; + mono_metadata_image_set_foreach(IncrementCountForImageSetMemPoolNumChunks, &count); + return count; +} + +static void AllocateMemoryForImageSetMemPool(MonoImageSet* imageSet, void *user_data) +{ + AllocateMemoryForMemPool(imageSet->mempool, user_data); +} + +static void CopyImageSetMemPool(MonoImageSet* imageSet, void *user_data) +{ + CopyMemPool(imageSet->mempool, user_data); +} + +typedef struct +{ + MonoManagedHeap* heap; + GHashTable* monoImages; + +} CaptureHeapInfoData; + +static void* CaptureHeapInfo(void* user) +{ + CaptureHeapInfoData* data = (CaptureHeapInfoData*)user; + MonoManagedHeap* heap = data->heap; + GHashTable* monoImages = data->monoImages; + + MonoDomain* domain = mono_domain_get(); + MonoDomain* rootDomain = mono_get_root_domain(); + SectionIterationContext iterationContext; + + // Increment count for each heap section + heap->sectionCount = GC_get_heap_section_count(); + // Increment count for the domain mem pool chunk + heap->sectionCount += MonoMemPoolNumChunks(rootDomain->mp); + heap->sectionCount += MonoMemPoolNumChunks(domain->mp); + // Increment count for each image mem pool chunk + heap->sectionCount += MonoImagesMemPoolNumChunks(monoImages); + // Increment count for each image->class_cache hash table. + heap->sectionCount += g_hash_table_size(monoImages); + // Increment count for each image set mem pool chunk + heap->sectionCount += MonoImageSetsMemPoolNumChunks(); + + heap->sections = g_new0(MonoManagedMemorySection, heap->sectionCount); + + iterationContext.currentSection = heap->sections; + + // Allocate memory for each heap section + GC_foreach_heap_section(&iterationContext, AllocateMemoryForSection); + // Allocate memory for the domain mem pool chunk + mono_domain_lock(rootDomain); + mono_mempool_foreach_block(rootDomain->mp, AllocateMemoryForMemPoolChunk, &iterationContext); + mono_domain_unlock(rootDomain); + mono_domain_lock(domain); + mono_mempool_foreach_block(domain->mp, AllocateMemoryForMemPoolChunk, &iterationContext); + mono_domain_unlock(domain); + // Allocate memory for each image mem pool chunk + g_hash_table_foreach(monoImages, (GHFunc)AllocateMemoryForImageMemPool, &iterationContext); + // Allocate memory for each image->class_cache hash table. + g_hash_table_foreach(monoImages, (GHFunc)AllocateMemoryForImageClassCache, &iterationContext); + // Allocate memory for each image->class_cache hash table. + mono_metadata_image_set_foreach(AllocateMemoryForImageSetMemPool, &iterationContext); + + return NULL; +} + +static void FreeMonoManagedHeap(MonoManagedHeap* heap) +{ + uint32_t i; + + for (i = 0; i < heap->sectionCount; i++) + { + g_free(heap->sections[i].sectionBytes); + } + + g_free(heap->sections); +} + +typedef struct VerifyHeapSectionStillValidIterationContext +{ + MonoManagedMemorySection* currentSection; + gboolean wasValid; +} VerifyHeapSectionStillValidIterationContext; + +static void VerifyHeapSectionIsStillValid(void* context, void* sectionStart, void* sectionEnd) +{ + VerifyHeapSectionStillValidIterationContext* iterationContext = (VerifyHeapSectionStillValidIterationContext*)context; + if (iterationContext->currentSection->sectionSize != (uint8_t*)(sectionEnd)-(uint8_t*)(sectionStart)) + iterationContext->wasValid = FALSE; + else if (iterationContext->currentSection->sectionStartAddress != (uint64_t)(sectionStart)) + iterationContext->wasValid = FALSE; + + iterationContext->currentSection++; +} + +static gboolean MonoManagedHeapStillValid(MonoManagedHeap* heap, GHashTable* monoImages) +{ + MonoDomain* rootDomain = mono_get_root_domain(); + MonoDomain* domain = mono_domain_get(); + + VerifyHeapSectionStillValidIterationContext iterationContext; + int currentSectionCount; + + currentSectionCount = GC_get_heap_section_count(); + currentSectionCount += MonoMemPoolNumChunks(rootDomain->mp); + currentSectionCount += MonoMemPoolNumChunks(domain->mp); + currentSectionCount += MonoImagesMemPoolNumChunks(monoImages); + currentSectionCount += g_hash_table_size(monoImages); // image->class_cache hash table. + currentSectionCount += MonoImageSetsMemPoolNumChunks(); + + if (heap->sectionCount != currentSectionCount) + return FALSE; + + iterationContext.currentSection = heap->sections; + iterationContext.wasValid = TRUE; + + GC_foreach_heap_section(&iterationContext, VerifyHeapSectionIsStillValid); + + return iterationContext.wasValid; +} + +// The difficulty in capturing the managed snapshot is that we need to do quite some work with the world stopped, +// to make sure that our snapshot is "valid", and didn't change as we were copying it. However, stopping the world, +// makes it so you cannot take any lock or allocations. We deal with it like this: +// +// 1) We take note of the amount of heap sections and their sizes, and we allocate memory to copy them into. +// 2) We stop the world. +// 3) We check if the amount of heapsections and their sizes didn't change in the mean time. If they did, try again. +// 4) Now, with the world still stopped, we memcpy() the memory from the real heapsections, into the memory that we +// allocated for their copies. +// 5) Start the world again. + +static void CaptureManagedHeap(MonoManagedHeap* heap, GHashTable* monoImages) +{ + MonoDomain* rootDomain = mono_get_root_domain(); + MonoDomain* domain = mono_domain_get(); + SectionIterationContext iterationContext; + + CaptureHeapInfoData data; + + data.heap = heap; + data.monoImages = monoImages; + + CaptureHeapInfo(&data); + + iterationContext.currentSection = heap->sections; + + GC_foreach_heap_section(&iterationContext, CopyHeapSection); + mono_mempool_foreach_block(rootDomain->mp, CopyMemPoolChunk, &iterationContext); + mono_mempool_foreach_block(domain->mp, CopyMemPoolChunk, &iterationContext); + g_hash_table_foreach(monoImages, (GHFunc)CopyImageMemPool, &iterationContext); + g_hash_table_foreach(monoImages, (GHFunc)CopyImageClassCache, &iterationContext); + mono_metadata_image_set_foreach(CopyImageSetMemPool, &iterationContext); +} + +static void GCHandleIterationCallback(MonoObject* managedObject, GPtrArray** managedObjects) +{ + g_ptr_array_add(*managedObjects, managedObject); +} + +static inline void CaptureGCHandleTargets(MonoGCHandles* gcHandles) +{ + uint32_t i; + GPtrArray* trackedObjects; + + trackedObjects = g_ptr_array_new(); + + mono_gc_strong_handle_foreach((GFunc)GCHandleIterationCallback, &trackedObjects); + + gcHandles->trackedObjectCount = (uint32_t)trackedObjects->len; + gcHandles->pointersToObjects = (uint64_t*)g_new0(uint64_t, gcHandles->trackedObjectCount); + + for (i = 0; i < gcHandles->trackedObjectCount; i++) + { + gcHandles->pointersToObjects[i] = (uint64_t)trackedObjects->pdata[i]; + } + + g_ptr_array_free(trackedObjects, TRUE); +} + +static void FillRuntimeInformation(MonoRuntimeInformation* runtimeInfo) +{ + runtimeInfo->pointerSize = (uint32_t)(sizeof(void*)); + runtimeInfo->objectHeaderSize = (uint32_t)(sizeof(MonoObject)); + runtimeInfo->arrayHeaderSize = offsetof(MonoArray, vector); + runtimeInfo->arraySizeOffsetInHeader = offsetof(MonoArray, max_length); + runtimeInfo->arrayBoundsOffsetInHeader = offsetof(MonoArray, bounds); + runtimeInfo->allocationGranularity = (uint32_t)(2 * sizeof(void*)); +} + +static gboolean ManagedHeapContainsAddress(MonoManagedHeap* heap, uint64_t address) +{ + uint32_t i; + + for (i = 0; i < heap->sectionCount; ++i) + { + MonoManagedMemorySection* section = &heap->sections[i]; + uint64_t sectionBegin = section->sectionStartAddress; + uint64_t sectionEnd = sectionBegin + section->sectionSize; + + if (address >= sectionBegin && address <= sectionEnd) + return TRUE; + } + + return FALSE; +} + +static void VerifySnapshot(MonoManagedMemorySnapshot* snapshot, GHashTable* monoImages) +{ + uint32_t i; + FILE* file; + GHashTableIter iter; + gpointer key; + MonoMetadataSnapshot* meta = &snapshot->metadata; + + file = fopen("MonoMemorySnapshotLog.txt", "w"); + + g_hash_table_iter_init(&iter, monoImages); + + while (g_hash_table_iter_next(&iter, &key, NULL)) + { + MonoImage* image = (MonoImage*)key; + fprintf(file, "MonoImage [0x%016llX] dynamic: %i name: '%s'\n", (uint64_t)image, image->dynamic, image->name); + } + + /* Verify that we have collected memory sections for all types */ + for (i = 0; i < meta->typeCount; ++i) + { + MonoMetadataType* type = &meta->types[i]; + + if (!ManagedHeapContainsAddress(&snapshot->heap, type->typeInfoAddress)) + { + fprintf(file, "The memory for type '%s' @ 0x%016llX is not the part the snapshot.\n", type->name, type->typeInfoAddress); + } + } + + fclose(file); +} + +static void CollectMonoImage(MonoImage* image, GHashTable* monoImages) +{ + if (g_hash_table_lookup(monoImages, image) != NULL) + return; + + g_hash_table_insert(monoImages, image, image); + + if (image->assembly->image != NULL && + image != image->assembly->image) + { + CollectMonoImage(image->assembly->image, monoImages); + } + + if (image->module_count > 0) + { + int i; + + for (i = 0; i < image->module_count; ++i) + { + MonoImage* moduleImage = image->modules[i]; + + if (moduleImage) + CollectMonoImage(moduleImage, monoImages); + } + } +} + +static void CollectMonoImageFromAssembly(MonoAssembly *assembly, void *user_data) +{ + GHashTable* monoImages = (GHashTable*)user_data; + CollectMonoImage(assembly->image, monoImages); +} + + + +typedef struct ClassReportContext { + ClassReportFunc callback; + void *user_data; +} ClassReportContext; + +static void +ReportHashMapClasses(gpointer key, gpointer value, gpointer user_data) +{ + ClassReportContext *context = (ClassReportContext *)user_data; + MonoClass *klass = (MonoClass *)value; + if (klass->inited) + context->callback(klass, context->user_data); +} + +static void +ReportHashMapListClasses(gpointer key, gpointer value, gpointer user_data) +{ + ClassReportContext *context = (ClassReportContext *)user_data; + GSList *list = (GSList *)value; + + while (list != NULL) { + MonoClass *klass = (MonoClass *)list->data; + + if (klass->inited) + context->callback(klass, context->user_data); + + list = g_slist_next(list); + } +} + +static void +ReportClassesFromAssembly(MonoAssembly *assembly, void *user_data) +{ + MonoImage *image = mono_assembly_get_image(assembly); + int i; + MonoTableInfo *tdef = &image->tables[MONO_TABLE_TYPEDEF]; + GSList *list; + ClassReportContext *context = (ClassReportContext*)user_data; + + if (image->dynamic) { + GHashTableIter iter; + gpointer key; + MonoDynamicImage *dynamicImage = (MonoDynamicImage *)image; + g_hash_table_iter_init(&iter, dynamicImage->typeref); + + while (g_hash_table_iter_next(&iter, &key, NULL)) { + MonoType *monoType = (MonoType *)key; + MonoClass *klass = mono_class_from_mono_type(monoType); + + if (klass && klass->inited) + context->callback(klass, context->user_data); + } + } + + /* Some classes are only in this list. + They are added in reflection_setup_internal_class_internal. + */ + list = image->reflection_info_unregister_classes; + + while (list) { + MonoClass *klass = (MonoClass *)list->data; + + if (klass && klass->inited) + context->callback(klass, context->user_data); + + list = list->next; + } + + for (i = 1; i < tdef->rows; ++i) { + MonoClass *klass; + MonoError error; + + guint32 token = (i + 1) | MONO_TOKEN_TYPE_DEF; + + klass = mono_class_get_checked(image, token, &error); + + if (klass && klass->inited) + context->callback(klass, context->user_data); + } + + if (image->array_cache) + g_hash_table_foreach(image->array_cache, ReportHashMapListClasses, user_data); + + if (image->szarray_cache) + g_hash_table_foreach(image->szarray_cache, ReportHashMapClasses, user_data); + + if (image->ptr_cache) + g_hash_table_foreach(image->ptr_cache, ReportHashMapClasses, user_data); +} + +static void +ReportGenericClasses(MonoGenericClass *genericClass, gpointer user_data) +{ + ClassReportContext *context = (ClassReportContext *)user_data; + + if (genericClass->cached_class != NULL) + context->callback(genericClass->cached_class, context->user_data); +} + +void +mono_unity_class_for_each(ClassReportFunc callback, void *user_data) +{ + ClassReportContext reportContext; + reportContext.callback = callback; + reportContext.user_data = user_data; + mono_domain_assembly_foreach(mono_domain_get(), ReportClassesFromAssembly, &reportContext); + mono_metadata_generic_class_foreach(ReportGenericClasses, &reportContext); +} + +MonoManagedMemorySnapshot* mono_unity_capture_memory_snapshot() +{ + GC_disable(); + GC_stop_world_external(); + + MonoManagedMemorySnapshot* snapshot; + snapshot = g_new0(MonoManagedMemorySnapshot, 1); + + GHashTable* monoImages = g_hash_table_new(NULL, NULL); + + mono_domain_assembly_foreach(mono_domain_get(), CollectMonoImageFromAssembly, monoImages); + + CollectMetadata(&snapshot->metadata, monoImages); + CaptureManagedHeap(&snapshot->heap, monoImages); + CaptureGCHandleTargets(&snapshot->gcHandles); + FillRuntimeInformation(&snapshot->runtimeInformation); + +#if _DEBUG +// VerifySnapshot(snapshot, monoImages); +#endif + + g_hash_table_destroy(monoImages); + + GC_start_world_external(); + GC_enable(); + + return snapshot; +} + +void mono_unity_free_captured_memory_snapshot(MonoManagedMemorySnapshot* snapshot) +{ + uint32_t i; + MonoMetadataSnapshot* metadata = &snapshot->metadata; + + FreeMonoManagedHeap(&snapshot->heap); + + g_free(snapshot->gcHandles.pointersToObjects); + + for (i = 0; i < metadata->typeCount; i++) + { + if ((metadata->types[i].flags & kArray) == 0) + { + g_free(metadata->types[i].fields); + g_free(metadata->types[i].statics); + } + + g_free(metadata->types[i].name); + } + + g_free(metadata->types); + g_free(snapshot); +} + +#else + +MonoManagedMemorySnapshot* mono_unity_capture_memory_snapshot() +{ + MonoManagedMemorySnapshot* snapshot; + snapshot = g_new0(MonoManagedMemorySnapshot, 1); + + return snapshot; +} + +void mono_unity_free_captured_memory_snapshot(MonoManagedMemorySnapshot* snapshot) +{ + g_free(snapshot); +} + +#endif \ No newline at end of file diff --git a/mono/metadata/unity-memory-info.h b/mono/metadata/unity-memory-info.h new file mode 100644 index 000000000000..e21c8f254368 --- /dev/null +++ b/mono/metadata/unity-memory-info.h @@ -0,0 +1,107 @@ +#ifndef __UNITY_MONO_MEMORY_INFO_H +#define __UNITY_MONO_MEMORY_INFO_H + +#include + +typedef struct MonoMetadataField +{ + uint32_t offset; + uint32_t typeIndex; + const char* name; + gboolean isStatic; +} MonoMetadataField; + +typedef enum MonoMetadataTypeFlags +{ + kNone = 0, + kValueType = 1 << 0, + kArray = 1 << 1, + kArrayRankMask = 0xFFFF0000 +} MonoMetadataTypeFlags; + +typedef struct MonoMetadataType +{ + MonoMetadataTypeFlags flags; // If it's an array, rank is encoded in the upper 2 bytes + MonoMetadataField* fields; + uint32_t fieldCount; + uint32_t staticsSize; + uint8_t* statics; + uint32_t baseOrElementTypeIndex; + char* name; + const char* assemblyName; + uint64_t typeInfoAddress; + uint32_t size; +} MonoMetadataType; + +typedef struct MonoMetadataSnapshot +{ + uint32_t typeCount; + MonoMetadataType* types; +} MonoMetadataSnapshot; + +typedef struct MonoManagedMemorySection +{ + uint64_t sectionStartAddress; + uint32_t sectionSize; + uint8_t* sectionBytes; +} MonoManagedMemorySection; + +typedef struct MonoManagedHeap +{ + uint32_t sectionCount; + MonoManagedMemorySection* sections; +} MonoManagedHeap; + +typedef struct MonoStacks +{ + uint32_t stackCount; + MonoManagedMemorySection* stacks; +} MonoStacks; + +typedef struct NativeObject +{ + uint32_t gcHandleIndex; + uint32_t size; + uint32_t instanceId; + uint32_t classId; + uint32_t referencedNativeObjectIndicesCount; + uint32_t* referencedNativeObjectIndices; +} NativeObject; + +typedef struct MonoGCHandles +{ + uint32_t trackedObjectCount; + uint64_t* pointersToObjects; +} MonoGCHandles; + +typedef struct MonoRuntimeInformation +{ + uint32_t pointerSize; + uint32_t objectHeaderSize; + uint32_t arrayHeaderSize; + uint32_t arrayBoundsOffsetInHeader; + uint32_t arraySizeOffsetInHeader; + uint32_t allocationGranularity; +} MonoRuntimeInformation; + +typedef struct MonoManagedMemorySnapshot +{ + MonoManagedHeap heap; + MonoStacks stacks; + MonoMetadataSnapshot metadata; + MonoGCHandles gcHandles; + MonoRuntimeInformation runtimeInformation; + void* additionalUserInformation; +} MonoManagedMemorySnapshot; + +typedef struct _MonoClass MonoClass; + +typedef void(*ClassReportFunc) (MonoClass* klass, void *user_data); + +MONO_API void +mono_unity_class_for_each(ClassReportFunc callback, void* user_data); + +MONO_API MonoManagedMemorySnapshot* mono_unity_capture_memory_snapshot(); +MONO_API void mono_unity_free_captured_memory_snapshot(MonoManagedMemorySnapshot* snapshot); + +#endif diff --git a/mono/metadata/unity-utils.c b/mono/metadata/unity-utils.c new file mode 100644 index 000000000000..57cb2c0367ae --- /dev/null +++ b/mono/metadata/unity-utils.c @@ -0,0 +1,1931 @@ +#include + + +#include + +/* allow Unity to use deprecated functions for now */ +#ifdef MONO_RT_EXTERNAL_ONLY +#undef MONO_RT_EXTERNAL_ONLY +#define MONO_RT_EXTERNAL_ONLY +#endif +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_BDWGC_GC +#include +#include +#endif + +#include + +#ifdef WIN32 +#define UTF8_2_WIDE(src,dst) MultiByteToWideChar( CP_UTF8, 0, src, -1, dst, MAX_PATH ) +#endif + +#undef exit + +void unity_mono_exit( int code ) +{ + //fprintf( stderr, "mono: exit called, code %d\n", code ); + exit( code ); +} + + +GString* gEmbeddingHostName = 0; + + +MONO_API void mono_unity_set_embeddinghostname(const char* name) +{ + gEmbeddingHostName = g_string_new(name); +} + + + +MonoString* mono_unity_get_embeddinghostname() +{ + if (gEmbeddingHostName == 0) + mono_unity_set_embeddinghostname("mono"); + return mono_string_new_wrapper(gEmbeddingHostName->str); +} + +static gboolean socket_security_enabled = FALSE; + +gboolean +mono_unity_socket_security_enabled_get () +{ + return socket_security_enabled; +} + +void +mono_unity_socket_security_enabled_set (gboolean enabled) +{ + socket_security_enabled = enabled; +} + +void mono_unity_set_vprintf_func (vprintf_func func) +{ + set_vprintf_func (func); +} + +MONO_API gboolean +mono_unity_class_is_interface (MonoClass* klass) +{ + return MONO_CLASS_IS_INTERFACE(klass); +} + +MONO_API gboolean +mono_unity_class_is_abstract (MonoClass* klass) +{ + return (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT); +} + +// classes_ref is a preallocated array of *length_ref MonoClass* +// returned classes are stored in classes_ref, number of stored classes is stored in length_ref +// return value is number of classes found (which may be greater than number of classes stored) +unsigned mono_unity_get_all_classes_with_name_case (MonoImage *image, const char *name, MonoClass **classes_ref, unsigned *length_ref) +{ + MonoClass *klass; + MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF]; + int i, count; + guint32 attrs, visibility; + unsigned length = 0; + + /* (yoinked from icall.c) we start the count from 1 because we skip the special type */ + for (i = 1; i < tdef->rows; ++i) + { + klass = mono_class_get (image, (i + 1) | MONO_TOKEN_TYPE_DEF); + if (klass && klass->name && 0 == mono_utf8_strcasecmp (klass->name, name)) + { + if (length < *length_ref) + classes_ref[length] = klass; + ++length; + } + } + + if (length < *length_ref) + *length_ref = length; + return length; +} + +gboolean +unity_mono_method_is_generic (MonoMethod* method) +{ + return method->is_generic; +} + +MONO_API MonoMethod* +unity_mono_reflection_method_get_method(MonoReflectionMethod* mrf) +{ + if(!mrf) + return NULL; + + return mrf->method; +} + +MONO_API void +mono_unity_g_free(void *ptr) +{ + g_free (ptr); +} + + +MONO_API MonoClass* +mono_custom_attrs_get_attrs (MonoCustomAttrInfo *ainfo, gpointer *iter) +{ + int index = -1; + if (!iter) + return NULL; + if (!*iter) + { + *iter = 1; + return ainfo->attrs[0].ctor->klass; + } + + index = GPOINTER_TO_INT (*iter); + if (index >= ainfo->num_attrs) + return NULL; + *iter = GINT_TO_POINTER (index + 1); + return ainfo->attrs[index].ctor->klass; +} + +MONO_API gboolean +mono_class_is_inflated (MonoClass *klass) +{ + g_assert(klass); + return (klass->class_kind == MONO_CLASS_GINST); +} + +MONO_API void +mono_thread_pool_cleanup (void) +{ + // TODO_UNITY : I am not sure we need to call this anymore + mono_threadpool_cleanup (); +} + +MONO_API void* +mono_class_get_userdata (MonoClass* klass) +{ + return klass->unity_user_data; +} + +MONO_API void +mono_class_set_userdata(MonoClass* klass, void* userdata) +{ + klass->unity_user_data = userdata; +} + +MONO_API int +mono_class_get_userdata_offset() +{ + return offsetof(struct _MonoClass, unity_user_data); +} + + +static UnityFindPluginCallback unity_find_plugin_callback; + +MONO_API void +mono_set_find_plugin_callback (UnityFindPluginCallback find) +{ + unity_find_plugin_callback = find; +} + +MONO_API UnityFindPluginCallback +mono_get_find_plugin_callback () +{ + return unity_find_plugin_callback; +} + +//object + +void mono_unity_object_init(void* obj, MonoClass* klass) +{ + if (klass->valuetype) + memset(obj, 0, klass->instance_size - sizeof(MonoObject)); + else + *(MonoObject**)obj = NULL; +} + +MonoObject* mono_unity_object_isinst_sealed(MonoObject* obj, MonoClass* targetType) +{ + return obj->vtable->klass == targetType ? obj : NULL; +} + +void mono_unity_object_unbox_nullable(MonoObject* obj, MonoClass* nullableArgumentClass, void* storage) +{ + uint32_t valueSize = nullableArgumentClass->instance_size - sizeof(MonoObject); + + if (obj == NULL) + { + *((mono_byte*)(storage)+valueSize) = 0; + } + else if (obj->vtable->klass != nullableArgumentClass) + { + mono_raise_exception_deprecated (mono_get_exception_invalid_cast()); + } + else + { + memcpy(storage, mono_object_unbox(obj), valueSize); + *((mono_byte*)(storage)+valueSize) = 1; + } +} + +MonoClass* mono_unity_object_get_class(MonoObject *obj) +{ + return obj->vtable->klass; +} + +MonoObject* mono_unity_object_compare_exchange(MonoObject **location, MonoObject *value, MonoObject *comparand) +{ + return ves_icall_System_Threading_Interlocked_CompareExchange_T(location, value, comparand); +} + +MonoObject* mono_unity_object_exchange(MonoObject **location, MonoObject *value) +{ + return ves_icall_System_Threading_Interlocked_Exchange_T(location, value); +} + +gboolean mono_unity_object_check_box_cast(MonoObject *obj, MonoClass *klass) +{ + return (obj->vtable->klass->element_class == klass->element_class); +} + +//class + +const char* mono_unity_class_get_image_name(MonoClass* klass) +{ + return klass->image->assembly_name; +} + +MonoClass* mono_unity_class_get_generic_definition(MonoClass* klass) +{ + MonoGenericClass* generic_class = mono_class_try_get_generic_class (klass); + if (generic_class) + return generic_class->container_class; + + return NULL; +} + +MonoClass* mono_unity_class_inflate_generic_class(MonoClass *gklass, MonoGenericContext *context) +{ + MonoError error; + return mono_class_inflate_generic_class_checked(gklass, context, &error); +} + +gboolean mono_unity_class_has_parent_unsafe(MonoClass *klass, MonoClass *parent) +{ + return mono_class_has_parent_fast(klass, parent); +} + +MonoAssembly* mono_unity_class_get_assembly(MonoClass *klass) +{ + return klass->image->assembly; +} + +gboolean mono_unity_class_is_array(MonoClass *klass) +{ + return klass->rank > 0; +} + +MonoClass* mono_unity_class_get_element_class(MonoClass *klass) +{ + return klass->element_class; +} + +gboolean mono_unity_class_is_delegate(MonoClass *klass) +{ + return klass->delegate; +} + +int mono_unity_class_get_instance_size(MonoClass *klass) +{ + return klass->instance_size; +} + +MonoClass* mono_unity_class_get_castclass(MonoClass *klass) +{ + return klass->cast_class; +} + +guint32 mono_unity_class_get_native_size(MonoClass* klass) +{ + MonoMarshalType* info = mono_marshal_load_type_info(klass); + return info->native_size; +} + +MonoBoolean mono_unity_class_is_string(MonoClass* klass) +{ + if (mono_class_get_type(klass)->type == MONO_TYPE_STRING) + return TRUE; + return FALSE; +} + +MonoBoolean mono_unity_class_is_class_type(MonoClass* klass) +{ + if (mono_class_get_type(klass)->type == MONO_TYPE_CLASS) + return TRUE; + return FALSE; +} + +MONO_API gboolean +mono_class_is_generic(MonoClass *klass) +{ + g_assert(klass); + return (klass->class_kind == MONO_CLASS_GTD); +} + +MONO_API gboolean +mono_class_is_blittable(MonoClass *klass) +{ + g_assert(klass); + return klass->blittable; +} + +gboolean mono_unity_class_has_cctor(MonoClass *klass) +{ + return klass->has_cctor ? TRUE : FALSE; +} + +//method + +MonoMethod* mono_unity_method_get_generic_definition(MonoMethod* method) +{ + if (method->is_inflated) + return ((MonoMethodInflated*)method)->declaring; + + return NULL; +} + +MonoReflectionMethod* mono_unity_method_get_object(MonoMethod *method) +{ + MonoError unused; + return mono_method_get_object_checked(mono_domain_get(), method, NULL, &unused); +} + +MonoMethod* mono_unity_method_alloc0(MonoClass* klass) +{ + return mono_image_alloc0(klass->image, sizeof(MonoMethod)); +} + +MonoMethod* mono_unity_method_delegate_invoke_wrapper(MonoClass* klass) +{ + MonoMethod* method = (MonoMethod*)mono_image_alloc0(klass->image, sizeof(MonoMethod)); + MonoMethod *invoke = mono_get_delegate_invoke (klass); + method->signature = mono_metadata_signature_dup_full (klass->image, mono_method_signature (invoke)); + return method; +} + +gboolean mono_unity_method_is_static(MonoMethod *method) +{ + return method->flags & METHOD_ATTRIBUTE_STATIC; +} + +MonoClass* mono_unity_method_get_class(const MonoMethod *method) +{ + return method->klass; +} + +#ifdef IL2CPP_ON_MONO + +void* mono_unity_method_get_method_pointer(MonoMethod* method) +{ + return method->method_pointer; +} + +void mono_unity_method_set_method_pointer(MonoMethod* method, void *p) +{ + method->method_pointer = p; +} + +void* mono_unity_method_get_invoke_pointer(MonoMethod* method) +{ + return method->invoke_pointer; +} + +void mono_unity_method_set_invoke_pointer(MonoMethod* method, void *p) +{ + method->invoke_pointer = p; +} + +#endif + +const char* mono_unity_method_get_name(const MonoMethod *method) +{ + return method->name; +} + + +//must match the hash in il2cpp code generation +static guint32 hash_string_djb2(guchar *str) +{ + guint32 hash = 5381; + int c; + + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} + +static guint32 get_array_structure_hash(MonoArrayType *atype) +{ + char buffer[100]; + char *ptr = buffer; + + *ptr++ = '['; + + char numbuffer[10]; + + for (int i = 0; i < atype->rank; ++i) + { + if (atype->numlobounds > 0 && atype->lobounds[i] != 0) + { + snprintf(numbuffer, 10, "%d", atype->lobounds[i]); + char *ptrnum = numbuffer; + while (*ptrnum) + *ptr++ = *ptrnum++; + + *ptr++ = ':'; + } + + if (atype->numsizes > 0 && atype->sizes[i] != 0) + { + snprintf(numbuffer, 10, "%d", atype->sizes[i]); + char *ptrnum = numbuffer; + while (*ptrnum) + *ptr++ = *ptrnum++; + } + + if (i < atype->rank - 1) + *ptr++ = ','; + } + + *ptr++ = ']'; + *ptr++ = 0; + + return hash_string_djb2(buffer); +} + +/* Begin: Hash computation helper functions */ + +static void get_type_hashes(MonoType *type, GList *hashes, gboolean inflate); +static void get_type_hashes_generic_inst(MonoGenericInst *inst, GList *hashes, gboolean inflate); + + +static void get_type_hashes_generic_inst(MonoGenericInst *inst, GList *hashes, gboolean inflate) +{ + for (int i = 0; i < inst->type_argc; ++i) + { + MonoType *type = inst->type_argv[i]; + get_type_hashes(type, hashes, inflate); + } +} + +static void get_type_hashes(MonoType *type, GList *hashes, gboolean inflate) +{ + if (type->type != MONO_TYPE_GENERICINST) + { + MonoClass *klass = NULL; + + switch (type->type) + { + case MONO_TYPE_ARRAY: + { + MonoArrayType *atype = type->data.array; + g_list_append(hashes, MONO_TOKEN_TYPE_SPEC); + g_list_append(hashes, get_array_structure_hash(atype)); + get_type_hashes(&(atype->eklass->this_arg), hashes, inflate); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + klass = type->data.klass; + break; + case MONO_TYPE_BOOLEAN: + klass = mono_defaults.boolean_class; + break; + case MONO_TYPE_CHAR: + klass = mono_defaults.char_class; + break; + case MONO_TYPE_I: + klass = mono_defaults.int_class; + break; + case MONO_TYPE_U: + klass = mono_defaults.uint_class; + break; + case MONO_TYPE_I1: + klass = mono_defaults.sbyte_class; + break; + case MONO_TYPE_U1: + klass = mono_defaults.byte_class; + break; + case MONO_TYPE_I2: + klass = mono_defaults.int16_class; + break; + case MONO_TYPE_U2: + klass = mono_defaults.uint16_class; + break; + case MONO_TYPE_I4: + klass = mono_defaults.int32_class; + break; + case MONO_TYPE_U4: + klass = mono_defaults.uint32_class; + break; + case MONO_TYPE_I8: + klass = mono_defaults.int64_class; + break; + case MONO_TYPE_U8: + klass = mono_defaults.uint64_class; + break; + case MONO_TYPE_R4: + klass = mono_defaults.single_class; + break; + case MONO_TYPE_R8: + klass = mono_defaults.double_class; + break; + case MONO_TYPE_STRING: + klass = mono_defaults.string_class; + break; + case MONO_TYPE_OBJECT: + klass = mono_defaults.object_class; + break; + } + + if (klass) + { + g_list_append(hashes, klass->type_token); + g_list_append(hashes, hash_string_djb2(klass->image->module_name)); + } + + return; + } + else + { + g_list_append(hashes, type->data.generic_class->container_class->type_token); + g_list_append(hashes, hash_string_djb2(type->data.generic_class->container_class->image->module_name)); + + if (inflate) + get_type_hashes_generic_inst(type->data.generic_class->context.class_inst, hashes, inflate); + } + +} + +static GList* get_type_hashes_method(MonoMethod *method, gboolean inflate) +{ + GList *hashes = monoeg_g_list_alloc(); + + hashes->data = method->token; + g_list_append(hashes, hash_string_djb2(method->klass->image->module_name)); + + if (inflate && method->klass->class_kind == MONO_CLASS_GINST) + { + g_list_append(hashes, method->klass->type_token); + get_type_hashes_generic_inst(mono_class_get_generic_class (method->klass)->context.class_inst, hashes, inflate); + } + + if (inflate && method->is_inflated) + { + MonoGenericContext* methodGenericContext = mono_method_get_context(method); + if (methodGenericContext->method_inst != NULL) + get_type_hashes_generic_inst(methodGenericContext->method_inst, hashes, inflate); + } + + return hashes; +} + +//hash combination function must match the one used in IL2CPP codegen +static guint64 combine_hashes(guint64 hash1, guint64 hash2) +{ + const guint64 seed = 486187739; + return hash1 * seed + hash2; +} + +static void combine_all_hashes(gpointer data, gpointer user_data) +{ + guint64 *hash = (guint64*)user_data; + if (*hash == 0) + *hash = (guint64)data; + else + *hash = combine_hashes(*hash, (guint64)(uintptr_t)data); +} + +/* End: Hash computation helper functions */ + +guint64 mono_unity_method_get_hash(MonoMethod *method, gboolean inflate) +{ + GList *hashes = get_type_hashes_method(method, inflate); + + guint64 hash = 0; + + g_list_first(hashes); + g_list_foreach(hashes, combine_all_hashes, &hash); + g_list_free(hashes); + + return hash; +} + +guint64 mono_unity_type_get_hash(MonoType *type, gboolean inflate) +{ + GList *hashes = monoeg_g_list_alloc(); + + get_type_hashes(type, hashes, inflate); + + guint64 hash = 0; + + g_list_first(hashes); + g_list_foreach(hashes, combine_all_hashes, &hash); + g_list_free(hashes); + + return hash; +} + + +MonoMethod* mono_unity_method_get_aot_array_helper_from_wrapper(MonoMethod *method) +{ + MonoMethod *m; + const char *prefix; + MonoGenericContext ctx; + MonoType *args[16]; + char *mname, *iname, *s, *s2, *helper_name = NULL; + + prefix = "System.Collections.Generic"; + s = g_strdup_printf("%s", method->name + strlen(prefix) + 1); + s2 = strstr(s, "`1."); + g_assert(s2); + s2[0] = '\0'; + iname = s; + mname = s2 + 3; + + //printf ("X: %s %s\n", iname, mname); + + if (!strcmp(iname, "IList")) + helper_name = g_strdup_printf("InternalArray__%s", mname); + else + helper_name = g_strdup_printf("InternalArray__%s_%s", iname, mname); + m = mono_class_get_method_from_name(mono_defaults.array_class, helper_name, mono_method_signature(method)->param_count); + g_assert(m); + g_free(helper_name); + g_free(s); + + if (m->is_generic) { + MonoError error; + memset(&ctx, 0, sizeof(ctx)); + args[0] = &method->klass->element_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst(1, args); + m = mono_class_inflate_generic_method_checked(m, &ctx, &error); + g_assert(mono_error_ok(&error)); /* FIXME don't swallow the error */ + } + + return m; +} + +MonoObject* mono_unity_method_convert_return_type_if_needed(MonoMethod *method, void *value) +{ + if (method->signature && method->signature->ret->type == MONO_TYPE_PTR) + { + MonoError unused; + return mono_value_box_checked(mono_domain_get(), mono_defaults.int_class, &value, &unused); + } + + return (MonoObject*)value; +} + +MONO_API gboolean +unity_mono_method_is_inflated(MonoMethod* method) +{ + return method->is_inflated; +} + +guint32 mono_unity_method_get_token(MonoMethod *method) +{ + return method->token; +} + +//domain + + +void mono_unity_domain_install_finalize_runtime_invoke(MonoDomain* domain, RuntimeInvokeFunction callback) +{ + domain->finalize_runtime_invoke = callback; +} + +void mono_unity_domain_install_capture_context_runtime_invoke(MonoDomain* domain, RuntimeInvokeFunction callback) +{ + domain->capture_context_runtime_invoke = callback; +} + +void mono_unity_domain_install_capture_context_method(MonoDomain* domain, gpointer callback) +{ + domain->capture_context_method = callback; +} + + +void mono_unity_domain_unload (MonoDomain* domain, MonoUnityExceptionFunc callback) +{ + MonoObject *exc = NULL; + mono_domain_try_unload (domain, &exc, callback); +} + +//array + +int mono_unity_array_get_element_size(MonoArray *arr) +{ + return arr->obj.vtable->klass->sizes.element_size; +} + +MonoClass* mono_unity_array_get_class(MonoArray *arr) +{ + return arr->obj.vtable->klass; +} + +mono_array_size_t mono_unity_array_get_max_length(MonoArray *arr) +{ + return arr->max_length; +} + +//type + +gboolean mono_unity_type_is_generic_instance(MonoType *type) +{ + return type->type == MONO_TYPE_GENERICINST; +} + +MonoGenericClass* mono_unity_type_get_generic_class(MonoType *type) +{ + if (type->type != MONO_TYPE_GENERICINST) + return NULL; + + return type->data.generic_class; +} + +gboolean mono_unity_type_is_enum_type(MonoType *type) +{ + if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) + return TRUE; + if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype) + return TRUE; + return FALSE; +} + +gboolean mono_unity_type_is_boolean(MonoType *type) +{ + return type->type == MONO_TYPE_BOOLEAN; +} + +MonoClass* mono_unity_type_get_element_class(MonoType *type) +{ + return type->data.klass->element_class; +} + +//generic class + +MonoGenericContext mono_unity_generic_class_get_context(MonoGenericClass *klass) +{ + return klass->context; +} + +MonoClass* mono_unity_generic_class_get_container_class(MonoGenericClass *klass) +{ + return klass->container_class; +} + +//method signature + +MonoClass* mono_unity_signature_get_class_for_param(MonoMethodSignature *sig, int index) +{ + MonoType *type = sig->params[index]; + return mono_class_from_mono_type(type); +} + +int mono_unity_signature_num_parameters(MonoMethodSignature *sig) +{ + return sig->param_count; +} + +gboolean mono_unity_signature_param_is_byref(MonoMethodSignature *sig, int index) +{ + return sig->params[index]->byref; +} + +//generic inst + +guint mono_unity_generic_inst_get_type_argc(MonoGenericInst *inst) +{ + return inst->type_argc; +} + +MonoType* mono_unity_generic_inst_get_type_argument(MonoGenericInst *inst, int index) +{ + return inst->type_argv[index]; +} + +//exception + +MonoString* mono_unity_exception_get_message(MonoException *exc) +{ + return exc->message; +} + +MonoString* mono_unity_exception_get_stack_trace(MonoException *exc) +{ + return exc->stack_trace; +} + +MonoObject* mono_unity_exception_get_inner_exception(MonoException *exc) +{ + return exc->inner_ex; +} + +MonoArray* mono_unity_exception_get_trace_ips(MonoException *exc) +{ + return exc->trace_ips; +} + +void mono_unity_exception_set_trace_ips(MonoException *exc, MonoArray *ips) +{ + g_assert(sizeof((exc)->trace_ips) == sizeof(void**)); + mono_gc_wbarrier_set_field((MonoObject*)(exc), &((exc)->trace_ips), (MonoObject*)ips); +} + +MonoException* mono_unity_exception_get_marshal_directive(const char* msg) +{ + return mono_exception_from_name_msg(mono_get_corlib(), "System.Runtime.InteropServices", "MarshalDirectiveException", msg); +} + +MonoException* mono_unity_error_convert_to_exception (MonoError *error) +{ + return mono_error_convert_to_exception (error); +} + +//defaults + +MonoClass* mono_unity_defaults_get_int_class() +{ + return mono_defaults.int_class; +} + +MonoClass* mono_unity_defaults_get_stack_frame_class() +{ + return mono_defaults.stack_frame_class; +} + +MonoClass* mono_unity_defaults_get_int32_class() +{ + return mono_defaults.int32_class; +} + +MonoClass* mono_unity_defaults_get_char_class() +{ + return mono_defaults.char_class; +} + +MonoClass* mono_unity_defaults_get_delegate_class() +{ + return mono_defaults.delegate_class; +} + +MonoClass* mono_unity_defaults_get_byte_class() +{ + return mono_defaults.byte_class; +} + +//unitytls + +static unitytls_interface_struct* gUnitytlsInterface = NULL; + +MONO_API unitytls_interface_struct* +mono_unity_get_unitytls_interface() +{ + return gUnitytlsInterface; +} + +// gc +MONO_API void mono_unity_gc_set_mode(MonoGCMode mode) +{ +#if HAVE_BDWGC_GC + switch (mode) + { + case MONO_GC_MODE_ENABLED: + if (GC_is_disabled()) + GC_enable(); + GC_set_disable_automatic_collection(FALSE); + break; + + case MONO_GC_MODE_DISABLED: + if (!GC_is_disabled()) + GC_disable(); + break; + + case MONO_GC_MODE_MANUAL: + if (GC_is_disabled()) + GC_enable(); + GC_set_disable_automatic_collection(TRUE); + break; + } +#else + g_assert_not_reached (); +#endif +} + +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API void mono_unity_gc_enable() +{ +#if HAVE_BDWGC_GC + GC_enable(); +#else + g_assert_not_reached (); +#endif +} + +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API void mono_unity_gc_disable() +{ +#if HAVE_BDWGC_GC + GC_disable(); +#else + g_assert_not_reached (); +#endif +} + +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API int mono_unity_gc_is_disabled() +{ +#if HAVE_BDWGC_GC + return GC_is_disabled (); +#else + g_assert_not_reached (); + return 0; +#endif +} + +MONO_API void +mono_unity_install_unitytls_interface(unitytls_interface_struct* callbacks) +{ + gUnitytlsInterface = callbacks; +} + +//misc + +MonoAssembly* mono_unity_assembly_get_mscorlib() +{ + return mono_defaults.corlib->assembly; +} + +MonoImage* mono_unity_image_get_mscorlib() +{ + return mono_defaults.corlib->assembly->image; +} + +MonoClass* mono_unity_generic_container_get_parameter_class(MonoGenericContainer* generic_container, gint index) +{ + MonoGenericParam *param = mono_generic_container_get_param(generic_container, index); + return mono_class_from_generic_parameter_internal(param); +} + +MonoString* mono_unity_string_append_assembly_name_if_necessary(MonoString* typeName, const char* assemblyName) +{ + if (typeName != NULL) + { + MonoTypeNameParse info; + + // The mono_reflection_parse_type function will mangle the name, so don't use this copy later. + MonoError unused; + char* nameForParsing = mono_string_to_utf8_checked(typeName, &unused); + if (mono_reflection_parse_type(nameForParsing, &info)) + { + if (!info.assembly.name) + { + GString* assemblyQualifiedName = g_string_new(0); + char* name = mono_string_to_utf8_checked(typeName, &unused); + g_string_append_printf(assemblyQualifiedName, "%s, %s", name, assemblyName); + + typeName = mono_string_new(mono_domain_get(), assemblyQualifiedName->str); + + g_string_free(assemblyQualifiedName, FALSE); + mono_free(name); + } + } + + mono_free(nameForParsing); + } + + return typeName; +} + +void mono_unity_memory_barrier() +{ + mono_memory_barrier(); +} + +MonoObject* mono_unity_delegate_get_target(MonoDelegate *delegate) +{ + return delegate->target; +} + +gchar* mono_unity_get_runtime_build_info(const char *date, const char *time) +{ + return g_strdup_printf("Unity IL2CPP(%s %s)", date, time); +} + +void* mono_unity_get_field_address(MonoObject *obj, MonoVTable *vt, MonoClassField *field) +{ + // This is a copy of mono_field_get_addr - we need to consider how to expose that on the public API. + MONO_REQ_GC_UNSAFE_MODE; + + guint8 *src; + + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) { + if (field->offset == -1) { + /* Special static */ + gpointer addr; + + mono_domain_lock(vt->domain); + addr = g_hash_table_lookup(vt->domain->special_static_fields, field); + mono_domain_unlock(vt->domain); + src = (guint8 *)mono_get_special_static_data(GPOINTER_TO_UINT(addr)); + } + else { + src = (guint8*)mono_vtable_get_static_field_data(vt) + field->offset; + } + } + else { + src = (guint8*)obj + field->offset; + } + + return src; +} + +gboolean mono_unity_thread_state_init_from_handle(MonoThreadUnwindState *tctx, MonoThreadInfo *info, void* fixme) +{ + tctx->valid = TRUE; + tctx->unwind_data[MONO_UNWIND_DATA_DOMAIN] = mono_domain_get(); + tctx->unwind_data[MONO_UNWIND_DATA_LMF] = NULL; + tctx->unwind_data[MONO_UNWIND_DATA_JIT_TLS] = NULL; + + return TRUE; +} + +void mono_unity_stackframe_set_method(MonoStackFrame *sf, MonoMethod *method) +{ + g_assert(sizeof(sf->method) == sizeof(void**)); + MonoError unused; + mono_gc_wbarrier_set_field((MonoObject*)(sf), &(sf->method), (MonoObject*)mono_method_get_object_checked(mono_domain_get(), method, NULL, &unused)); +} + +MonoType* mono_unity_reflection_type_get_type(MonoReflectionType *type) +{ + return type->type; +} + +// layer to proxy differences between old and new Mono versions + +MONO_API void +mono_unity_runtime_set_main_args (int argc, const char* argv[]) +{ + mono_runtime_set_main_args (argc, argv); +} + +MONO_API MonoString* +mono_unity_string_empty_wrapper () +{ + return mono_string_empty (mono_domain_get ()); +} + +MONO_API MonoArray* +mono_unity_array_new_2d (MonoDomain *domain, MonoClass *eklass, size_t size0, size_t size1) +{ + MonoError error; + uintptr_t sizes[] = { (uintptr_t)size0, (uintptr_t)size1 }; + MonoClass* ac = mono_array_class_get (eklass, 2); + + MonoArray* array = mono_array_new_full_checked (domain, ac, sizes, NULL, &error); + mono_error_cleanup (&error); + + return array; +} + +MONO_API MonoArray* +mono_unity_array_new_3d (MonoDomain *domain, MonoClass *eklass, size_t size0, size_t size1, size_t size2) +{ + MonoError error; + uintptr_t sizes[] = { (uintptr_t)size0, (uintptr_t)size1, (uintptr_t)size2 }; + MonoClass* ac = mono_array_class_get (eklass, 3); + + MonoArray* array = mono_array_new_full_checked (domain, ac, sizes, NULL, &error); + mono_error_cleanup (&error); + + return array; +} + +MONO_API void +mono_unity_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name) +{ + mono_domain_set_config (domain, base_dir, config_file_name); +} + +MONO_API MonoException* +mono_unity_loader_get_last_error_and_error_prepare_exception () +{ + return NULL; +} + +MONO_API void +mono_unity_install_memory_callbacks (MonoAllocatorVTable* callbacks) +{ + mono_set_allocator_vtable (callbacks); +} + +static char* data_dir = NULL; +MONO_API void +mono_unity_set_data_dir(const char* dir) +{ + if (data_dir) + g_free(data_dir); + + data_dir = g_new(char*, strlen(dir) + 1); + strcpy(data_dir, dir); +} + +MONO_API char* +mono_unity_get_data_dir() +{ + return data_dir; +} + +MONO_API MonoClass* +mono_unity_class_get_generic_type_definition (MonoClass* klass) +{ + MonoGenericClass* generic_class = mono_class_try_get_generic_class (klass); + return generic_class ? generic_class->container_class : NULL; +} + +MONO_API MonoClass* +mono_unity_class_get_generic_parameter_at (MonoClass* klass, guint32 index) +{ + MonoGenericContainer* generic_container = mono_class_try_get_generic_container (klass); + if (!generic_container || index >= generic_container->type_argc) + return NULL; + + return mono_class_from_generic_parameter_internal (mono_generic_container_get_param (generic_container, index)); +} + +MONO_API guint32 +mono_unity_class_get_generic_parameter_count (MonoClass* klass) +{ + MonoGenericContainer* generic_container = mono_class_try_get_generic_container (klass); + + if (!generic_container) + return 0; + + return generic_container->type_argc; +} + +MONO_API MonoClass* +mono_unity_class_get_generic_argument_at (MonoClass* klass, guint32 index) +{ + if (!mono_class_is_ginst (klass)) + return NULL; + + MonoGenericClass* generic_class = mono_class_get_generic_class (klass); + + if (index >= generic_class->context.class_inst->type_argc) + return NULL; + + return mono_class_from_mono_type (generic_class->context.class_inst->type_argv[index]); +} + +MONO_API guint32 +mono_unity_class_get_generic_argument_count (MonoClass* klass) +{ + if (!mono_class_is_ginst (klass)) + return NULL; + + MonoGenericClass* generic_class = mono_class_get_generic_class (klass); + + return generic_class->context.class_inst->type_argc; +} + +MONO_API MonoClass* +mono_unity_class_get(MonoImage* image, guint32 type_token) +{ + // Unity expects to try to get classes that don't exist, and + // have a value of NULL returned. So eat the error message. + MonoError unused; + MonoClass* klass= mono_class_get_checked(image, type_token, &unused); + mono_error_cleanup(&unused); + return klass; +} + +MONO_API gpointer +mono_unity_alloc(gsize size) +{ + return g_malloc(size); +} + + +MONO_API void +mono_unity_thread_fast_attach (MonoDomain *domain) +{ + MonoInternalThread *thread; + + g_assert (domain); + g_assert (domain != mono_get_root_domain ()); + + thread = mono_thread_internal_current (); + g_assert (thread); + + mono_thread_push_appdomain_ref (domain); + g_assert (mono_domain_set (domain, FALSE)); + + //mono_profiler_thread_fast_attach (thread->tid); +} + +MONO_API void +mono_unity_thread_fast_detach () +{ + MonoInternalThread *thread; + MonoDomain *current_domain; + + thread = mono_thread_internal_current (); + g_assert (thread); + + current_domain = mono_domain_get (); + + g_assert (current_domain); + g_assert (current_domain != mono_get_root_domain ()); + + //mono_profiler_thread_fast_detach (thread->tid); + + // Migrating to the root domain and popping the domain reference allows + // the thread to stay alive and keep running while the domain can be unloaded + g_assert (mono_domain_set (mono_get_root_domain (), FALSE)); + mono_thread_pop_appdomain_ref (); +} + +// hack, FIXME jon + +/* +size_t RemapPathFunction (const char* path, char* buffer, size_t buffer_len) + path = original path + buffer = provided buffer to fill out + buffer_len = byte size of buffer (above) + return value = buffer size needed, incl. terminating 0 + * may be called with buffer = null / buffer_len = 0, or a shorter-than-necessary-buffer. + * return value is always the size _needed_; not the size written. + * terminating zero should always be written. + * if buffer_len is less than needed, buffer content is undefined + * if return value is 0 no remapping is needed / available +*/ +static RemapPathFunction g_RemapPathFunc = NULL; + +void +mono_unity_register_path_remapper (RemapPathFunction func) +{ + g_RemapPathFunc = func; +} + +/* calls remapper function if registered; allocates memory if remapping is available */ +static inline size_t +call_remapper(const char* path, char** buf) +{ + size_t len; + + if (!g_RemapPathFunc) + return 0; + + *buf = NULL; + len = g_RemapPathFunc(path, *buf, 0); + + if (len == 0) + return 0; + + *buf = g_new (char, len); + g_RemapPathFunc(path, *buf, len); + + return len; +} + +MonoBoolean +ves_icall_System_IO_MonoIO_RemapPath (MonoString *path, MonoString **new_path) +{ + MonoError error; + const gunichar2* path_remapped; + + if (!g_RemapPathFunc) + return 0; + + path_remapped = mono_unity_remap_path_utf16 (mono_string_chars (path)); + + if (!path_remapped) + return FALSE; + + mono_gc_wbarrier_generic_store (new_path, (MonoObject*)mono_string_from_utf16_checked (path_remapped, &error)); + + g_free (path_remapped); + + mono_error_set_pending_exception (&error); + + return TRUE; +} + +const char* +mono_unity_remap_path (const char* path) +{ + const char* path_remap = NULL; + call_remapper (path, &path_remap); + + return path_remap; +} + +const gunichar2* +mono_unity_remap_path_utf16 (const gunichar2* path) +{ + const gunichar2* path_remap = NULL; + char * utf8_path; + char * buf; + char * path_end; + size_t len; + + if (!g_RemapPathFunc) + return path_remap; + + utf8_path = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL); + len = call_remapper (utf8_path, &buf); + if (len == 0) + { + g_free (utf8_path); + return path_remap; + } + + path_end = memchr (buf, '\0', len); + len = path_end ? (size_t)(path_end - buf) : len; + + path_remap = g_utf8_to_utf16 (buf, len, NULL, NULL, NULL); + + g_free (utf8_path); + g_free (buf); + + return path_remap; +} + +MonoMethod* +mono_method_get_method_definition (MonoMethod *method) +{ + while (method->is_inflated) + method = ((MonoMethodInflated*)method)->declaring; + return method; +} + +gboolean mono_allow_gc_aware_layout = TRUE; + +void +mono_class_set_allow_gc_aware_layout(mono_bool allow) +{ + mono_allow_gc_aware_layout = allow; +} + +static mono_bool enable_handler_block_guards = TRUE; + +void +mono_unity_set_enable_handler_block_guards (mono_bool allow) +{ + enable_handler_block_guards = allow; +} + +mono_bool +mono_unity_get_enable_handler_block_guards (void) +{ + return enable_handler_block_guards; +} + +//helper structures for VM information extraction functions +typedef struct { + GFunc callback; + gpointer user_data; +} execution_ctx; + +typedef struct +{ + gpointer start; + size_t size; +} mono_heap_chunk; + +// class metadata memory +static void +handle_mem_pool_chunk(gpointer chunkStart, gpointer chunkEnd, gpointer userData) +{ + mono_heap_chunk chunk; + chunk.start = chunkStart; + chunk.size = (uint8_t *)chunkEnd - (uint8_t *)chunkStart; + + execution_ctx *ctx = (execution_ctx *)userData; + ctx->callback(&chunk, ctx->user_data); +} + +static void +handle_image_set_mem_pool(MonoImageSet *imageSet, gpointer user_data) +{ + mono_mempool_foreach_block(imageSet->mempool, handle_mem_pool_chunk, user_data); +} + +MONO_API void +mono_unity_image_set_mempool_chunk_foreach(GFunc callback, gpointer user_data) +{ + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + + mono_metadata_image_set_foreach(handle_image_set_mem_pool, &ctx); +} + +MONO_API void +mono_unity_domain_mempool_chunk_foreach(MonoDomain *domain, GFunc callback, gpointer user_data) +{ + mono_domain_lock(domain); + + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + mono_mempool_foreach_block(domain->mp, handle_mem_pool_chunk, &ctx); + + mono_domain_unlock(domain); +} + +MONO_API void +mono_unity_root_domain_mempool_chunk_foreach(GFunc callback, gpointer user_data) +{ + MonoDomain *domain = mono_get_root_domain(); + mono_domain_lock(domain); + + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + mono_mempool_foreach_block(domain->mp, handle_mem_pool_chunk, &ctx); + + mono_domain_unlock(domain); +} + +MONO_API void +mono_unity_assembly_mempool_chunk_foreach(MonoAssembly *assembly, GFunc callback, gpointer user_data) +{ + MonoImage *image = assembly->image; + mono_image_lock(image); + + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + mono_mempool_foreach_block(image->mempool, handle_mem_pool_chunk, &ctx); + + if (image->module_count > 0) { + guint32 i; + + for (i = 0; i < image->module_count; ++i) { + MonoImage *moduleImage = image->modules[i]; + + if (moduleImage) { + mono_mempool_foreach_block(moduleImage->mempool, handle_mem_pool_chunk, &ctx); + } + } + } + mono_image_unlock(image); +} + +// class metadata + +static char * +mono_identifier_escape_type_append(char *bufferPtr, const char *identifier) +{ + for (const char *s = identifier; *s != 0; ++s) { + switch (*s) { + case ',': + case '+': + case '&': + case '*': + case '[': + case ']': + case '\\': + *bufferPtr++ = '\\'; + *bufferPtr++ = *s; + + return bufferPtr; + default: + *bufferPtr++ = *s; + + return bufferPtr; + } + } + + return bufferPtr; +} + +enum { + //max digits on uint16 is 5(used to convert the number of generic args) + max 3 other slots taken; + kNameChunkBufferSize = 8 +}; + +static inline char * +flush_name_buffer(char *buffer, GFunc callback, void *userData) +{ + callback(buffer, userData); + memset(buffer, 0x00, kNameChunkBufferSize); + + return buffer; +} + +static void +mono_unity_type_get_name_foreach_name_chunk_recurse(MonoType *type, gboolean is_recursed, MonoTypeNameFormat format, GFunc nameChunkReport, void *userData) +{ + MonoClass *klass = NULL; + char buffer[kNameChunkBufferSize + 1]; //null terminate the buffer + memset(buffer, 0x00, kNameChunkBufferSize + 1); + char *bufferPtr = buffer; + char *bufferIter = buffer; + + switch (type->type) { + case MONO_TYPE_ARRAY: { + int i, rank = type->data.array->rank; + MonoTypeNameFormat nested_format; + + nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; + + mono_unity_type_get_name_foreach_name_chunk_recurse( + &type->data.array->eklass->byval_arg, FALSE, nested_format, nameChunkReport, userData); + + *bufferIter++ = '['; + + if (rank == 1) { + *bufferIter++ = '*'; + } + + for (i = 1; i < rank; i++) { + + *bufferIter++ = ','; + + if (kNameChunkBufferSize - (bufferIter - bufferPtr) < 2) { + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + } + } + + *bufferIter++ = ']'; + + if (type->byref) { + *bufferIter++ = '&'; + } + + nameChunkReport(bufferPtr, userData); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { + MonoClass *klass = mono_class_from_mono_type(type); + MonoImage *klassImg = mono_class_get_image(klass); + char *imgName = mono_image_get_name(klassImg); + nameChunkReport(imgName, userData); + } + break; + } + case MONO_TYPE_SZARRAY: { + MonoTypeNameFormat nested_format; + + nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; + + mono_unity_type_get_name_foreach_name_chunk_recurse(&type->data.klass->byval_arg, FALSE, nested_format, nameChunkReport, userData); + + *bufferIter++ = '['; + *bufferIter++ = ']'; + + if (type->byref) + *bufferIter++ = '&'; + + nameChunkReport(bufferPtr, userData); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { + MonoClass *klass = mono_class_from_mono_type(type); + MonoImage *klassImg = mono_class_get_image(klass); + char *imgName = mono_image_get_name(klassImg); + nameChunkReport(imgName, userData); + } + break; + } + case MONO_TYPE_PTR: { + MonoTypeNameFormat nested_format; + + nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ? MONO_TYPE_NAME_FORMAT_FULL_NAME : format; + + mono_unity_type_get_name_foreach_name_chunk_recurse(type->data.type, FALSE, nested_format, nameChunkReport, userData); + *bufferIter++ = '*'; + + if (type->byref) + *bufferIter++ = '&'; + + nameChunkReport(bufferPtr, userData); + + if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) { + MonoClass *klass = mono_class_from_mono_type(type); + MonoImage *klassImg = mono_class_get_image(klass); + char *imgName = mono_image_get_name(klassImg); + nameChunkReport(imgName, userData); + } + break; + } + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + if (!mono_generic_param_info(type->data.generic_param)) { + + if (type->type == MONO_TYPE_VAR) { + *bufferIter++ = '!'; + } + else { + *bufferIter++ = '!'; + *bufferIter++ = '!'; + } + sprintf(bufferIter, "%d", type->data.generic_param->num); + } + else + nameChunkReport(mono_generic_param_info(type->data.generic_param)->name, userData); + + if (type->byref) + *bufferIter++ = '&'; + + nameChunkReport(bufferPtr, userData); + break; + default: + klass = mono_class_from_mono_type(type); + if (klass->nested_in) { + mono_unity_type_get_name_foreach_name_chunk_recurse(&klass->nested_in->byval_arg, TRUE, format, nameChunkReport, userData); + if (format == MONO_TYPE_NAME_FORMAT_IL) + *bufferIter++ = '.'; + else + *bufferIter++ = '+'; + } + else if (*klass->name_space) { + if (format == MONO_TYPE_NAME_FORMAT_IL) + nameChunkReport(klass->name_space, userData); + else + bufferIter = mono_identifier_escape_type_append(bufferIter, klass->name_space); + + *bufferIter++ = '.'; + } + + if (format == MONO_TYPE_NAME_FORMAT_IL) { + char *s = strchr(klass->name, '`'); + int len = s ? s - klass->name : strlen(klass->name); + + for (int i = 0; i < len; ++i) { + + *bufferIter++ = *(klass->name + i); + if (kNameChunkBufferSize - (bufferIter - bufferPtr) == 0) { + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + } + } + + if (bufferPtr != bufferIter) { + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + } + + } + else { + bufferIter = mono_identifier_escape_type_append(bufferIter, klass->name); + } + + if (!is_recursed) { + if (bufferIter != bufferPtr) + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + + if (mono_class_is_ginst(klass)) { + MonoGenericClass *gclass = mono_class_get_generic_class(klass); + MonoGenericInst *inst = gclass->context.class_inst; + MonoTypeNameFormat nested_format; + int i; + + nested_format = format == MONO_TYPE_NAME_FORMAT_FULL_NAME ? MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : format; + + if (format == MONO_TYPE_NAME_FORMAT_IL) + *bufferIter++ = '<'; + else + *bufferIter++ = '['; + + for (i = 0; i < inst->type_argc; i++) { + MonoType *t = inst->type_argv[i]; + + if (i) + *bufferIter++ = ','; + if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) + *bufferIter++ = '['; + + //flush the buffer before recursing + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + mono_unity_type_get_name_foreach_name_chunk_recurse(inst->type_argv[i], FALSE, nested_format, nameChunkReport, userData); + + if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) + *bufferIter++ = ']'; + } + if (format == MONO_TYPE_NAME_FORMAT_IL) + *bufferIter++ = '>'; + else + *bufferIter++ = ']'; + } + else if (mono_class_is_gtd(klass) && + (format != MONO_TYPE_NAME_FORMAT_FULL_NAME) && + (format != MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)) { + int i; + + if (format == MONO_TYPE_NAME_FORMAT_IL) + *bufferIter++ = '<'; + else + *bufferIter++ = '['; + + bufferIter = flush_name_buffer(bufferPtr, nameChunkReport, userData); + + for (i = 0; i < mono_class_get_generic_container(klass)->type_argc; i++) { + if (i) + nameChunkReport(",", userData); + nameChunkReport(mono_generic_container_get_param_info(mono_class_get_generic_container(klass), i)->name, userData); + } + if (format == MONO_TYPE_NAME_FORMAT_IL) + *bufferIter++ = '>'; + else + *bufferIter++ = ']'; + } + + if (type->byref) + *bufferIter++ = '&'; + } + + if (bufferPtr != bufferIter) + nameChunkReport(bufferPtr, userData); + + if ((format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) && + (type->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR)) { + MonoImage *klassImg = mono_class_get_image(klass); + char *imgName = mono_image_get_name(klassImg); + nameChunkReport(imgName, userData); + } + break; + } +} + +/** + * mono_unity_type_get_name_full_chunked: + * \param type a type + * \reports chunks null terminated of a type's name via a callback. + */ + +MONO_API void +mono_unity_type_get_name_full_chunked(MonoType *type, GFunc chunkReportFunc, gpointer userData) +{ + mono_unity_type_get_name_foreach_name_chunk_recurse(type, FALSE, MONO_TYPE_NAME_FORMAT_IL, chunkReportFunc, userData); +} + +MONO_API gboolean +mono_unity_type_is_pointer_type(MonoType *type) +{ + return type->type == MONO_TYPE_PTR; +} + +MONO_API gboolean +mono_unity_type_is_static(MonoType *type) +{ + return (type->attrs & FIELD_ATTRIBUTE_STATIC) != 0; +} + +MONO_API MonoVTable * +mono_unity_class_try_get_vtable(MonoDomain *domain, MonoClass *klass) +{ + return mono_class_try_get_vtable(domain, klass); +} + +MONO_API uint32_t +mono_unity_class_get_data_size(MonoClass *klass) +{ + return mono_class_data_size(klass); +} + +MONO_API void * +mono_unity_vtable_get_static_field_data(MonoVTable *vTable) +{ + return mono_vtable_get_static_field_data(vTable); +} + +MONO_API gboolean +mono_unity_class_field_is_literal(MonoClassField *field) +{ + return (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0; +} + +// GC world control +MONO_API void +mono_unity_stop_gc_world() +{ +#if HAVE_BDWGC_GC + GC_stop_world_external(); +#else + g_assert_not_reached(); +#endif +} + +MONO_API void +mono_unity_start_gc_world() +{ +#if HAVE_BDWGC_GC + GC_start_world_external(); +#else + g_assert_not_reached(); +#endif +} + + +//GC memory +static void +handle_gc_heap_chunk(void *userdata, gpointer chunk_start, gpointer chunk_end) +{ + execution_ctx *ctx = (execution_ctx *)userdata; + mono_heap_chunk chunk; + chunk.start = chunk_start; + chunk.size = (uint8_t *)chunk_end - (uint8_t *)chunk_start; + ctx->callback(&chunk, ctx->user_data); +} + +MONO_API void +mono_unity_gc_heap_foreach(GFunc callback, gpointer user_data) +{ +#if HAVE_BDWGC_GC + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + + GC_foreach_heap_section(&ctx, handle_gc_heap_chunk); +#else + g_assert_not_reached(); +#endif +} + +//GC handles +static void +handle_gc_handle(gpointer handle_target, gpointer handle_report_callback) +{ + execution_ctx *ctx = (execution_ctx *)handle_report_callback; + ctx->callback(handle_target, ctx->user_data); +} + +MONO_API void +mono_unity_gc_handles_foreach_get_target(GFunc callback, gpointer user_data) +{ +#if HAVE_BDWGC_GC + execution_ctx ctx; + ctx.callback = callback; + ctx.user_data = user_data; + mono_gc_strong_handle_foreach(handle_gc_handle, &ctx); +#else + g_assert_not_reached(); +#endif +} + +// VM runtime info +MONO_API uint32_t +mono_unity_object_header_size() +{ + return (uint32_t)(sizeof(MonoObject)); +} + +MONO_API uint32_t +mono_unity_array_object_header_size() +{ + return offsetof(MonoArray, vector); +} + +MONO_API uint32_t +mono_unity_offset_of_array_length_in_array_object_header() +{ + return offsetof(MonoArray, max_length); +} + +MONO_API uint32_t +mono_unity_offset_of_array_bounds_in_array_object_header() +{ + return offsetof(MonoArray, bounds); +} + +MONO_API uint32_t +mono_unity_allocation_granularity() +{ + return (uint32_t)(2 * sizeof(void *)); +} \ No newline at end of file diff --git a/mono/metadata/unity-utils.h b/mono/metadata/unity-utils.h new file mode 100644 index 000000000000..8fcb18736c04 --- /dev/null +++ b/mono/metadata/unity-utils.h @@ -0,0 +1,218 @@ +#ifndef __UNITY_MONO_UTILS_H +#define __UNITY_MONO_UTILS_H + +#include +#include +#include +#include + +typedef void(*vprintf_func)(const char* msg, va_list args); +typedef struct { + void* (*malloc_func)(size_t size); + void(*free_func)(void *ptr); + void* (*calloc_func)(size_t nmemb, size_t size); + void* (*realloc_func)(void *ptr, size_t size); +} MonoMemoryCallbacks; + +/** + * Custom exit function, called instead of system exit() + */ +void unity_mono_exit( int code ); + +/** + * Closes redirected output files. + */ +void unity_mono_close_output(void); + +extern MonoString* mono_unity_get_embeddinghostname(void); + +#ifdef WIN32 +FILE* unity_fopen( const char *name, const char *mode ); +#endif + +extern gboolean mono_unity_socket_security_enabled_get (void); +MONO_API extern void mono_unity_socket_security_enabled_set (gboolean enabled); +MONO_API void mono_unity_set_vprintf_func(vprintf_func func); + + +void unity_mono_install_memory_callbacks(MonoMemoryCallbacks* callbacks); + +MONO_API gboolean +unity_mono_method_is_generic (MonoMethod* method); + +typedef const char*(*UnityFindPluginCallback)(const char*); + +MONO_API void +mono_set_find_plugin_callback(UnityFindPluginCallback find); + +MONO_API UnityFindPluginCallback +mono_get_find_plugin_callback(); + +//object +void mono_unity_object_init(void* obj, MonoClass* klass); +MonoObject* mono_unity_object_isinst_sealed(MonoObject* obj, MonoClass* targetType); +void mono_unity_object_unbox_nullable(MonoObject* obj, MonoClass* nullableArgumentClass, void* storage); +MonoClass* mono_unity_object_get_class(MonoObject *obj); +MonoObject* mono_unity_object_compare_exchange(MonoObject **location, MonoObject *value, MonoObject *comparand); +MonoObject* mono_unity_object_exchange(MonoObject **location, MonoObject *value); +gboolean mono_unity_object_check_box_cast(MonoObject *obj, MonoClass *klass); + +//class +const char* mono_unity_class_get_image_name(MonoClass* klass); +MonoClass* mono_unity_class_get_generic_definition(MonoClass* klass); +MonoClass* mono_unity_class_inflate_generic_class(MonoClass *gklass, MonoGenericContext *context); +gboolean mono_unity_class_has_parent_unsafe(MonoClass *klass, MonoClass *parent); +MonoAssembly* mono_unity_class_get_assembly(MonoClass *klass); +gboolean mono_unity_class_is_array(MonoClass *klass); +MonoClass* mono_unity_class_get_element_class(MonoClass *klass); +gboolean mono_unity_class_is_delegate(MonoClass *klass); +int mono_unity_class_get_instance_size(MonoClass *klass); +MonoClass* mono_unity_class_get_castclass(MonoClass *klass); +guint32 mono_unity_class_get_native_size(MonoClass* klass); +MonoBoolean mono_unity_class_is_string(MonoClass* klass); +MonoBoolean mono_unity_class_is_class_type(MonoClass* klass); +MONO_API gboolean mono_class_is_generic(MonoClass *klass); +MONO_API gboolean mono_class_is_blittable(MonoClass *klass); +MONO_API gboolean mono_class_is_inflated(MonoClass *klass); +gboolean mono_unity_class_has_cctor(MonoClass *klass); + +//method +MonoMethod* mono_unity_method_get_generic_definition(MonoMethod* method); +MonoReflectionMethod* mono_unity_method_get_object(MonoMethod *method); +MonoMethod* mono_unity_method_alloc0(MonoClass* klass); +MonoMethod* mono_unity_method_delegate_invoke_wrapper(MonoClass* klass); +gboolean mono_unity_method_is_static(MonoMethod *method); +MonoClass* mono_unity_method_get_class(const MonoMethod *method); + +#ifdef IL2CPP_ON_MONO +void* mono_unity_method_get_method_pointer(MonoMethod* method); +void mono_unity_method_set_method_pointer(MonoMethod* method, void *p); +void* mono_unity_method_get_invoke_pointer(MonoMethod* method); +void mono_unity_method_set_invoke_pointer(MonoMethod* method, void *p); +#endif + +const char* mono_unity_method_get_name(const MonoMethod *method); +guint64 mono_unity_method_get_hash(MonoMethod *method, gboolean inflate); +MonoMethod* mono_unity_method_get_aot_array_helper_from_wrapper(MonoMethod *method); +MonoObject* mono_unity_method_convert_return_type_if_needed(MonoMethod *method, void *value); +MONO_API gboolean unity_mono_method_is_inflated(MonoMethod* method); +guint32 mono_unity_method_get_token(MonoMethod *method); + +//domain +void mono_unity_domain_install_finalize_runtime_invoke(MonoDomain* domain, RuntimeInvokeFunction callback); +void mono_unity_domain_install_capture_context_runtime_invoke(MonoDomain* domain, RuntimeInvokeFunction callback); +void mono_unity_domain_install_capture_context_method(MonoDomain* domain, void* callback); +MONO_API void mono_unity_domain_unload (MonoDomain *domain, MonoUnityExceptionFunc callback); + +//array +int mono_unity_array_get_element_size(MonoArray *arr); +MonoClass* mono_unity_array_get_class(MonoArray *arr); +mono_array_size_t mono_unity_array_get_max_length(MonoArray *arr); + +//type +gboolean mono_unity_type_is_generic_instance(MonoType *type); +MonoGenericClass* mono_unity_type_get_generic_class(MonoType *type); +gboolean mono_unity_type_is_enum_type(MonoType *type); +gboolean mono_unity_type_is_boolean(MonoType *type); +MonoClass* mono_unity_type_get_element_class(MonoType *type); //only safe to call when the type has a defined klass data element +guint64 mono_unity_type_get_hash(MonoType *type, gboolean inflate); + +//generic class +MonoGenericContext mono_unity_generic_class_get_context(MonoGenericClass *klass); +MonoClass* mono_unity_generic_class_get_container_class(MonoGenericClass *klass); + +//method signature +MonoClass* mono_unity_signature_get_class_for_param(MonoMethodSignature *sig, int index); +int mono_unity_signature_num_parameters(MonoMethodSignature *sig); +gboolean mono_unity_signature_param_is_byref(MonoMethodSignature *sig, int index); + +//generic inst +guint mono_unity_generic_inst_get_type_argc(MonoGenericInst *inst); +MonoType* mono_unity_generic_inst_get_type_argument(MonoGenericInst *inst, int index); + +//exception +MonoString* mono_unity_exception_get_message(MonoException *exc); +MonoString* mono_unity_exception_get_stack_trace(MonoException *exc); +MonoObject* mono_unity_exception_get_inner_exception(MonoException *exc); +MonoArray* mono_unity_exception_get_trace_ips(MonoException *exc); +void mono_unity_exception_set_trace_ips(MonoException *exc, MonoArray *ips); +MonoException* mono_unity_exception_get_marshal_directive(const char* msg); +MONO_API MonoException* mono_unity_error_convert_to_exception(MonoError *error); + +//defaults +MonoClass* mono_unity_defaults_get_int_class(); +MonoClass* mono_unity_defaults_get_stack_frame_class(); +MonoClass* mono_unity_defaults_get_int32_class(); +MonoClass* mono_unity_defaults_get_char_class(); +MonoClass* mono_unity_defaults_get_delegate_class(); +MonoClass* mono_unity_defaults_get_byte_class(); + +//unitytls +typedef struct unitytls_interface_struct unitytls_interface_struct; +MONO_API unitytls_interface_struct* mono_unity_get_unitytls_interface(); +MONO_API void mono_unity_install_unitytls_interface(unitytls_interface_struct* callbacks); + +// gc +typedef enum +{ + MONO_GC_MODE_DISABLED = 0, + MONO_GC_MODE_ENABLED = 1, + MONO_GC_MODE_MANUAL = 2 +} MonoGCMode; + +MONO_API void mono_unity_gc_set_mode(MonoGCMode mode); + +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API void mono_unity_gc_enable(); +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API void mono_unity_gc_disable(); +// Deprecated. Remove when Unity has switched to mono_unity_gc_set_mode +MONO_API int mono_unity_gc_is_disabled(); + + +//misc +MonoAssembly* mono_unity_assembly_get_mscorlib(); +MonoImage* mono_unity_image_get_mscorlib(); +MonoClass* mono_unity_generic_container_get_parameter_class(MonoGenericContainer* generic_container, gint index); +MonoString* mono_unity_string_append_assembly_name_if_necessary(MonoString* typeName, const char* assemblyName); +void mono_unity_memory_barrier(); +MonoException* mono_unity_thread_check_exception(); +MonoObject* mono_unity_delegate_get_target(MonoDelegate *delegate); +gchar* mono_unity_get_runtime_build_info(const char *date, const char *time); +void* mono_unity_get_field_address(MonoObject *obj, MonoVTable *vt, MonoClassField *field); +gboolean mono_unity_thread_state_init_from_handle(MonoThreadUnwindState *tctx, MonoThreadInfo *info, void* fixme); +void mono_unity_stackframe_set_method(MonoStackFrame *sf, MonoMethod *method); +MonoType* mono_unity_reflection_type_get_type(MonoReflectionType *type); +MONO_API void mono_unity_set_data_dir(const char* dir); +MONO_API char* mono_unity_get_data_dir(); +MONO_API MonoClass* mono_unity_class_get(MonoImage* image, guint32 type_token); +MONO_API gpointer mono_unity_alloc(gsize size); +MONO_API void mono_unity_g_free (void *ptr); + +MONO_API MonoClass* mono_custom_attrs_get_attrs (MonoCustomAttrInfo *ainfo, gpointer *iter); + +typedef size_t (*RemapPathFunction)(const char* path, char* buffer, size_t buffer_len); +MONO_API void mono_unity_register_path_remapper (RemapPathFunction func); + +const char* +mono_unity_remap_path (const char* path); + +const gunichar2* +mono_unity_remap_path_utf16 (const gunichar2* path); + +MonoBoolean +ves_icall_System_IO_MonoIO_RemapPath (MonoString *path, MonoString **new_path); + +MonoMethod* +mono_method_get_method_definition(MonoMethod *method); + +void +mono_class_set_allow_gc_aware_layout(mono_bool allow); + +MONO_API void +mono_unity_set_enable_handler_block_guards (mono_bool allow); + +mono_bool +mono_unity_get_enable_handler_block_guards (void); + +#endif diff --git a/mono/metadata/verify-internals.h b/mono/metadata/verify-internals.h index 821de196bdd6..b7aa55a6f421 100644 --- a/mono/metadata/verify-internals.h +++ b/mono/metadata/verify-internals.h @@ -14,17 +14,19 @@ G_BEGIN_DECLS typedef enum { MONO_VERIFIER_MODE_OFF, + MONO_VERIFIER_PE_ONLY, MONO_VERIFIER_MODE_VALID, MONO_VERIFIER_MODE_VERIFIABLE, MONO_VERIFIER_MODE_STRICT } MiniVerifierMode; -void mono_verifier_set_mode (MiniVerifierMode mode); +UNITY_MONO_API void mono_verifier_set_mode (MiniVerifierMode mode); void mono_verifier_enable_verify_all (void); gboolean mono_verifier_is_enabled_for_image (MonoImage *image); gboolean mono_verifier_is_enabled_for_method (MonoMethod *method); gboolean mono_verifier_is_enabled_for_class (MonoClass *klass); +gboolean mono_verifier_is_enabled_for_pe_only (); gboolean mono_verifier_is_method_full_trust (MonoMethod *method); gboolean mono_verifier_is_class_full_trust (MonoClass *klass); diff --git a/mono/metadata/verify.c b/mono/metadata/verify.c index e509ed680c13..9bec0667e6f6 100644 --- a/mono/metadata/verify.c +++ b/mono/metadata/verify.c @@ -6134,13 +6134,19 @@ mono_verifier_is_enabled_for_method (MonoMethod *method) gboolean mono_verifier_is_enabled_for_class (MonoClass *klass) { - return verify_all || (verifier_mode > MONO_VERIFIER_MODE_OFF && !(klass->image->assembly && klass->image->assembly->in_gac) && klass->image != mono_defaults.corlib); + return verify_all || (verifier_mode > MONO_VERIFIER_PE_ONLY && !(klass->image->assembly && klass->image->assembly->in_gac) && klass->image != mono_defaults.corlib); } gboolean mono_verifier_is_enabled_for_image (MonoImage *image) { - return verify_all || verifier_mode > MONO_VERIFIER_MODE_OFF; + return verify_all || verifier_mode > MONO_VERIFIER_PE_ONLY; +} + +gboolean +mono_verifier_is_enabled_for_pe_only () +{ + return verify_all || verifier_mode == MONO_VERIFIER_PE_ONLY; } /* diff --git a/mono/metadata/w32error-unity.c b/mono/metadata/w32error-unity.c new file mode 100644 index 000000000000..5617f52f549f --- /dev/null +++ b/mono/metadata/w32error-unity.c @@ -0,0 +1,27 @@ +#include +#include "w32error.h" + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +guint32 +mono_w32error_get_last (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +void +mono_w32error_set_last (guint32 error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +guint32 +mono_w32error_unix_to_win32 (guint32 error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/metadata/w32event-unity.c b/mono/metadata/w32event-unity.c new file mode 100644 index 000000000000..e1a570ba0f8a --- /dev/null +++ b/mono/metadata/w32event-unity.c @@ -0,0 +1,84 @@ +#include "w32event.h" +#include "Handle-c-api.h" +#include "Event-c-api.h" +#include "Error-c-api.h" + +void +mono_w32event_init (void) +{ +} + +gpointer +mono_w32event_create (gboolean manual, gboolean initial) +{ + UnityPalEvent* event = UnityPalEventNew(manual, initial); + return UnityPalEventHandleNew(event); +} + +gboolean +mono_w32event_close (gpointer handle) +{ + UnityPalHandleDestroy(handle); + return TRUE; +} + +void +mono_w32event_set (gpointer handle) +{ + UnityPalEventSet(UnityPalEventHandleGet(handle)); +} + +void +mono_w32event_reset (gpointer handle) +{ + UnityPalEventReset(UnityPalEventHandleGet(handle)); +} + +gpointer +ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoStringHandle name, gint32 *err, MonoError *error) +{ + error_init (error); + if (!MONO_HANDLE_IS_NULL (name)) + { + g_assertion_message("Named events are not supported by the Unity platform."); + return NULL; + } + + UnityPalEvent* event = UnityPalEventNew(manual, initial); + *err = UnityPalGetLastError(); + return UnityPalEventHandleNew(event); +} + +gboolean +ves_icall_System_Threading_Events_SetEvent_internal (gpointer handle) +{ + UnityPalErrorCode result = UnityPalEventSet(UnityPalEventHandleGet(handle)); + return UnityPalSuccess(result); +} + +gboolean +ves_icall_System_Threading_Events_ResetEvent_internal (gpointer handle) +{ + UnityPalErrorCode result = UnityPalEventReset(UnityPalEventHandleGet(handle)); + return UnityPalSuccess(result); +} + +void +ves_icall_System_Threading_Events_CloseEvent_internal (gpointer handle) +{ + UnityPalHandleDestroy(handle); +} + +gpointer +ves_icall_System_Threading_Events_OpenEvent_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error) +{ + g_assertion_message("Named events are not supported by the Unity platform."); + return NULL; +} + +MonoW32HandleNamespace* +mono_w32event_get_namespace (MonoW32HandleNamedEvent *event) +{ + g_assertion_message("Named events are not supported by the Unity platform."); + return NULL; +} diff --git a/mono/metadata/w32file-unity.c b/mono/metadata/w32file-unity.c new file mode 100644 index 000000000000..786c41111bd4 --- /dev/null +++ b/mono/metadata/w32file-unity.c @@ -0,0 +1,554 @@ +#include +#include + + + +#include "Directory-c-api.h" +#include "File-c-api.h" +#include "w32error.h" +#include "w32file.h" +#include "utils/strenc.h" +#include + + +#ifdef HOST_WIN32 + +gunichar2 +ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () +{ + return (gunichar2) ':'; /* colon */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () +{ + return (gunichar2) '\\'; /* backslash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () +{ + return (gunichar2) '/'; /* forward slash */ +} + +gunichar2 +ves_icall_System_IO_MonoIO_get_PathSeparator () +{ + return (gunichar2) ';'; /* semicolon */ +} + +void ves_icall_System_IO_MonoIO_DumpHandles (void) +{ + return; +} +#endif /* HOST_WIN32 */ + +gpointer +mono_w32file_create(const gunichar2 *name, guint32 fileaccess, guint32 sharemode, guint32 createmode, guint32 attrs) +{ + int error = 0; + gpointer handle; + gchar* palPath = mono_unicode_to_external(name); + handle = UnityPalOpen(palPath, (int) createmode, (int) fileaccess, (int) sharemode, attrs, &error); + mono_w32error_set_last(error); + g_free(palPath); + + if (handle == NULL) + return INVALID_HANDLE_VALUE; + + return handle; +} + +gboolean +mono_w32file_close (gpointer handle) +{ + if (handle == NULL) + return FALSE; + + int error = 0; + gboolean result = UnityPalClose(handle, &error); + mono_w32error_set_last(error); + + return result; +} + +gboolean +mono_w32file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +{ + int error = 0; + + *bytesread = UnityPalRead(handle, buffer, numbytes, &error); + mono_w32error_set_last(error); + + return TRUE; +} + +gboolean +mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) +{ + int error = 0; + + *byteswritten = UnityPalWrite(handle, buffer, numbytes, &error); + mono_w32error_set_last(error); + + return (*byteswritten > 0); +} + +gboolean +mono_w32file_flush (gpointer handle) +{ + int error = 0; + + gboolean result = UnityPalFlush(handle, &error); + mono_w32error_set_last(error); + + return result; +} + +gboolean +mono_w32file_truncate (gpointer handle) +{ + int error = 0; + + gboolean result = UnityPalTruncate(handle, &error); + mono_w32error_set_last(error); + + return result; +} + +guint32 +mono_w32file_seek (gpointer handle, gint32 movedistance, gint32 *highmovedistance, guint32 method) +{ + int error = 0; + + int32_t result = UnityPalSeek(handle, movedistance, 0, &error); + mono_w32error_set_last(error); + + return result; +} + +gint +mono_w32file_get_type (gpointer handle) +{ + if (handle == NULL) + return 0; + + return UnityPalGetFileType(handle); +} + +gboolean +mono_w32file_get_times (gpointer handle, FILETIME *create_time, FILETIME *access_time, FILETIME *write_time) +{ + /* Not Supported in UnityPAL */ + g_assert_not_reached(); +} + +gboolean +mono_w32file_set_times (gpointer handle, const FILETIME *create_time, const FILETIME *access_time, const FILETIME *write_time) +{ + int error = 0; + + gboolean result = UnityPalSetFileTime(handle, create_time, access_time, write_time, &error); + mono_w32error_set_last(error); + + return result; +} + +gpointer +mono_w32file_find_first (const gunichar2 *pattern, WIN32_FIND_DATA *find_data) +{ + gchar* palPath = mono_unicode_to_external(pattern); + UnityPalFindHandle* findHandle = UnityPalDirectoryFindHandleNew(palPath); + int32_t resultAttributes = 0; + + int32_t result = 0; + const char* filename; + + result = UnityPalDirectoryFindFirstFile(findHandle, palPath, &filename, &resultAttributes); + + if (result != 0) + { + mono_w32error_set_last(result); + return INVALID_HANDLE_VALUE; + } + + find_data->dwFileAttributes = resultAttributes; + + gunichar2 *utf16_basename; + glong bytes; + utf16_basename = g_utf8_to_utf16 (filename, -1, NULL, &bytes, NULL); + + /* this next section of memset and memcpy is code from mono, the cFileName field is + gunichar2 cFileName [MAX_PATH]. + Notes from mono: + Truncating a utf16 string like this might leave the last + gchar incomplete + utf16 is 2 * utf8 + */ + bytes *= 2; + memset (find_data->cFileName, '\0', (MAX_PATH * 2)); + memcpy (find_data->cFileName, utf16_basename, bytes < (MAX_PATH * 2) - 2 ? bytes : (MAX_PATH * 2) - 2); + + g_free(filename); + g_free(palPath); + g_free(utf16_basename); + + find_data->dwReserved0 = 0; + find_data->dwReserved1 = 0; + + find_data->cAlternateFileName [0] = 0; + + return findHandle; +} + +gboolean +mono_w32file_find_next (gpointer handle, WIN32_FIND_DATA *find_data) +{ + + int32_t resultAttributes = 0; + int32_t result; + const char* filename; + + result = UnityPalDirectoryFindNextFile(handle, &filename, &resultAttributes); + + find_data->dwFileAttributes = resultAttributes; + gunichar2 *utf16_basename; + glong bytes; + utf16_basename = g_utf8_to_utf16 (filename, -1, NULL, &bytes, NULL); + bytes *= 2; + + memset (find_data->cFileName, '\0', (MAX_PATH * 2)); + memcpy (find_data->cFileName, utf16_basename, bytes < (MAX_PATH * 2) - 2 ? bytes : (MAX_PATH * 2) - 2); + + g_free(filename); + g_free(utf16_basename); + + find_data->dwReserved0 = 0; + find_data->dwReserved1 = 0; + + find_data->cAlternateFileName [0] = 0; + + return (result == 0); +} + +gboolean +mono_w32file_find_close (gpointer handle) +{ + gboolean result = UnityPalDirectoryCloseOSHandle(handle); + UnityPalDirectoryFindHandleDelete(handle); + + return result; +} + +gboolean +mono_w32file_create_directory (const gunichar2 *name) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external(name); + gboolean result = UnityPalDirectoryCreate(palPath, &error); + mono_w32error_set_last(error); + g_free(palPath); + + return result; +} + +guint32 +mono_w32file_get_attributes (const gunichar2 *name) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external(name); + guint32 result = UnityPalGetFileAttributes(palPath, &error); + mono_w32error_set_last(error); + g_free(palPath); + + return result; +} + +gboolean +mono_w32file_get_attributes_ex (const gunichar2 *name, MonoIOStat *stat) +{ + gboolean result; + UnityPalFileStat palStat; + int error = 0; + + gchar* palPath = mono_unicode_to_external(name); + result = UnityPalGetFileStat(palPath, &palStat, &error); + mono_w32error_set_last(error); + + if (result) { + stat->attributes = palStat.attributes; + stat->creation_time = palStat.creation_time; + stat->last_access_time = palStat.last_access_time; + stat->last_write_time = palStat.last_write_time; + stat->length = palStat.length; + } + g_free(palPath); + + return result; +} + +gboolean +mono_w32file_set_attributes (const gunichar2 *name, guint32 attrs) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external(name); + gboolean result = UnityPalSetFileAttributes(palPath, attrs, &error); + mono_w32error_set_last(error); + g_free(palPath); + + return result; +} + +gboolean +mono_w32file_create_pipe (gpointer *readpipe, gpointer *writepipe, guint32 size) +{ + return UnityPalCreatePipe(*readpipe, *writepipe); +} + +gboolean +mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_bytes_avail, guint64 *total_number_of_bytes, guint64 *total_number_of_free_bytes) +{ + g_assert_not_reached(); + return FALSE; +} + +gboolean +mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize) +{ + g_assert_not_reached(); + return FALSE; +} + +gboolean +mono_w32file_move (const gunichar2 *path, const gunichar2 *dest, gint32 *error) +{ + gboolean result; + *error = 0; + MONO_ENTER_GC_SAFE; + + gchar* palPath = mono_unicode_to_external(path); + gchar* palDest = mono_unicode_to_external(dest); + result = UnityPalMoveFile(palPath, palDest, error); + mono_w32error_set_last(*error); + g_free(palPath); + g_free(palDest); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_replace (const gunichar2 *destination_file_name, const gunichar2 *source_file_name, const gunichar2 *destination_backup_file_name, guint32 flags, gint32 *error) +{ + gboolean result; + gchar* destPath = NULL; + gchar* sourcePath = NULL; + gchar* destBackupPath = NULL; + + if (destination_file_name != NULL) + { + destPath = mono_unicode_to_external(destination_file_name); + } + + if (source_file_name != NULL) + { + sourcePath = mono_unicode_to_external(source_file_name); + } + + if (destination_backup_file_name != NULL) + { + destBackupPath = mono_unicode_to_external(destination_backup_file_name); + } + + MONO_ENTER_GC_SAFE; + + result = UnityPalReplaceFile(sourcePath, destPath, destBackupPath, 0, error); + mono_w32error_set_last(*error); + + MONO_EXIT_GC_SAFE; + + g_free(destPath); + g_free(sourcePath); + g_free(destBackupPath); + + return result; +} + +gboolean +mono_w32file_copy (const gunichar2 *path, const gunichar2 *dest, gboolean overwrite, gint32 *error) +{ + gboolean result; + *error = 0; + + MONO_ENTER_GC_SAFE; + + gchar* palPath = mono_unicode_to_external(path); + gchar* palDest = mono_unicode_to_external(dest); + result = UnityPalCopyFile(palPath, palDest, overwrite, error); + mono_w32error_set_last(*error); + g_free(palPath); + g_free(palDest); + + MONO_EXIT_GC_SAFE; + + return result; +} + +gboolean +mono_w32file_lock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + MONO_ENTER_GC_SAFE; + + UnityPalLock(handle, position, length, error); + mono_w32error_set_last(*error); + + MONO_EXIT_GC_SAFE; + + return (*error == 0); +} + +gboolean +mono_w32file_unlock (gpointer handle, gint64 position, gint64 length, gint32 *error) +{ + MONO_ENTER_GC_SAFE; + + UnityPalUnlock(handle, position, length, error); + mono_w32error_set_last(*error); + + MONO_EXIT_GC_SAFE; + + return (*error == 0); +} + +gpointer +mono_w32file_get_console_input (void) +{ + return UnityPalGetStdInput(); +} + +gpointer +mono_w32file_get_console_output (void) +{ + return UnityPalGetStdOutput(); +} + +gpointer +mono_w32file_get_console_error (void) +{ + return UnityPalGetStdError(); +} + +gint64 +mono_w32file_get_file_size (gpointer handle, gint32 *error) +{ + gint64 length; + + MONO_ENTER_GC_SAFE; + + length = UnityPalGetLength(handle, error); + mono_w32error_set_last(*error); + + MONO_EXIT_GC_SAFE; + + return length; +} + +guint32 +mono_w32file_get_drive_type (const gunichar2 *root_path_name) +{ + return 0; +} + +gint32 +mono_w32file_get_logical_drive (guint32 len, gunichar2 *buf) +{ + return -1; +} + +gboolean +mono_w32file_remove_directory (const gunichar2 *name) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external (name); + gboolean result = UnityPalDirectoryRemove(palPath, &error); + mono_w32error_set_last(error); + g_free(palPath); + + return result; +} + +gboolean mono_w32file_delete(const gunichar2 *name) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external (name); + gboolean result = UnityPalDeleteFile(palPath, &error); + mono_w32error_set_last (error); + g_free(palPath); + + return result; +} + +guint32 +mono_w32file_get_cwd (guint32 length, gunichar2 *buffer) +{ + /* length is the number of characters in buffer, including the null terminator */ + /* count is the number of characters in the current directory, including the null terminator */ + gunichar2 *utf16_path; + glong count; + uintptr_t bytes; + int error = 0; + + const char* palPath = UnityPalDirectoryGetCurrent(&error); + mono_w32error_set_last (error); + utf16_path = mono_unicode_from_external(palPath, &bytes); + count = (bytes / 2) + 1; + + if (count <= length) { + /* Add the terminator */ + memset (buffer, '\0', bytes+2); + memcpy (buffer, utf16_path, bytes); + } + + g_free(utf16_path); + g_free(palPath); + + return count; +} + +gboolean +mono_w32file_set_cwd (const gunichar2 *path) +{ + int error = 0; + + gchar* palPath = mono_unicode_to_external(path); + gboolean result = UnityPalDirectorySetCurrent(palPath, &error); + mono_w32error_set_last (error); + g_free(palPath); + + return result; +} + +gboolean +mono_w32file_set_length (gpointer handle, gint64 length, gint32 *error) +{ + gboolean result = UnityPalSetLength(handle, length, error); + mono_w32error_set_last(*error); + + return result; +} + +void +mono_w32file_cleanup (void) +{ +} + +void +mono_w32file_init (void) +{ +} diff --git a/mono/metadata/w32file-unix.c b/mono/metadata/w32file-unix.c index 94d9b547da3a..e5a52b66c717 100644 --- a/mono/metadata/w32file-unix.c +++ b/mono/metadata/w32file-unix.c @@ -40,6 +40,7 @@ #include "w32file-unix-glob.h" #include "w32error.h" #include "fdhandle.h" +#include "mono/metadata/profiler-private.h" #include "utils/mono-io-portability.h" #include "utils/mono-logger-internals.h" #include "utils/mono-os-mutex.h" @@ -1037,6 +1038,7 @@ file_read(FileHandle *filehandle, gpointer buffer, guint32 numbytes, guint32 *by if (bytesread != NULL) { *bytesread = ret; + MONO_PROFILER_RAISE (fileio, (1, *bytesread)); } return(TRUE); @@ -1104,6 +1106,7 @@ file_write(FileHandle *filehandle, gconstpointer buffer, guint32 numbytes, guint } if (byteswritten != NULL) { *byteswritten = ret; + MONO_PROFILER_RAISE (fileio, (0, *byteswritten)); } return(TRUE); } @@ -1788,6 +1791,23 @@ static mode_t convert_perms(guint32 sharemode) } #endif +static gboolean already_shared(gboolean file_alread_shared, ino_t inode) +{ +#if HOST_DARWIN + /* On macOS and FAT32 partitions, we will sometimes get an inode value + * of 999999999 (or 1 on exFAT partitions) for more than one file. It + * means the file is empty (FILENO_EMPTY is defined in an internal + * header). When this happens, the hash table of file shares becomes + * corrupt, since more then one file has the same inode. Instead, let's + * assume it is always fine to share empty files. + * (Unity case 950616 or case 1253812). + */ + return file_alread_shared && inode != 999999999 && inode != 1; +#else + return file_alread_shared; +#endif +} + static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, guint32 fileaccess, FileShare **share_info) @@ -1797,7 +1817,7 @@ static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode, file_already_shared = file_share_get (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info); - if (file_already_shared) { + if (already_shared (file_already_shared, statbuf->st_ino)) { /* The reference to this share info was incremented * when we looked it up, so be careful to put it back * if we conclude we can't use this file. @@ -4177,7 +4197,6 @@ mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_byte #elif defined(HAVE_STATFS) struct statfs fsstat; #endif - gboolean isreadonly; gchar *utf8_path_name; gint ret; unsigned long block_size; @@ -4204,17 +4223,11 @@ mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_byte MONO_ENTER_GC_SAFE; ret = statvfs (utf8_path_name, &fsstat); MONO_EXIT_GC_SAFE; - isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY); block_size = fsstat.f_frsize; #elif defined(HAVE_STATFS) MONO_ENTER_GC_SAFE; ret = statfs (utf8_path_name, &fsstat); MONO_EXIT_GC_SAFE; -#if defined (MNT_RDONLY) - isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY); -#elif defined (MS_RDONLY) - isreadonly = ((fsstat.f_flags & MS_RDONLY) == MS_RDONLY); -#endif block_size = fsstat.f_bsize; #endif } while(ret == -1 && errno == EINTR); @@ -4228,13 +4241,9 @@ mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_byte } /* total number of free bytes for non-root */ + if (free_bytes_avail != NULL) { - if (isreadonly) { - *free_bytes_avail = 0; - } - else { - *free_bytes_avail = block_size * (guint64)fsstat.f_bavail; - } + *free_bytes_avail = block_size * (guint64)fsstat.f_bavail; } /* total number of bytes available for non-root */ @@ -4243,13 +4252,9 @@ mono_w32file_get_disk_free_space (const gunichar2 *path_name, guint64 *free_byte } /* total number of bytes available for root */ + if (total_number_of_free_bytes != NULL) { - if (isreadonly) { - *total_number_of_free_bytes = 0; - } - else { - *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree; - } + *total_number_of_free_bytes = block_size * (guint64)fsstat.f_bfree; } return(TRUE); diff --git a/mono/metadata/w32file-win32.c b/mono/metadata/w32file-win32.c index 7d08aefdeeb0..249dc5aceda9 100644 --- a/mono/metadata/w32file-win32.c +++ b/mono/metadata/w32file-win32.c @@ -11,6 +11,8 @@ #include #include #include "mono/metadata/w32file-win32-internals.h" +#include "mono/metadata/profiler-private.h" +#include "mono/metadata/w32error.h" void mono_w32file_init (void) @@ -81,23 +83,58 @@ mono_w32file_delete (const gunichar2 *name) return res; } +static void +cancel_w32_io (HANDLE file_handle) +{ + CancelIoEx (file_handle, NULL); +} + gboolean -mono_w32file_read(gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) +mono_w32file_read (gpointer handle, gpointer buffer, guint32 numbytes, guint32 *bytesread) { - gboolean res; + + gboolean interrupted; + guint32 last_error; + gboolean res = FALSE; + + mono_thread_info_install_interrupt (cancel_w32_io, handle, &interrupted); + if (interrupted) + return res; + MONO_ENTER_GC_SAFE; - res = ReadFile (handle, buffer, numbytes, bytesread, NULL); + res = ReadFile ((HANDLE)handle, buffer, numbytes, (PDWORD)bytesread, NULL); + /* need to save and restore since clients expect error code set for + * failed IO calls and mono_thread_info_uninstall_interrupt overwrites value */ + last_error = mono_w32error_get_last (); + MONO_EXIT_GC_SAFE; + mono_thread_info_uninstall_interrupt (&interrupted); + mono_w32error_set_last (last_error); + + return res; } gboolean mono_w32file_write (gpointer handle, gconstpointer buffer, guint32 numbytes, guint32 *byteswritten) { - gboolean res; + gboolean interrupted; + guint32 last_error; + gboolean res = FALSE; + + mono_thread_info_install_interrupt (cancel_w32_io, handle, &interrupted); + if (interrupted) + return res; + MONO_ENTER_GC_SAFE; - res = WriteFile (handle, buffer, numbytes, byteswritten, NULL); + res = WriteFile ((HANDLE)handle, buffer, numbytes, (PDWORD)byteswritten, NULL); + /* need to save and restore since clients expect error code set for + * failed IO calls and mono_thread_info_uninstall_interrupt overwrites value */ + last_error = mono_w32error_get_last (); MONO_EXIT_GC_SAFE; + mono_thread_info_uninstall_interrupt (&interrupted); + mono_w32error_set_last (last_error); + return res; } diff --git a/mono/metadata/w32file.c b/mono/metadata/w32file.c index 2fbb9a0005a2..88f9664bb2f9 100644 --- a/mono/metadata/w32file.c +++ b/mono/metadata/w32file.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -190,7 +191,11 @@ MonoBoolean ves_icall_System_IO_MonoIO_CreateDirectory (const gunichar2 *path, gint32 *error) { gboolean ret; + const gunichar2 *path_remapped; + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; + *error=ERROR_SUCCESS; ret=mono_w32file_create_directory (path); @@ -198,6 +203,8 @@ ves_icall_System_IO_MonoIO_CreateDirectory (const gunichar2 *path, gint32 *error *error=mono_w32error_get_last (); } + g_free (path_remapped); + return(ret); } @@ -205,6 +212,10 @@ MonoBoolean ves_icall_System_IO_MonoIO_RemoveDirectory (const gunichar2 *path, gint32 *error) { gboolean ret; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; *error=ERROR_SUCCESS; @@ -213,6 +224,8 @@ ves_icall_System_IO_MonoIO_RemoveDirectory (const gunichar2 *path, gint32 *error *error=mono_w32error_get_last (); } + g_free (path_remapped); + return(ret); } @@ -221,6 +234,10 @@ ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2 *path_with_pattern, Mo { HANDLE hnd; WIN32_FIND_DATA data; + const gunichar2 *path_with_pattern_remapped; + + if (path_with_pattern_remapped = mono_unity_remap_path_utf16 (path_with_pattern)) + path_with_pattern = path_with_pattern_remapped; hnd = mono_w32file_find_first (path_with_pattern, &data); @@ -228,6 +245,7 @@ ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2 *path_with_pattern, Mo MONO_HANDLE_ASSIGN (file_name, NULL_HANDLE_STRING); *file_attr = 0; *ioerror = mono_w32error_get_last (); + g_free (path_with_pattern_remapped); return hnd; } @@ -239,6 +257,7 @@ ves_icall_System_IO_MonoIO_FindFirstFile (const gunichar2 *path_with_pattern, Mo *file_attr = data.dwFileAttributes; *ioerror = ERROR_SUCCESS; + g_free (path_with_pattern_remapped); return hnd; } @@ -315,6 +334,10 @@ ves_icall_System_IO_MonoIO_SetCurrentDirectory (const gunichar2 *path, gint32 *error) { gboolean ret; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; *error=ERROR_SUCCESS; @@ -322,15 +345,31 @@ ves_icall_System_IO_MonoIO_SetCurrentDirectory (const gunichar2 *path, if(ret==FALSE) { *error=mono_w32error_get_last (); } - + + g_free (path_remapped); + return(ret); } MonoBoolean ves_icall_System_IO_MonoIO_MoveFile (const gunichar2 *path, const gunichar2 *dest, gint32 *error) { + gboolean ret; + const gunichar2 *path_remapped; + const gunichar2 *dest_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; + if (dest_remapped = mono_unity_remap_path_utf16 (dest)) + dest = dest_remapped; + *error=ERROR_SUCCESS; - return mono_w32file_move (path, dest, error); + ret = mono_w32file_move (path, dest, error); + + g_free (path_remapped); + g_free (dest_remapped); + + return ret; } MonoBoolean @@ -338,29 +377,63 @@ ves_icall_System_IO_MonoIO_ReplaceFile (const gunichar2 *source_file_name, const const gunichar2 *destination_backup_file_name, MonoBoolean ignore_metadata_errors, gint32 *error) { + gboolean ret; guint32 replace_flags = REPLACEFILE_WRITE_THROUGH; + const gunichar2 *source_file_name_remapped; + const gunichar2 *destination_file_name_remapped; + const gunichar2 *destination_backup_file_name_remapped; + if (source_file_name_remapped = mono_unity_remap_path_utf16 (source_file_name)) + source_file_name = source_file_name_remapped; + if (destination_file_name_remapped = mono_unity_remap_path_utf16 (destination_file_name)) + destination_file_name = destination_file_name_remapped; + if (destination_backup_file_name_remapped = mono_unity_remap_path_utf16 (destination_backup_file_name)) + destination_backup_file_name = destination_backup_file_name_remapped; *error = ERROR_SUCCESS; if (ignore_metadata_errors) replace_flags |= REPLACEFILE_IGNORE_MERGE_ERRORS; /* FIXME: source and destination file names must not be NULL, but apparently they might be! */ - return mono_w32file_replace (destination_file_name, source_file_name, + ret = mono_w32file_replace (destination_file_name, source_file_name, destination_backup_file_name, replace_flags, error); + + g_free (source_file_name_remapped); + g_free (destination_file_name_remapped); + g_free (destination_backup_file_name_remapped); + + return ret; } MonoBoolean ves_icall_System_IO_MonoIO_CopyFile (const gunichar2 *path, const gunichar2 *dest, MonoBoolean overwrite, gint32 *error) { + gboolean ret; + const gunichar2 *path_remapped; + const gunichar2 *dest_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; + if (dest_remapped = mono_unity_remap_path_utf16 (dest)) + dest = dest_remapped; + *error=ERROR_SUCCESS; - return mono_w32file_copy (path, dest, overwrite, error); + ret = mono_w32file_copy (path, dest, overwrite, error); + + g_free (path_remapped); + g_free (dest_remapped); + + return ret; } MonoBoolean ves_icall_System_IO_MonoIO_DeleteFile (const gunichar2 *path, gint32 *error) { gboolean ret; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; *error=ERROR_SUCCESS; @@ -368,6 +441,8 @@ ves_icall_System_IO_MonoIO_DeleteFile (const gunichar2 *path, gint32 *error) if(ret==FALSE) { *error=mono_w32error_get_last (); } + + g_free (path_remapped); return(ret); } @@ -376,6 +451,11 @@ gint32 ves_icall_System_IO_MonoIO_GetFileAttributes (const gunichar2 *path, gint32 *error) { gint32 ret; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; + *error=ERROR_SUCCESS; ret = mono_w32file_get_attributes (path); @@ -390,6 +470,9 @@ ves_icall_System_IO_MonoIO_GetFileAttributes (const gunichar2 *path, gint32 *err /* if(ret==INVALID_FILE_ATTRIBUTES) { */ *error=mono_w32error_get_last (); } + + g_free (path_remapped); + return(ret); } @@ -398,6 +481,11 @@ ves_icall_System_IO_MonoIO_SetFileAttributes (const gunichar2 *path, gint32 attr gint32 *error) { gboolean ret; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; + *error=ERROR_SUCCESS; ret=mono_w32file_set_attributes (path, @@ -405,6 +493,9 @@ ves_icall_System_IO_MonoIO_SetFileAttributes (const gunichar2 *path, gint32 attr if(ret==FALSE) { *error=mono_w32error_get_last (); } + + g_free (path_remapped); + return(ret); } @@ -430,6 +521,10 @@ MonoBoolean ves_icall_System_IO_MonoIO_GetFileStat (const gunichar2 *path, MonoIOStat *stat, gint32 *error) { gboolean result; + const gunichar2 *path_remapped; + + if (path_remapped = mono_unity_remap_path_utf16 (path)) + path = path_remapped; *error=ERROR_SUCCESS; @@ -440,6 +535,8 @@ ves_icall_System_IO_MonoIO_GetFileStat (const gunichar2 *path, MonoIOStat *stat, memset (stat, 0, sizeof (MonoIOStat)); } + g_free (path_remapped); + return result; } @@ -450,6 +547,10 @@ ves_icall_System_IO_MonoIO_Open (const gunichar2 *filename, gint32 mode, { HANDLE ret; int attributes, attrs; + const gunichar2 *filename_remapped; + + if (filename_remapped = mono_unity_remap_path_utf16 (filename)) + filename = filename_remapped; *error=ERROR_SUCCESS; @@ -485,8 +586,10 @@ ves_icall_System_IO_MonoIO_Open (const gunichar2 *filename, gint32 mode, ret=mono_w32file_create (filename, convert_access ((MonoFileAccess)access_mode), convert_share ((MonoFileShare)share), convert_mode ((MonoFileMode)mode), attributes); if(ret==INVALID_HANDLE_VALUE) { *error=mono_w32error_get_last (); - } - + } + + g_free (filename_remapped); + return(ret); } diff --git a/mono/metadata/w32handle.c b/mono/metadata/w32handle.c index 5ba22cab55c4..7ba74ef23fce 100644 --- a/mono/metadata/w32handle.c +++ b/mono/metadata/w32handle.c @@ -68,7 +68,7 @@ mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gbo { #ifdef DEBUG g_message ("%s: setting state of %p to %s (broadcast %s)", __func__, - handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE"); + handle_data, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE"); #endif if (state) { diff --git a/mono/metadata/w32mutex-unity.c b/mono/metadata/w32mutex-unity.c new file mode 100644 index 000000000000..f58205fca35c --- /dev/null +++ b/mono/metadata/w32mutex-unity.c @@ -0,0 +1,49 @@ +#include "w32mutex.h" +#include "Mutex-c-api.h" + +void +mono_w32mutex_init (void) +{ +} + +gpointer +ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error) +{ + UnityPalMutex* mutex = NULL; + + *created = TRUE; + + if (!name) { + mutex = UnityPalMutexNew (owned); + } else { + g_assertion_message ("Named mutexes are not supported by the Unity platform."); + } + + return UnityPalMutexHandleNew(mutex); +} + +MonoBoolean +ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) +{ + UnityPalMutexUnlock(UnityPalMutexHandleGet(handle)); + return TRUE; +} + +gpointer +ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights, gint32 *err, MonoError *error) +{ + g_assertion_message ("Named mutexes are not supported by the Unity platform."); + return NULL; +} + +MonoW32HandleNamespace* +mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex) +{ + g_assertion_message ("Named mutexes are not supported by the Unity platform."); + return NULL; +} + +void +mono_w32mutex_abandon (void) +{ +} diff --git a/mono/metadata/w32process-unity.c b/mono/metadata/w32process-unity.c new file mode 100644 index 000000000000..0b79fe252865 --- /dev/null +++ b/mono/metadata/w32process-unity.c @@ -0,0 +1,182 @@ +#include +#include + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +#ifdef HOST_WIN32 +typedef struct { + gpointer lpBaseOfDll; + guint32 SizeOfImage; + gpointer EntryPoint; +} MODULEINFO; +#endif + +void +mono_w32process_init (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_w32process_cleanup (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gpointer +ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info, gpointer stdin_handle, + gpointer stdout_handle, gpointer stderr_handle, MonoW32ProcessInfo *process_info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoArray * +ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gint32 +ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creationtime, gint64 *exittime, gint64 *kerneltime, gint64 *usertime) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gpointer +ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gboolean +mono_w32process_get_fileversion_info (gunichar2 *filename, gpointer* data) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +guint32 +mono_w32process_get_fileversion_info_size (gunichar2 *filename, guint32 *handle) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +guint32 +mono_w32process_get_pid (gpointer handle) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +guint32 +mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gboolean +mono_w32process_module_get_information (gpointer process, gpointer module, MODULEINFO *modinfo, guint32 size) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +guint32 +mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + + +gboolean +mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +guint32 +mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gboolean +mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/metadata/w32process-unix.c b/mono/metadata/w32process-unix.c index 48d5e7af8465..e2100d266e80 100644 --- a/mono/metadata/w32process-unix.c +++ b/mono/metadata/w32process-unix.c @@ -1401,34 +1401,35 @@ process_add_sigchld_handler (void) void mono_w32process_signal_finished (void) { - int status; - int pid; - Process *process; + mono_coop_mutex_lock (&processes_mutex); + + for (Process* process = processes; process; process = process->next) { + int status = -1; + int pid; - do { do { - pid = waitpid (-1, &status, WNOHANG); + pid = waitpid (process->pid, &status, WNOHANG); } while (pid == -1 && errno == EINTR); - if (pid <= 0) - break; - - mono_coop_mutex_lock (&processes_mutex); + // possible values of 'pid': + // process->pid : the status changed for this child + // 0 : status unchanged for this PID + // ECHILD : process has been reaped elsewhere (or never existed) + // EINVAL : invalid PID or other argument - for (process = processes; process; process = process->next) { - if (process->pid != pid) - continue; - if (process->signalled) - continue; + // Therefore, we ignore status unchanged (nothing to do) and error + // events (process is cleaned up later). + if (pid <= 0) + continue; + if (process->signalled) + continue; - process->signalled = TRUE; - process->status = status; - mono_coop_sem_post (&process->exit_sem); - break; - } + process->signalled = TRUE; + process->status = status; + mono_coop_sem_post (&process->exit_sem); + } - mono_coop_mutex_unlock (&processes_mutex); - } while (1); + mono_coop_mutex_unlock (&processes_mutex); } static gboolean @@ -2061,10 +2062,6 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, if (process_info != NULL) { process_info->process_handle = handle; process_info->pid = pid; - - /* FIXME: we might need to handle the thread info some day */ - process_info->thread_handle = INVALID_HANDLE_VALUE; - process_info->tid = 0; } mono_w32handle_unref (handle_data); @@ -2217,20 +2214,18 @@ ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStar } /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */ mono_w32handle_close (process_info->process_handle); - process_info->process_handle = NULL; + process_info->process_handle = INVALID_HANDLE_VALUE; } done: if (ret == FALSE) { process_info->pid = -mono_w32error_get_last (); } else { - process_info->thread_handle = NULL; #if !defined(MONO_CROSS_COMPILE) process_info->pid = mono_w32process_get_pid (process_info->process_handle); #else process_info->pid = 0; #endif - process_info->tid = 0; } return ret; diff --git a/mono/metadata/w32process-win32.c b/mono/metadata/w32process-win32.c index 24fb97ce4d49..63d02a703e73 100644 --- a/mono/metadata/w32process-win32.c +++ b/mono/metadata/w32process-win32.c @@ -111,13 +111,11 @@ ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStar process_info->pid = -GetLastError (); } else { process_info->process_handle = shellex.hProcess; - process_info->thread_handle = NULL; #if !defined(MONO_CROSS_COMPILE) process_info->pid = GetProcessId (shellex.hProcess); #else process_info->pid = 0; #endif - process_info->tid = 0; } return ret; @@ -190,14 +188,12 @@ process_unquote_application_name (gchar *appname) static gchar* process_quote_path (const gchar *path) { - gchar *res = g_shell_quote (path); - gchar *q = res; - while (*q) { - if (*q == '\'') - *q = '\"'; - q++; - } - return res; + size_t len = strlen (path); + GString *result = g_string_sized_new (len + 3); + g_string_append_c (result, '"'); + g_string_append (result, path); + g_string_append_c (result, '"'); + return g_string_free (result, FALSE); } /* Only used when UseShellExecute is false */ @@ -335,11 +331,9 @@ ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStart if (ret) { process_info->process_handle = procinfo.hProcess; /*process_info->thread_handle=procinfo.hThread;*/ - process_info->thread_handle = NULL; if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE) CloseHandle (procinfo.hThread); process_info->pid = procinfo.dwProcessId; - process_info->tid = procinfo.dwThreadId; } else { process_info->pid = -GetLastError (); } diff --git a/mono/metadata/w32process.c b/mono/metadata/w32process.c index b4720d029b6e..abfcfe600dd7 100644 --- a/mono/metadata/w32process.c +++ b/mono/metadata/w32process.c @@ -87,39 +87,21 @@ mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 la #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) */ -static MonoImage *system_image; - -static void -stash_system_image (MonoImage *image) -{ - system_image = image; -} - static MonoClass* -get_file_version_info_class (void) +get_file_version_info_class (MonoImage *system_image) { - static MonoClass *file_version_info_class; - - if (file_version_info_class) - return file_version_info_class; - g_assert (system_image); - return file_version_info_class = mono_class_load_from_name ( + return mono_class_load_from_name ( system_image, "System.Diagnostics", "FileVersionInfo"); } static MonoClass* -get_process_module_class (void) +get_process_module_class (MonoImage *system_image) { - static MonoClass *process_module_class; - - if (process_module_class) - return process_module_class; - g_assert (system_image); - return process_module_class = mono_class_load_from_name ( + return mono_class_load_from_name ( system_image, "System.Diagnostics", "ProcessModule"); } @@ -430,8 +412,6 @@ ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject { MonoError error; - stash_system_image (mono_object_class (this_obj)->image); - mono_w32process_get_fileversion (this_obj, mono_string_chars (filename), &error); if (!mono_error_ok (&error)) { mono_error_set_pending_exception (&error); @@ -483,7 +463,7 @@ process_add_module (HANDLE process, HMODULE mod, gunichar2 *filename, gunichar2 item = mono_object_new_checked (domain, proc_class, error); return_val_if_nok (error, NULL); - filever = mono_object_new_checked (domain, get_file_version_info_class (), error); + filever = mono_object_new_checked (domain, get_file_version_info_class (proc_class->image), error); return_val_if_nok (error, NULL); mono_w32process_get_fileversion (filever, filename, error); @@ -536,7 +516,7 @@ process_get_module (MonoAssembly *assembly, MonoClass *proc_class, MonoError *er item = mono_object_new_checked (domain, proc_class, error); return_val_if_nok (error, NULL); - filever = mono_object_new_checked (domain, get_file_version_info_class (), error); + filever = mono_object_new_checked (domain, get_file_version_info_class (proc_class->image), error); return_val_if_nok (error, NULL); filename = g_strdup_printf ("[In Memory] %s", modulename); @@ -572,8 +552,9 @@ ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, guint32 count = 0, module_count = 0, assembly_count = 0; guint32 i, num_added = 0; GPtrArray *assemblies = NULL; + MonoClass *process_module_class; - stash_system_image (mono_object_class (this_obj)->image); + process_module_class = get_process_module_class (mono_object_class (this_obj)->image); if (mono_w32process_get_pid (process) == mono_process_current_pid ()) { assemblies = get_domain_assemblies (mono_domain_get ()); @@ -584,7 +565,7 @@ ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, module_count += needed / sizeof(HMODULE); count = module_count + assembly_count; - temp_arr = mono_array_new_checked (mono_domain_get (), get_process_module_class (), count, &error); + temp_arr = mono_array_new_checked (mono_domain_get (), process_module_class, count, &error); if (mono_error_set_pending_exception (&error)) return NULL; @@ -592,7 +573,7 @@ ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, if (mono_w32process_module_get_name (process, mods[i], modname, MAX_PATH) && mono_w32process_module_get_filename (process, mods[i], filename, MAX_PATH)) { - MonoObject *module = process_add_module (process, mods[i], filename, modname, get_process_module_class (), &error); + MonoObject *module = process_add_module (process, mods[i], filename, modname, process_module_class, &error); if (!mono_error_ok (&error)) { mono_error_set_pending_exception (&error); return NULL; @@ -604,7 +585,7 @@ ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, if (assemblies) { for (i = 0; i < assembly_count; i++) { MonoAssembly *ass = (MonoAssembly *)g_ptr_array_index (assemblies, i); - MonoObject *module = process_get_module (ass, get_process_module_class (), &error); + MonoObject *module = process_get_module (ass, process_module_class, &error); if (!mono_error_ok (&error)) { mono_error_set_pending_exception (&error); return NULL; @@ -618,7 +599,7 @@ ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this_obj, arr = temp_arr; } else { /* shorter version of the array */ - arr = mono_array_new_checked (mono_domain_get (), get_process_module_class (), num_added, &error); + arr = mono_array_new_checked (mono_domain_get (), process_module_class, num_added, &error); if (mono_error_set_pending_exception (&error)) return NULL; diff --git a/mono/metadata/w32process.h b/mono/metadata/w32process.h index 0565ac1eb684..41c3a3154d9f 100644 --- a/mono/metadata/w32process.h +++ b/mono/metadata/w32process.h @@ -34,9 +34,7 @@ typedef enum { typedef struct { gpointer process_handle; - gpointer thread_handle; guint32 pid; /* Contains mono_w32error_get_last () on failure */ - guint32 tid; MonoArray *env_variables; MonoString *username; MonoString *domain; diff --git a/mono/metadata/w32semaphore-unity.c b/mono/metadata/w32semaphore-unity.c new file mode 100644 index 000000000000..778cb1693ccc --- /dev/null +++ b/mono/metadata/w32semaphore-unity.c @@ -0,0 +1,42 @@ +#include "w32semaphore.h" +#include "Semaphore-c-api.h" +#include "Error-c-api.h" + +void +mono_w32semaphore_init (void) +{ +} + +gpointer +ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error) +{ + if (name != NULL) + { + g_assertion_message("Named semaphores are not supported by the Unity platform."); + return NULL; + } + + UnityPalSemaphore* semaphore = UnityPalSemaphoreNew(initialCount, maximumCount); + *error = UnityPalGetLastError(); + return UnityPalSemaphoreHandleNew(semaphore); +} + +MonoBoolean +ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle, gint32 releaseCount, gint32 *prevcount) +{ + return UnityPalSemaphorePost(UnityPalSemaphoreHandleGet(handle), releaseCount, prevcount); +} + +gpointer +ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error) +{ + g_assertion_message("Named semaphores are not supported by the Unity platform."); + return NULL; +} + +MonoW32HandleNamespace* +mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore *semaphore) +{ + g_assertion_message("Named semaphores are not supported by the Unity platform."); + return NULL; +} diff --git a/mono/metadata/w32socket-internals.h b/mono/metadata/w32socket-internals.h index 62e2eb154c9c..7557818a630d 100644 --- a/mono/metadata/w32socket-internals.h +++ b/mono/metadata/w32socket-internals.h @@ -63,6 +63,9 @@ mono_w32socket_cleanup (void); SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking); +SOCKET +mono_w32socket_accept_internal (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking); + int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking); diff --git a/mono/metadata/w32socket-unity.c b/mono/metadata/w32socket-unity.c new file mode 100644 index 000000000000..38729c773d95 --- /dev/null +++ b/mono/metadata/w32socket-unity.c @@ -0,0 +1,201 @@ +#include "w32socket.h" +#include "w32socket-internals.h" + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +#ifdef NO_HAVE_TRANSMIT_FILE_BUFFERS + +typedef struct { + gpointer Head; + guint32 HeadLength; + gpointer Tail; + guint32 TailLength; +} TRANSMIT_FILE_BUFFERS; + +#endif + +gboolean +ves_icall_System_Net_Sockets_Socket_SupportPortReuse (MonoProtocolType proto, MonoError* error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +MonoBoolean +ves_icall_System_Net_Dns_GetHostByName_internal (MonoStringHandle host, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +void +mono_w32socket_initialize (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_w32socket_cleanup (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return INVALID_SOCKET; +} + +int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_set_blocking (SOCKET sock, gboolean blocking) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_get_available (SOCKET sock, guint64 *amount) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +void +mono_w32socket_set_last_error (gint32 error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gint32 +mono_w32socket_get_last_error (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint32 +mono_w32socket_convert_error (gint error) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_bind (SOCKET sock, struct sockaddr *addr, socklen_t addrlen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_getpeername (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_getsockname (SOCKET sock, struct sockaddr *name, socklen_t *namelen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_getsockopt (SOCKET sock, gint level, gint optname, gpointer optval, socklen_t *optlen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_setsockopt (SOCKET sock, gint level, gint optname, const gpointer optval, socklen_t optlen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_listen (SOCKET sock, gint backlog) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +gint +mono_w32socket_shutdown (SOCKET sock, gint how) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +SOCKET +mono_w32socket_socket (int domain, int type, int protocol) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return INVALID_SOCKET; +} + +gboolean +mono_w32socket_close (SOCKET sock) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/metadata/w32socket-win32.c b/mono/metadata/w32socket-win32.c index 076500687bae..8c4b55c3aebd 100644 --- a/mono/metadata/w32socket-win32.c +++ b/mono/metadata/w32socket-win32.c @@ -37,281 +37,144 @@ mono_w32socket_cleanup (void) { } -static gboolean set_blocking (SOCKET sock, gboolean block) -{ - u_long non_block = block ? 0 : 1; - return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR; -} - -static DWORD get_socket_timeout (SOCKET sock, int optname) -{ - DWORD timeout = 0; - int optlen = sizeof (DWORD); - if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) { - WSASetLastError (0); - return WSA_INFINITE; - } - if (timeout == 0) - timeout = WSA_INFINITE; // 0 means infinite - return timeout; -} - -/* -* Performs an alertable wait for the specified event (FD_ACCEPT_BIT, -* FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket. -* Returns TRUE if the event is fired without errors. Calls WSASetLastError() -* with WSAEINTR and returns FALSE if the thread is alerted. If the event is -* fired but with an error WSASetLastError() is called to set the error and the -* function returns FALSE. -*/ -static gboolean alertable_socket_wait (SOCKET sock, int event_bit) -{ - static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" }; - gboolean success = FALSE; - int error = -1; - DWORD timeout = WSA_INFINITE; - if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) { - timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO); - } - WSASetLastError (0); - WSAEVENT event = WSACreateEvent (); - if (event != WSA_INVALID_EVENT) { - if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) { - LOGDEBUG (g_message ("%06d - Calling mono_win32_wsa_wait_for_multiple_events () on socket %d", GetCurrentThreadId (), sock)); - DWORD ret = mono_win32_wsa_wait_for_multiple_events (1, &event, TRUE, timeout, TRUE); - if (ret == WSA_WAIT_IO_COMPLETION) { - LOGDEBUG (g_message ("%06d - mono_win32_wsa_wait_for_multiple_events () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock)); - error = WSAEINTR; - } else if (ret == WSA_WAIT_TIMEOUT) { - error = WSAETIMEDOUT; - } else { - g_assert (ret == WSA_WAIT_EVENT_0); - WSANETWORKEVENTS ne = { 0 }; - if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) { - if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) { - LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock)); - error = ne.iErrorCode[event_bit]; - } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) { - LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock)); - error = ne.iErrorCode[FD_CLOSE_BIT]; - } else { - LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock)); - success = TRUE; - error = 0; - } - } - } - WSAEventSelect (sock, NULL, 0); - } - WSACloseEvent (event); - } - if (error != -1) { - WSASetLastError (error); - } - return success; -} - -#define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \ - LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \ - if (blocking) { \ - if (set_blocking(sock, FALSE)) { \ - while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \ - int _error = WSAGetLastError ();\ - if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \ - break; \ - if (!alertable_socket_wait (sock, event_bit) || !repeat) \ - break; \ - } \ - int _saved_error = WSAGetLastError (); \ - set_blocking (sock, TRUE); \ - WSASetLastError (_saved_error); \ - } \ - } else { \ - ret = op (sock, __VA_ARGS__); \ - } \ - int _saved_error = WSAGetLastError (); \ - LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ - blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \ - WSASetLastError (_saved_error); +#define INTERRUPTABLE_SOCKET_CALL(blocking, ret, op, sock, ...) \ + MonoThreadInfo *info = mono_thread_info_current (); \ + if (blocking) \ + mono_win32_enter_blocking_io_call (info, (HANDLE)sock); \ + MONO_ENTER_GC_SAFE; \ + ret = op (sock, __VA_ARGS__); \ + MONO_EXIT_GC_SAFE; \ + if (blocking) \ + mono_win32_leave_blocking_io_call (info, (HANDLE)sock); SOCKET mono_w32socket_accept (SOCKET s, struct sockaddr *addr, socklen_t *addrlen, gboolean blocking) { - MonoInternalThread *curthread = mono_thread_internal_current (); - SOCKET newsock = INVALID_SOCKET; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen); - MONO_EXIT_GC_SAFE; - return newsock; + SOCKET ret = INVALID_SOCKET; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, accept, s, addr, addrlen); + return ret; } int mono_w32socket_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT, blocking, FALSE, ret, connect, s, name, namelen); - ret = WSAGetLastError () != 0 ? SOCKET_ERROR : 0; - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, connect, s, name, namelen); return ret; } int mono_w32socket_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking) { - MonoInternalThread *curthread = mono_thread_internal_current (); int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, recv, s, buf, len, flags); return ret; } int mono_w32socket_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, recvfrom, s, buf, len, flags, from, fromlen); return ret; } int mono_w32socket_recvbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 *lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, WSARecv, s, lpBuffers, dwBufferCount, (LPDWORD)lpNumberOfBytesRecvd, (LPDWORD)lpFlags, (LPWSAOVERLAPPED)lpOverlapped, (LPWSAOVERLAPPED_COMPLETION_ROUTINE)lpCompletionRoutine); return ret; } int mono_w32socket_send (SOCKET s, char *buf, int len, int flags, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, send, s, (const char *)buf, len, flags); return ret; } int mono_w32socket_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, sendto, s, buf, len, flags, to, tolen); return ret; } int mono_w32socket_sendbuffers (SOCKET s, WSABUF *lpBuffers, guint32 dwBufferCount, guint32 *lpNumberOfBytesRecvd, guint32 lpFlags, gpointer lpOverlapped, gpointer lpCompletionRoutine, gboolean blocking) { int ret = SOCKET_ERROR; - MONO_ENTER_GC_SAFE; - ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); - MONO_EXIT_GC_SAFE; + INTERRUPTABLE_SOCKET_CALL (blocking, ret, WSASend, s, lpBuffers, dwBufferCount, (LPDWORD)lpNumberOfBytesRecvd, lpFlags, (LPWSAOVERLAPPED)lpOverlapped, (LPWSAOVERLAPPED_COMPLETION_ROUTINE)lpCompletionRoutine); return ret; } #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) +static gint +internal_w32socket_transmit_file (SOCKET sock, gpointer file, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) { - LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket)); + gint ret = ERROR_NOT_SUPPORTED; + LPFN_TRANSMITFILE transmit_file; + GUID transmit_file_guid = WSAID_TRANSMITFILE; + DWORD output_bytes; - int error = 0, ret; + if (!WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &transmit_file_guid, sizeof (GUID), &transmit_file, sizeof (LPFN_TRANSMITFILE), &output_bytes, NULL, NULL)) { + MonoThreadInfo *info = mono_thread_info_current (); - MONO_ENTER_GC_SAFE; + if (blocking) + mono_win32_enter_blocking_io_call (info, (HANDLE)sock); - if (blocking) { - OVERLAPPED overlapped = { 0 }; - overlapped.hEvent = WSACreateEvent (); - if (overlapped.hEvent == WSA_INVALID_EVENT) { - ret = FALSE; - goto done; - } - if (!TransmitFile (hSocket, hFile, 0, 0, &overlapped, lpTransmitBuffers, dwReserved)) { - error = WSAGetLastError (); - if (error == WSA_IO_PENDING) { - error = 0; - // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either - DWORD ret = mono_win32_wait_for_single_object_ex (overlapped.hEvent, INFINITE, TRUE); - if (ret == WAIT_IO_COMPLETION) { - LOGDEBUG (g_message ("%06d - mono_win32_wait_for_single_object_ex () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket)); - error = WSAEINTR; - } else if (ret == WAIT_TIMEOUT) { - error = WSAETIMEDOUT; - } else if (ret != WAIT_OBJECT_0) { - error = GetLastError (); - } - } - } - WSACloseEvent (overlapped.hEvent); - } else { - if (!TransmitFile (hSocket, hFile, 0, 0, NULL, lpTransmitBuffers, dwReserved)) { - error = WSAGetLastError (); - } - } + MONO_ENTER_GC_SAFE; + if (transmit_file (sock, file, 0, 0, NULL, lpTransmitBuffers, dwReserved)) + ret = 0; + MONO_EXIT_GC_SAFE; - LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \ - blocking ? "blocking" : "non-blocking", hSocket, error == 0, error)); - WSASetLastError (error); + if (blocking) + mono_win32_leave_blocking_io_call (info, (HANDLE)sock); + } - ret = error == 0; + if (ret != 0) + ret = WSAGetLastError (); -done: - MONO_EXIT_GC_SAFE; return ret; } -#endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) -gint -mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +static gint +internal_w32socket_disconnect (SOCKET sock, gboolean reuse, gboolean blocking) { + gint ret = ERROR_NOT_SUPPORTED; LPFN_DISCONNECTEX disconnect; - LPFN_TRANSMITFILE transmit_file; + GUID disconnect_guid = WSAID_DISCONNECTEX; DWORD output_bytes; - gint ret; - MONO_ENTER_GC_SAFE; + if (!WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &disconnect_guid, sizeof (GUID), &disconnect, sizeof (LPFN_DISCONNECTEX), &output_bytes, NULL, NULL)) { + MonoThreadInfo *info = mono_thread_info_current (); - /* Use the SIO_GET_EXTENSION_FUNCTION_POINTER to determine - * the address of the disconnect method without taking - * a hard dependency on a single provider - * - * For an explanation of why this is done, you can read the - * article at http://www.codeproject.com/internet/jbsocketserver3.asp - * - * I _think_ the extension function pointers need to be looked - * up for each socket. - * - * FIXME: check the best way to store pointers to functions in - * managed objects that still works on 64bit platforms. */ + if (blocking) + mono_win32_enter_blocking_io_call (info, (HANDLE)sock); + MONO_ENTER_GC_SAFE; + if (disconnect (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0)) + ret = 0; + MONO_EXIT_GC_SAFE; - GUID disconnect_guid = WSAID_DISCONNECTEX; - ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &disconnect_guid, sizeof (GUID), &disconnect, sizeof (LPFN_DISCONNECTEX), &output_bytes, NULL, NULL); - if (ret == 0) { - if (!disconnect (sock, NULL, reuse ? TF_REUSE_SOCKET : 0, 0)) { - ret = WSAGetLastError (); - goto done; - } - - ret = 0; - goto done; + if (blocking) + mono_win32_leave_blocking_io_call (info, (HANDLE)sock); } - GUID transmit_file_guid = WSAID_TRANSMITFILE; - ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &transmit_file_guid, sizeof (GUID), &transmit_file, sizeof (LPFN_TRANSMITFILE), &output_bytes, NULL, NULL); - if (ret == 0) { - if (!transmit_file (sock, NULL, 0, 0, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0))) { - ret = WSAGetLastError (); - goto done; - } - - ret = 0; - goto done; - } + if (ret != 0) + ret = WSAGetLastError (); - ret = ERROR_NOT_SUPPORTED; + return ret; +} + +BOOL mono_w32socket_transmit_file (SOCKET hSocket, gpointer hFile, TRANSMIT_FILE_BUFFERS *lpTransmitBuffers, guint32 dwReserved, gboolean blocking) +{ + return internal_w32socket_transmit_file (hSocket, hFile, lpTransmitBuffers, dwReserved, blocking) == 0 ? TRUE : FALSE; +} + +gint +mono_w32socket_disconnect (SOCKET sock, gboolean reuse) +{ + gint ret = SOCKET_ERROR; + + ret = internal_w32socket_disconnect (sock, reuse, TRUE); + if (ret == 0) + ret = internal_w32socket_transmit_file (sock, NULL, NULL, TF_DISCONNECT | (reuse ? TF_REUSE_SOCKET : 0), TRUE); -done: - MONO_EXIT_GC_SAFE; return ret; } #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */ @@ -331,8 +194,10 @@ gint mono_w32socket_get_available (SOCKET sock, guint64 *amount) { gint ret; + u_long amount_long = 0; MONO_ENTER_GC_SAFE; - ret = ioctlsocket (sock, FIONREAD, (int*) amount); + ret = ioctlsocket (sock, FIONREAD, &amount_long); + *amount = amount_long; MONO_EXIT_GC_SAFE; return ret; } diff --git a/mono/metadata/w32socket.c b/mono/metadata/w32socket.c index 8fab506185d0..4e9121f0edf9 100644 --- a/mono/metadata/w32socket.c +++ b/mono/metadata/w32socket.c @@ -2466,6 +2466,12 @@ ves_icall_System_Net_Sockets_Socket_IOControl_internal (gsize sock, gint32 code, return (gint)output_bytes; } +MonoBoolean +ves_icall_System_Net_Sockets_Socket_IsProtocolSupported_internal (gint32 networkInterface) +{ + return TRUE; +} + static gboolean addrinfo_add_string (MonoDomain *domain, const char *s, MonoArrayHandle arr, int index, MonoError *error) { diff --git a/mono/metadata/w32socket.h b/mono/metadata/w32socket.h index b03ac936738b..4208591c41fc 100644 --- a/mono/metadata/w32socket.h +++ b/mono/metadata/w32socket.h @@ -265,6 +265,9 @@ ves_icall_System_Net_Dns_GetHostByName_internal (MonoStringHandle host, MonoStri MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, gint32 hint, MonoError *error); +MonoBoolean +ves_icall_System_Net_Sockets_Socket_IsProtocolSupported_internal (gint32 networkInterface); + MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal (MonoStringHandle addr, MonoStringHandleOut h_name, MonoArrayHandleOut h_aliases, MonoArrayHandleOut h_addr_list, diff --git a/mono/mini/.gitignore b/mono/mini/.gitignore index af434f4218a0..0fb8914a9923 100644 --- a/mono/mini/.gitignore +++ b/mono/mini/.gitignore @@ -31,6 +31,7 @@ /TAGS /mono-sgen /mono-boehm +/mono-bdwgc /buildver-sgen.h /buildver-boehm.h /regressiontests.out diff --git a/mono/mini/Makefile.am.in b/mono/mini/Makefile.am.in index 498fd7029c0d..535e2069daeb 100755 --- a/mono/mini/Makefile.am.in +++ b/mono/mini/Makefile.am.in @@ -18,12 +18,20 @@ libgc_libs=$(monodir)/libgc/libmonogc.la libgc_static_libs=$(monodir)/libgc/libmonogc-static.la endif +libbdwgc_libs=$(monodir)/external/bdwgc/libgc-static.la + boehm_libs= \ $(monodir)/mono/metadata/libmonoruntime.la \ $(monodir)/mono/utils/libmonoutils.la \ $(GLIB_LIBS) $(LIBICONV) \ $(libgc_libs) +bdwgc_libs= \ + $(monodir)/mono/metadata/libmonoruntimebdwgc.la \ + $(monodir)/mono/utils/libmonoutils.la \ + $(GLIB_LIBS) $(LIBICONV) \ + $(libbdwgc_libs) + sgen_libs = \ $(monodir)/mono/metadata/libmonoruntimesgen.la \ $(monodir)/mono/sgen/libmonosgen.la \ @@ -36,6 +44,12 @@ boehm_static_libs= \ $(GLIB_LIBS) $(LIBICONV) \ $(libgc_static_libs) +bdwgc_static_libs= \ + $(monodir)/mono/metadata/libmonoruntimebdwgc-static.la \ + $(monodir)/mono/utils/libmonoutils.la \ + $(GLIB_LIBS) $(LIBICONV) \ + $(libbdwgc_libs) + sgen_static_libs = \ $(monodir)/mono/metadata/libmonoruntimesgen-static.la \ $(monodir)/mono/sgen/libmonosgen-static.la \ @@ -102,15 +116,28 @@ boehm_libraries = libmonoboehm-2.0.la boehm_static_libraries = libmini-static.la $(boehm_static_libs) boehm_binaries = mono-boehm endif + +if SUPPORT_BDWGC +bdwgc_libraries = libmonobdwgc-2.0.la +bdwgc_static_libraries = libmini-static.la $(bdwgc_static_libs) +bdwgc_binaries = mono-bdwgc +endif if SUPPORT_SGEN mono_bin_suffix = sgen libmono_suffix = sgen -else +endif + +if SUPPORT_BOEHM mono_bin_suffix = boehm libmono_suffix = boehm endif +if SUPPORT_BDWGC +mono_bin_suffix = bdwgc +libmono_suffix = bdwgc +endif + if DISABLE_EXECUTABLES else mono: mono-$(mono_bin_suffix) @@ -127,9 +154,9 @@ endif if DISABLE_EXECUTABLES else if HOST_WIN32 -bin_PROGRAMS = $(boehm_binaries) $(sgen_binaries) monow +bin_PROGRAMS = $(boehm_binaries) $(bdwgc_binaries) $(sgen_binaries) monow else -bin_PROGRAMS = $(boehm_binaries) $(sgen_binaries) +bin_PROGRAMS = $(boehm_binaries) $(bdwgc_binaries) $(sgen_binaries) endif endif @@ -140,10 +167,10 @@ noinst_PROGRAMS = mono endif if DISABLE_EXECUTABLES -shared_libraries = $(boehm_libraries) $(sgen_libraries) +shared_libraries = $(boehm_libraries) $(bdwgc_libraries) $(sgen_libraries) else if SHARED_MONO -shared_libraries = $(boehm_libraries) $(sgen_libraries) +shared_libraries = $(boehm_libraries) $(bdwgc_libraries) $(sgen_libraries) endif endif @@ -179,6 +206,11 @@ mono_CFLAGS = $(AM_CFLAGS) mono_boehm_CFLAGS = $(AM_CFLAGS) +mono_bdwgc_SOURCES = \ + main.c + +mono_bdwgc_CFLAGS = $(AM_CFLAGS) + AM_CPPFLAGS = $(LIBGC_CPPFLAGS) mono_sgen_SOURCES = \ @@ -201,6 +233,16 @@ endif mono_boehm-main.$(OBJEXT): buildver-boehm.h endif +if SUPPORT_BDWGC +if DISABLE_EXECUTABLES +buildver-boehm.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntimebdwgc.la +else +buildver-boehm.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntimebdwgc-static.la +endif + @echo "const char *build_date = \"`date`\";" > buildver-boehm.h +mono_bdwgc-main.$(OBJEXT): buildver-boehm.h +endif + if DISABLE_EXECUTABLES buildver-sgen.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntimesgen.la $(monodir)/mono/sgen/libmonosgen.la else @@ -227,9 +269,11 @@ if STATIC_MONO # This leads to higher performance, especially with TLS MONO_LIB=$(boehm_static_libraries) MONO_SGEN_LIB=$(sgen_static_libraries) +MONO_BDWGC_LIB=$(bdwgc_static_libraries) else MONO_LIB=libmonoboehm-2.0.la MONO_SGEN_LIB=libmonosgen-2.0.la +MONO_BDWGC_LIB=libmonobdwgc-2.0.la endif if LOADED_LLVM @@ -249,6 +293,17 @@ mono_boehm_LDADD = \ mono_boehm_LDFLAGS = \ $(static_flags) -export-dynamic $(monobinldflags) $(monobin_platform_ldflags) +mono_bdwgc_LDADD = \ + $(MONO_BDWGC_LIB) \ + $(GLIB_LIBS) \ + $(LLVMMONOF) \ + $(LIBICONV) \ + -lm \ + $(MONO_DTRACE_OBJECT) + +mono_bdwgc_LDFLAGS = \ + $(static_flags) -export-dynamic $(monobinldflags) $(monobin_platform_ldflags) + mono_sgen_LDADD = \ $(MONO_SGEN_LIB) \ $(GLIB_LIBS) \ @@ -289,13 +344,23 @@ if SUPPORT_BOEHM monow_LDADD = $(mono_boehm_LDADD) monow_LDFLAGS = $(mono_boehm_LDFLAGS) -mwindows monow_SOURCES = $(mono_boehm_SOURCES) -else +endif +if SUPPORT_BDWGC +monow_LDADD = $(mono_bdwgc_LDADD) +monow_LDFLAGS = $(mono_bdwgc_LDFLAGS) -mwindows +monow_SOURCES = $(mono_bdwgc_SOURCES) +endif +if SUPPORT_SGEN monow_LDADD = $(mono_sgen_LDADD) monow_LDFLAGS = $(mono_sgen_LDFLAGS) -mwindows monow_SOURCES = $(mono_sgen_SOURCES) endif endif + +unity_sources = \ + mini-unity.c + wasm_sources = \ mini-wasm.c \ mini-wasm.h \ @@ -480,6 +545,8 @@ common_sources = \ type-checking.c \ lldb.h \ lldb.c \ + mixed_callstack_plugin.h \ + mixed_callstack_plugin.c \ memory-access.c \ mini-profiler.c \ interp-stubs.c \ @@ -636,7 +703,7 @@ os_sources = $(darwin_sources) $(posix_sources) monobin_platform_ldflags=-framework CoreFoundation -framework Foundation endif -libmini_la_SOURCES = $(common_sources) $(llvm_sources) $(llvm_runtime_sources) $(interp_sources) $(arch_sources) $(os_sources) +libmini_la_SOURCES = $(common_sources) $(llvm_sources) $(llvm_runtime_sources) $(interp_sources) $(arch_sources) $(os_sources) $(unity_sources) libmini_la_CFLAGS = $(mono_CFLAGS) libmonoboehm_2_0_la_SOURCES = @@ -644,6 +711,11 @@ libmonoboehm_2_0_la_CFLAGS = $(mono_boehm_CFLAGS) libmonoboehm_2_0_la_LIBADD = libmini.la $(boehm_libs) $(LIBMONO_DTRACE_OBJECT) $(LLVMMONOF) libmonoboehm_2_0_la_LDFLAGS = $(libmonoldflags) $(monobin_platform_ldflags) +libmonobdwgc_2_0_la_SOURCES = +libmonobdwgc_2_0_la_CFLAGS = $(mono_bdwgc_CFLAGS) +libmonobdwgc_2_0_la_LIBADD = libmini.la $(bdwgc_libs) $(LIBMONO_DTRACE_OBJECT) $(LLVMMONOF) +libmonobdwgc_2_0_la_LDFLAGS = $(libmonoldflags) $(monobin_platform_ldflags) + libmonosgen_2_0_la_SOURCES = libmonosgen_2_0_la_CFLAGS = $(mono_sgen_CFLAGS) libmonosgen_2_0_la_LIBADD = libmini.la $(sgen_libs) $(LIBMONO_DTRACE_OBJECT) $(LLVMMONOF) diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index cd6627f9a22c..07ef2b70b859 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -4111,7 +4111,7 @@ add_wrappers (MonoAotCompile *acfg) } /* write barriers */ - if (mono_gc_is_moving ()) { + if (mono_gc_needs_write_barriers()) { add_method (acfg, mono_gc_get_specific_write_barrier (FALSE)); add_method (acfg, mono_gc_get_specific_write_barrier (TRUE)); } diff --git a/mono/mini/cpu-amd64.md b/mono/mini/cpu-amd64.md index b2354f176286..abb1725fba4d 100755 --- a/mono/mini/cpu-amd64.md +++ b/mono/mini/cpu-amd64.md @@ -112,7 +112,7 @@ cgt: dest:c len:8 cgt.un: dest:c len:8 clt: dest:c len:8 clt.un: dest:c len:8 -localloc: dest:i src1:i len:96 +localloc: dest:i src1:i len:120 compare: src1:i src2:i len:3 lcompare: src1:i src2:i len:3 icompare: src1:i src2:i len:3 @@ -561,7 +561,7 @@ vcall2_membase: src1:b len:64 clob:c dyn_call: src1:i src2:i len:192 clob:c -localloc_imm: dest:i len:96 +localloc_imm: dest:i len:120 load_mem: dest:i len:16 loadi8_mem: dest:i len:16 diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 8b7ea714ee87..01de50246ab2 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -73,12 +73,19 @@ #include #include #include -#include "debugger-agent.h" + +#ifndef RUNTIME_IL2CPP #include "mini.h" #include "seq-points.h" #include "aot-runtime.h" #include "mini-runtime.h" #include "interp/interp.h" +#else +#include +#include +#include +#define MONO_ARCH_SOFT_DEBUG_SUPPORTED +#endif // !RUNTIME_IL2CPP /* * On iOS we can't use System.Environment.Exit () as it will do the wrong @@ -103,6 +110,14 @@ #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread +#include "debugger-agent.h" + +#ifdef RUNTIME_IL2CPP +extern Il2CppMonoDefaults il2cpp_mono_defaults; +extern Il2CppMonoDebugOptions il2cpp_mono_debug_options; +#endif // RUNTIME_IL2CPP + + typedef struct { gboolean enabled; char *transport; @@ -139,6 +154,9 @@ typedef struct MonoMethod *api_method; MonoContext ctx; MonoDebugMethodJitInfo *jit; +#ifdef RUNTIME_IL2CPP + Il2CppSequencePointExecutionContext* frame_context; +#endif // RUNTIME_IL2CPP MonoJitInfo *ji; MonoInterpFrameHandle interp_frame; int flags; @@ -175,6 +193,10 @@ struct _InvokeData typedef struct { MonoThreadUnwindState context; +#ifdef RUNTIME_IL2CPP + Il2CppThreadUnwindState* il2cpp_context; +#endif // RUNTIME_IL2CPP + /* This is computed on demand when it is requested using the wire protocol */ /* It is freed up when the thread is resumed */ int frame_count; @@ -230,8 +252,8 @@ typedef struct { MonoThreadUnwindState async_state; /* - * The context used for filter clauses - */ + * The context used for filter clauses + */ MonoThreadUnwindState filter_state; gboolean abort_requested; @@ -242,6 +264,9 @@ typedef struct { InvokeData *invoke; StackFrameInfo catch_frame; +#ifdef RUNTIME_IL2CPP + MonoException *exception; +#endif // RUNTIME_IL2CPP gboolean has_catch_frame; /* @@ -261,9 +286,10 @@ typedef struct { typedef struct { const char *name; void (*connect) (const char *address); + int (*wait_for_attach) (void); void (*close1) (void); void (*close2) (void); - gboolean (*send) (void *buf, int len); + int (*send) (void *buf, int len); int (*recv) (void *buf, int len); } DebuggerTransport; @@ -274,7 +300,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 45 +#define MINOR_VERSION 46 typedef enum { CMD_SET_VM = 1, @@ -290,7 +316,8 @@ typedef enum { CMD_SET_TYPE = 23, CMD_SET_MODULE = 24, CMD_SET_FIELD = 25, - CMD_SET_EVENT = 64 + CMD_SET_EVENT = 64, + CMD_SET_POINTER = 65 } CommandSet; typedef enum { @@ -495,7 +522,8 @@ typedef enum { CMD_TYPE_GET_INTERFACES = 16, CMD_TYPE_GET_INTERFACE_MAP = 17, CMD_TYPE_IS_INITIALIZED = 18, - CMD_TYPE_CREATE_INSTANCE = 19 + CMD_TYPE_CREATE_INSTANCE = 19, + CMD_TYPE_GET_VALUE_SIZE = 20 } CmdType; typedef enum { @@ -518,6 +546,10 @@ typedef enum { CMD_STRING_REF_GET_CHARS = 3 } CmdString; +typedef enum { + CMD_POINTER_GET_VALUE = 1 +} CmdPointer; + typedef enum { CMD_OBJECT_REF_GET_TYPE = 1, CMD_OBJECT_REF_GET_VALUES = 2, @@ -592,7 +624,7 @@ typedef struct { char *category, *message; /* For EVENT_KIND_TYPE_LOAD */ MonoClass *klass; -} EventInfo; +} DebuggerEventInfo; typedef struct { guint8 *buf, *p, *end; @@ -702,6 +734,10 @@ static SingleStepReq *ss_req; static int ss_count; #endif +#ifdef RUNTIME_IL2CPP +gboolean g_unity_pause_point_active; +#endif // RUNTIME_IL2CPP + /* The protocol version of the client */ static int major_version, minor_version; @@ -728,6 +764,7 @@ static MonoCoopMutex debug_mutex; static void transport_init (void); static void transport_connect (const char *address); +static void transport_close2 (void); static gboolean transport_handshake (void); static void register_transport (DebuggerTransport *trans); @@ -755,7 +792,9 @@ static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_ static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly); +#ifndef RUNTIME_IL2CPP static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly); +#endif // !RUNTIME_IL2CPP static void emit_assembly_load (gpointer assembly, gpointer user_data); @@ -795,8 +834,14 @@ static void ids_cleanup (void); static void suspend_init (void); +#ifdef RUNTIME_IL2CPP +static Il2CppSequencePoint* il2cpp_find_catch_sequence_point(DebuggerTlsData *tls); +static void ss_start_il2cpp(SingleStepReq *ss_req, DebuggerTlsData *tls, Il2CppSequencePoint *catchFrameSp); +static void GetSequencePointsAndSourceFilesUniqueSequencePoints(MonoMethod* method, GPtrArray** sequencePoints, GPtrArray** uniqueFileSequencePoints, GArray** uniqueFileSequencePointIndices); +#else static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch, StackFrame **frames, int nframes); +#endif // RUNTIME_IL2CPP static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilter filter, EventRequest *req); static void ss_destroy (SingleStepReq *req); @@ -814,6 +859,38 @@ static void register_socket_transport (void); #endif +#ifndef RUNTIME_IL2CPP +static MonoAssembly* mono_domain_get_assemblies_iter(MonoDomain *domain, void* *iter) +{ + if (!iter) + return NULL; + + if (*iter) + *iter = ((GList*)(*iter))->next; + else + *iter = domain->domain_assemblies; + + + if (*iter) + return ((GList*)(*iter))->data; + else + return NULL; +} +#endif // !RUNTIME_IL2CPP + +#ifdef RUNTIME_IL2CPP +static inline gboolean +is_debugger_thread (void) +{ + MonoInternalThread *internal; + + internal = mono_thread_internal_current (); + if (!internal) + return FALSE; + + return debugger_thread_id == mono_native_thread_id_get (); +} +#else static inline gboolean is_debugger_thread (void) { @@ -825,6 +902,7 @@ is_debugger_thread (void) return internal->debugger_thread; } +#endif // RUNTIME_IL2CPP static int parse_address (char *address, char **host, int *port) @@ -973,6 +1051,66 @@ mono_debugger_agent_parse_options (char *options) } } +static gboolean disable_optimizations = TRUE; + +static void +update_mdb_optimizations () +{ + gboolean enable = disable_optimizations; +#ifndef RUNTIME_IL2CPP + mini_get_debug_options ()->gen_sdb_seq_points = enable; + /* + * This is needed because currently we don't handle liveness info. + */ + mini_get_debug_options ()->mdb_optimizations = enable; + +#ifndef MONO_ARCH_HAVE_CONTEXT_SET_INT_REG + /* This is needed because we can't set local variables in registers yet */ + mono_disable_optimizations (MONO_OPT_LINEARS); +#endif + + /* + * The stack walk done from thread_interrupt () needs to be signal safe, but it + * isn't, since it can call into mono_aot_find_jit_info () which is not signal + * safe (#3411). So load AOT info eagerly when the debugger is running as a + * workaround. + */ + mini_get_debug_options ()->load_aot_jit_info_eagerly = enable; +#endif // !RUNTIME_IL2CPP +} + +MONO_API void +mono_debugger_set_generate_debug_info (gboolean enable) +{ + disable_optimizations = enable; + update_mdb_optimizations (); +} + +MONO_API gboolean +mono_debugger_get_generate_debug_info () +{ + return disable_optimizations; +} + +MONO_API void +mono_debugger_disconnect () +{ + // We just need to close the debugger client socket since + // the thread is blocked in the RECV method. The debugger_thread + // loop already handles the debugger client disconnection properly, + // so calling transport_close2 method is enough since the method + // only closes the debugger client socket. + transport_close2(); +} + +typedef void (*MonoDebuggerAttachFunc)(gboolean attached); +static MonoDebuggerAttachFunc attach_func; +MONO_API void +mono_debugger_install_attach_detach_callback (MonoDebuggerAttachFunc func) +{ + attach_func = func; +} + void mono_debugger_agent_init (void) { @@ -1000,7 +1138,9 @@ mono_debugger_agent_init (void) mono_profiler_set_thread_started_callback (prof, thread_startup); mono_profiler_set_thread_stopped_callback (prof, thread_end); mono_profiler_set_assembly_loaded_callback (prof, assembly_load); +#ifndef RUNTIME_IL2CPP mono_profiler_set_assembly_unloading_callback (prof, assembly_unload); +#endif // !RUNTIME_IL2CPP mono_profiler_set_jit_done_callback (prof, jit_done); mono_profiler_set_jit_failed_callback (prof, jit_failed); @@ -1009,11 +1149,11 @@ mono_debugger_agent_init (void) /* Needed by the hash_table_new_type () call below */ mono_gc_base_init (); - thread_to_tls = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger TLS Table"); + thread_to_tls = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "thread-to-tls table"); - tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Table"); + tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "tid-to-thread table"); - tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Object Table"); + tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "tid-to-thread object table"); pending_assembly_loads = g_ptr_array_new (); domains = g_hash_table_new (mono_aligned_addr_hash, NULL); @@ -1038,24 +1178,7 @@ mono_debugger_agent_init (void) breakpoints_init (); suspend_init (); - mini_get_debug_options ()->gen_sdb_seq_points = TRUE; - /* - * This is needed because currently we don't handle liveness info. - */ - mini_get_debug_options ()->mdb_optimizations = TRUE; - -#ifndef MONO_ARCH_HAVE_CONTEXT_SET_INT_REG - /* This is needed because we can't set local variables in registers yet */ - mono_disable_optimizations (MONO_OPT_LINEARS); -#endif - - /* - * The stack walk done from thread_interrupt () needs to be signal safe, but it - * isn't, since it can call into mono_aot_find_jit_info () which is not signal - * safe (#3411). So load AOT info eagerly when the debugger is running as a - * workaround. - */ - mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE; + update_mdb_optimizations (); #ifdef HAVE_SETPGID if (agent_config.setpgid) @@ -1066,6 +1189,45 @@ mono_debugger_agent_init (void) finish_agent_init (TRUE); } +#ifdef RUNTIME_IL2CPP +void +mono_debugger_run_debugger_thread_func(void* arg) +{ + debugger_thread(arg); +} + +typedef struct { + void(*il2cpp_debugger_save_thread_context)(Il2CppThreadUnwindState* context, int frameCountAdjust); + void(*il2cpp_debugger_free_thread_context)(Il2CppThreadUnwindState* context); +} MonoDebuggerRuntimeCallbacks; + +static MonoDebuggerRuntimeCallbacks callbacks; + +void mono_debugger_install_runtime_callbacks(MonoDebuggerRuntimeCallbacks* cbs) +{ + callbacks = *cbs; +} + +gboolean unity_debugger_agent_is_global_breakpoint_active() +{ + if (!ss_req) + return FALSE; + else + return ss_req->global; +} + +int32_t unity_debugger_agent_is_single_stepping () +{ + return ss_count; +} + +#define UPDATE_PAUSE_STATE() do { g_unity_pause_point_active = unity_debugger_agent_is_global_breakpoint_active() || unity_debugger_agent_is_single_stepping(); } while (0) +#else + +#define UPDATE_PAUSE_STATE() + +#endif // RUNTIME_IL2CPP + /* * finish_agent_init: * @@ -1193,7 +1355,13 @@ static int socket_transport_accept (int socket_fd) { MONO_ENTER_GC_SAFE; +#if defined(HOST_WIN32) && !defined(RUNTIME_IL2CPP) + conn_fd = mono_w32socket_accept (socket_fd, NULL, NULL, TRUE); + if (conn_fd != -1) + mono_w32socket_set_blocking (conn_fd, TRUE); +#else conn_fd = accept (socket_fd, NULL, NULL); +#endif MONO_EXIT_GC_SAFE; if (conn_fd == -1) { @@ -1205,6 +1373,26 @@ socket_transport_accept (int socket_fd) return conn_fd; } +static int +socket_transport_wait_for_attach(void) +{ + if (listen_fd == -1) { + DEBUG_PRINTF(1, "[dbg] Invalid listening socket\n"); + return 0; + } + + /* Block and wait for client connection */ + conn_fd = socket_transport_accept(listen_fd); + + DEBUG_PRINTF(1, "Accepted connection on %d\n", conn_fd); + if (conn_fd == -1) { + DEBUG_PRINTF(1, "[dbg] Bad client connection\n"); + return 0; + } + + return 1; +} + static gboolean socket_transport_send (void *data, int len) { @@ -1372,7 +1560,11 @@ socket_transport_connect (const char *address) break; /* Success */ MONO_ENTER_GC_SAFE; +#ifdef HOST_WIN32 + closesocket (sfd); +#else close (sfd); +#endif MONO_EXIT_GC_SAFE; } @@ -1397,6 +1589,9 @@ socket_transport_close1 (void) /* Close the read part only so it can still send back replies */ /* Also shut down the connection listener so that we can exit normally */ #ifdef HOST_WIN32 + MonoThreadInfo* info = mono_thread_info_lookup (debugger_thread_id); + if (info) + mono_threads_suspend_abort_syscall (info); /* SD_RECEIVE doesn't break the recv in the debugger thread */ shutdown (conn_fd, SD_BOTH); shutdown (listen_fd, SD_BOTH); @@ -1405,6 +1600,7 @@ socket_transport_close1 (void) shutdown (conn_fd, SHUT_RD); shutdown (listen_fd, SHUT_RDWR); MONO_ENTER_GC_SAFE; + // In Emscripten C# debugging builds, this function is not actually proxied, but shutdown(fd, SHUT_RDWR) also performs a close(fd). close (listen_fd); MONO_EXIT_GC_SAFE; #endif @@ -1427,6 +1623,7 @@ register_socket_transport (void) trans.name = "dt_socket"; trans.connect = socket_transport_connect; + trans.wait_for_attach = socket_transport_wait_for_attach; trans.close1 = socket_transport_close1; trans.close2 = socket_transport_close2; trans.send = socket_transport_send; @@ -1462,6 +1659,7 @@ register_socket_fd_transport (void) /* This is the same as the 'dt_socket' transport, but receives an already connected socket fd */ trans.name = "socket-fd"; trans.connect = socket_fd_transport_connect; + trans.wait_for_attach = socket_transport_wait_for_attach; trans.close1 = socket_transport_close1; trans.close2 = socket_transport_close2; trans.send = socket_transport_send; @@ -1531,6 +1729,12 @@ transport_connect (const char *address) transport->connect (address); } +int +transport_wait_for_attach(void) +{ + return transport->wait_for_attach (); +} + static void transport_close1 (void) { @@ -1603,10 +1807,10 @@ transport_handshake (void) if (conn_fd) { int flag = 1; int result = setsockopt (conn_fd, - IPPROTO_TCP, - TCP_NODELAY, - (char *) &flag, - sizeof(int)); + IPPROTO_TCP, + TCP_NODELAY, + (char *) &flag, + sizeof(int)); g_assert (result >= 0); } @@ -1638,11 +1842,20 @@ stop_debugger_thread (void) mono_coop_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex); mono_coop_mutex_unlock (&debugger_thread_exited_mutex); } while (!debugger_thread_exited); + + mono_native_thread_join (debugger_thread_id); } transport_close2 (); } +#if RUNTIME_IL2CPP +static void +start_debugger_thread (void) +{ + il2cpp_start_debugger_thread (); +} +#else static void start_debugger_thread (void) { @@ -1655,6 +1868,7 @@ start_debugger_thread (void) debugger_thread_handle = mono_threads_open_thread_handle (thread->handle); g_assert (debugger_thread_handle); } +#endif // RUNTIME_IL2CPP /* * Functions to decode protocol data @@ -1956,7 +2170,7 @@ objrefs_init (void) { objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref); obj_to_objref = g_hash_table_new (NULL, NULL); - suspended_objs = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Suspended Object Table"); + suspended_objs = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "suspended objects table"); } static void @@ -2170,6 +2384,35 @@ typedef struct { /* Protected by the dbg lock */ static GPtrArray *ids [ID_NUM]; +#ifdef RUNTIME_IL2CPP +static GHashTable* s_jit_info_hashtable; + +void mono_debugger_il2cpp_init () +{ + s_jit_info_hashtable = g_hash_table_new_full(mono_aligned_addr_hash, NULL, NULL, NULL); + debug_options.native_debugger_break = FALSE; +} + +static gpointer +unity_mono_jit_find_compiled_method_with_jit_info(MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji) +{ + MonoJitInfo* ji2; + + g_assert(mono_get_root_domain() == domain); + + ji2 = g_hash_table_lookup(s_jit_info_hashtable, method); + + if (!ji2) + { + *ji = NULL; + return NULL; + } + + *ji = ji2; + return ji2->code_start; +} +#endif // RUNTIME_IL2CPP + static void ids_init (void) { @@ -2197,7 +2440,7 @@ ids_cleanup (void) void mono_debugger_agent_free_domain_info (MonoDomain *domain) { - AgentDomainInfo *info = (AgentDomainInfo *)domain_jit_info (domain)->agent_info; + AgentDomainInfo *info = (AgentDomainInfo *)VM_DOMAIN_GET_AGENT_INFO(domain); int i, j; GHashTableIter iter; GPtrArray *file_names; @@ -2232,7 +2475,7 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain) g_free (info); } - domain_jit_info (domain)->agent_info = NULL; + VM_DOMAIN_SET_AGENT_INFO(domain, NULL); /* Clear ids referencing structures in the domain */ dbg_lock (); @@ -2259,10 +2502,10 @@ get_agent_domain_info (MonoDomain *domain) mono_domain_lock (domain); - info = (AgentDomainInfo *)domain_jit_info (domain)->agent_info; + info = (AgentDomainInfo *)VM_DOMAIN_GET_AGENT_INFO(domain); if (!info) { info = g_new0 (AgentDomainInfo, 1); - domain_jit_info (domain)->agent_info = info; + VM_DOMAIN_SET_AGENT_INFO(domain, info); info->loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL); info->source_files = g_hash_table_new (mono_aligned_addr_hash, NULL); info->source_file_to_class = g_hash_table_new (g_str_hash, g_str_equal); @@ -2331,11 +2574,13 @@ decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDom res = (Id *)g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1)); dbg_unlock (); +#ifndef RUNTIME_IL2CPP if (res->domain == NULL || res->domain->state == MONO_APPDOMAIN_UNLOADED) { DEBUG_PRINTF (1, "ERR_UNLOADED, id=%d, type=%d.\n", id, type); *err = ERR_UNLOADED; return NULL; } +#endif // !RUNTIME_IL2CPP if (domain) *domain = res->domain; @@ -2361,7 +2606,7 @@ decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, if (G_UNLIKELY (log_level >= 2) && klass) { char *s; - s = mono_type_full_name (&klass->byval_arg); + s = mono_type_full_name (mono_class_get_type(klass)); DEBUG_PRINTF (2, "[dbg] recv class [%s]\n", s); g_free (s); } @@ -2421,7 +2666,7 @@ buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass) if (G_UNLIKELY (log_level >= 2) && klass) { char *s; - s = mono_type_full_name (&klass->byval_arg); + s = mono_type_full_name (mono_class_get_type(klass)); if (is_debugger_thread ()) DEBUG_PRINTF (2, "[dbg] send class [%s]\n", s); else @@ -2482,7 +2727,7 @@ static void invoke_method (void); /* * SUSPEND/RESUME */ - +#ifndef RUNTIME_IL2CPP static MonoJitInfo* get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) { @@ -2511,6 +2756,7 @@ get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) } return ji; } +#endif // !RUNTIME_IL2CPP /* * save_thread_context: @@ -2518,6 +2764,18 @@ get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) * Set CTX as the current threads context which is used for computing stack traces. * This function is signal-safe. */ +#ifdef RUNTIME_IL2CPP +static void +save_thread_context (MonoContext *ctx) +{ + DebuggerTlsData *tls; + + tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + g_assert (tls); + + callbacks.il2cpp_debugger_save_thread_context(tls->il2cpp_context, 0); +} +#else static void save_thread_context (MonoContext *ctx) { @@ -2531,6 +2789,7 @@ save_thread_context (MonoContext *ctx) else mono_thread_state_init_from_current (&tls->context); } +#endif // RUNTIME_IL2CPP /* Number of threads suspended */ /* @@ -2596,7 +2855,7 @@ copy_unwind_state_from_frame_data (MonoThreadUnwindState *to, GetLastFrameUserDa to->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = jit_tls; to->valid = TRUE; } - +#ifndef RUNTIME_IL2CPP /* * thread_interrupt: * @@ -2681,6 +2940,7 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, MonoJitInfo *ji) } } } +#endif // !RUNTIME_IL2CPP /* * reset_native_thread_suspend_state: @@ -2709,6 +2969,21 @@ typedef struct { gboolean valid_info; } InterruptData; +#ifdef RUNTIME_IL2CPP +static SuspendThreadResult +debugger_interrupt_critical (MonoThreadInfo *info, gpointer user_data) +{ + InterruptData *data = (InterruptData *)user_data; + + data->valid_info = TRUE; + if (!data->tls->suspended && !data->tls->suspending) { + data->tls->suspended = TRUE; + mono_coop_sem_post(&suspend_sem); + } + + return MonoResumeThread; +} +#else static SuspendThreadResult debugger_interrupt_critical (MonoThreadInfo *info, gpointer user_data) { @@ -2726,6 +3001,7 @@ debugger_interrupt_critical (MonoThreadInfo *info, gpointer user_data) thread_interrupt (data->tls, info, ji); return MonoResumeThread; } +#endif // RUNTIME_IL2CPP /* * notify_thread: @@ -2761,9 +3037,11 @@ notify_thread (gpointer key, gpointer value, gpointer user_data) static void process_suspend (DebuggerTlsData *tls, MonoContext *ctx) { +#ifndef RUNTIME_IL2CPP guint8 *ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx); MonoJitInfo *ji; MonoMethod *method; +#endif // !RUNTIME_IL2CPP if (mono_loader_lock_is_owned_by_self ()) { /* @@ -2793,12 +3071,14 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) return; } +#ifndef RUNTIME_IL2CPP ji = get_top_method_ji (ip, NULL, NULL); g_assert (ji); /* Can't suspend in these methods */ method = jinfo_get_method (ji); - if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) + if (method->klass == mono_defaults.string_class && (!strcmp (mono_method_get_name(method), "memset") || strstr (mono_method_get_name(method), "memcpy"))) return; +#endif // !RUNTIME_IL2CPP save_thread_context (ctx); @@ -2814,6 +3094,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) static void suspend_vm (void) { + gboolean tp_suspend = FALSE; mono_loader_lock (); mono_coop_mutex_lock (&suspend_mutex); @@ -2834,9 +3115,11 @@ suspend_vm (void) /* * Suspend creation of new threadpool threads, since they cannot run */ - mono_threadpool_suspend (); - + tp_suspend = TRUE; mono_loader_unlock (); + + if (tp_suspend) + mono_threadpool_suspend (); } /* @@ -2849,6 +3132,7 @@ static void resume_vm (void) { g_assert (is_debugger_thread ()); + gboolean tp_resume = FALSE; mono_loader_lock (); @@ -2872,9 +3156,11 @@ resume_vm (void) //g_assert (err == 0); if (suspend_count == 0) - mono_threadpool_resume (); - + tp_resume = TRUE; mono_loader_unlock (); + + if (tp_resume) + mono_threadpool_resume (); } /* @@ -2920,8 +3206,10 @@ free_frames (StackFrame **frames, int nframes) int i; for (i = 0; i < nframes; ++i) { +#ifndef RUNTIME_IL2CPP if (frames [i]->jit) mono_debug_free_method_jit_info (frames [i]->jit); +#endif // !RUNTIME_IL2CPP g_free (frames [i]); } g_free (frames); @@ -2930,6 +3218,8 @@ free_frames (StackFrame **frames, int nframes) static void invalidate_frames (DebuggerTlsData *tls) { + mono_loader_lock (); + if (!tls) tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); g_assert (tls); @@ -2941,6 +3231,8 @@ invalidate_frames (DebuggerTlsData *tls) free_frames (tls->restore_frames, tls->restore_frame_count); tls->restore_frame_count = 0; tls->restore_frames = NULL; + + mono_loader_unlock (); } /* @@ -2964,40 +3256,47 @@ suspend_current (void) return; } - tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); g_assert (tls); - mono_coop_mutex_lock (&suspend_mutex); + gboolean do_resume = FALSE; + while (!do_resume) { + mono_coop_mutex_lock (&suspend_mutex); - tls->suspending = FALSE; - tls->really_suspended = TRUE; + tls->suspending = FALSE; + tls->really_suspended = TRUE; - if (!tls->suspended) { - tls->suspended = TRUE; - mono_coop_sem_post (&suspend_sem); - } + if (!tls->suspended) { + tls->suspended = TRUE; + mono_coop_sem_post (&suspend_sem); + } - DEBUG_PRINTF (1, "[%p] Suspended.\n", (gpointer) (gsize) mono_native_thread_id_get ()); + DEBUG_PRINTF (1, "[%p] Suspended.\n", (gpointer) (gsize) mono_native_thread_id_get ()); - while (suspend_count - tls->resume_count > 0) { - mono_coop_cond_wait (&suspend_cond, &suspend_mutex); - } + while (suspend_count - tls->resume_count > 0) { + mono_coop_cond_wait (&suspend_cond, &suspend_mutex); + } - tls->suspended = FALSE; - tls->really_suspended = FALSE; + tls->suspended = FALSE; + tls->really_suspended = FALSE; - threads_suspend_count --; + threads_suspend_count --; - mono_coop_mutex_unlock (&suspend_mutex); + mono_coop_mutex_unlock (&suspend_mutex); - DEBUG_PRINTF (1, "[%p] Resumed.\n", (gpointer) (gsize) mono_native_thread_id_get ()); + DEBUG_PRINTF (1, "[%p] Resumed.\n", (gpointer) (gsize) mono_native_thread_id_get ()); - if (tls->pending_invoke) { - /* Save the original context */ - tls->pending_invoke->has_ctx = TRUE; - tls->pending_invoke->ctx = tls->context.ctx; + if (tls->pending_invoke) { + /* Save the original context */ + tls->pending_invoke->has_ctx = TRUE; + tls->pending_invoke->ctx = tls->context.ctx; - invoke_method (); + invoke_method (); + + /* Have to suspend again */ + } else { + do_resume = TRUE; + } } /* The frame info becomes invalid after a resume */ @@ -3093,12 +3392,14 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) SeqPoint sp; int flags = 0; + mono_loader_lock (); if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) { /* Mark the last frame as an invoke frame */ if (ud->frames) ((StackFrame*)g_slist_last (ud->frames)->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE; } + mono_loader_unlock (); return FALSE; } @@ -3109,11 +3410,15 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) actual_method = info->actual_method; api_method = method; - if (!method) + if (!method) { + mono_loader_unlock (); return FALSE; + } - if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)) + if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)) { + mono_loader_unlock (); return FALSE; + } if (info->il_offset == -1) { /* mono_debug_il_offset_from_address () doesn't seem to be precise enough (#2092) */ @@ -3128,12 +3433,16 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) DEBUG_PRINTF (1, "\tFrame: %s:[il=0x%x, native=0x%x] %d\n", mono_method_full_name (method, TRUE), info->il_offset, info->native_offset, info->managed); if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) { - if (!CHECK_PROTOCOL_VERSION (2, 17)) + if (!CHECK_PROTOCOL_VERSION (2, 17)) { /* Older clients can't handle this flag */ + mono_loader_unlock (); return FALSE; + } api_method = mono_marshal_method_from_wrapper (method); - if (!api_method) + if (!api_method) { + mono_loader_unlock (); return FALSE; + } actual_method = api_method; flags |= FRAME_FLAG_NATIVE_TRANSITION; } @@ -3157,6 +3466,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) ud->frames = g_slist_append (ud->frames, frame); + mono_loader_unlock (); return FALSE; } @@ -3209,19 +3519,19 @@ compute_frame_info_from (MonoInternalThread *thread, DebuggerTlsData *tls, MonoT } static void -compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) +compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean force_update) { ComputeFramesUserData user_data; GSList *tmp; - int i, findex, new_frame_count; + int i=0, findex, new_frame_count; StackFrame **new_frames, *f; MonoUnwindOptions opts = (MonoUnwindOptions)(MONO_UNWIND_DEFAULT | MONO_UNWIND_REG_LOCATIONS); // FIXME: Locking on tls - if (tls->frames && tls->frames_up_to_date) + if (tls->frames && tls->frames_up_to_date && !force_update) return; - DEBUG_PRINTF (1, "Frames for %p(tid=%lx):\n", thread, (glong)thread->tid); + DEBUG_PRINTF (1, "Frames for %p(tid=%lx):\n", thread, thread->tid); user_data.tls = tls; user_data.frames = NULL; @@ -3229,10 +3539,17 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) tls->frame_count = 0; return; } if (!tls->really_suspended && tls->async_state.valid) { +#ifdef RUNTIME_IL2CPP + NOT_IMPLEMENTED; +#else /* Have to use the state saved by the signal handler */ process_frame (&tls->async_last_frame, NULL, &user_data); mono_walk_stack_with_state (process_frame, &tls->async_state, opts, &user_data); +#endif // RUNTIME_IL2CPP } else if (tls->filter_state.valid) { +#ifdef RUNTIME_IL2CPP + NOT_IMPLEMENTED; +#else /* * We are inside an exception filter. * @@ -3246,8 +3563,58 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) * After that, we resume unwinding from the location where the exception has been thrown. */ mono_walk_stack_with_state (process_frame, &tls->filter_state, opts, &user_data); +#endif // RUNTIME_IL2CPP } else if (tls->context.valid) { +#ifdef RUNTIME_IL2CPP + NOT_IMPLEMENTED; +#else mono_walk_stack_with_state (process_frame, &tls->context, opts, &user_data); +#endif // RUNTIME_IL2CPP +#ifdef RUNTIME_IL2CPP + } else if (tls->il2cpp_context->frameCount > 0) { + for (int frame_index = tls->il2cpp_context->frameCount - 1; frame_index >= 0; --frame_index) + { + Il2CppSequencePoint* seq_point = tls->il2cpp_context->executionContexts[frame_index]->currentSequencePoint; + StackFrame* frame = g_new0(StackFrame, 1); + MonoMethod *sp_method = il2cpp_get_seq_point_method(seq_point); + frame->method = sp_method; + frame->actual_method = tls->il2cpp_context->executionContexts[frame_index]->method; + frame->frame_context = tls->il2cpp_context->executionContexts[frame_index]; + frame->api_method = sp_method; + frame->il_offset = seq_point->ilOffset; + frame->native_offset = 0; + frame->flags = 0; + frame->ji = g_new0 (MonoJitInfo, 1); + frame->ji->is_interp = 1; + frame->domain = mono_domain_get(); + frame->has_ctx = 1; + + + StackFrameInfo info = { 0 }; + info.type = FRAME_TYPE_INTERP; + info.ji = g_new0 (MonoJitInfo, 1); + info.ji->is_interp = 1; + info.method = sp_method; + info.actual_method = tls->il2cpp_context->executionContexts[frame_index]->method; + info.ji->d.method = info.actual_method; + info.domain = mono_domain_get (); + info.managed = TRUE; + info.async_context; + info.native_offset; + info.il_offset = seq_point->ilOffset; + info.interp_exit_data; + info.interp_frame = tls->il2cpp_context->executionContexts[frame_index]; + info.lmf; + info.unwind_info_len; + info.unwind_info; + info.reg_locations; + + + process_frame (&info, NULL, &user_data); + + //user_data.frames = g_slist_append(user_data.frames, frame); + } +#endif // RUNTIME_IL2CPP } else { // FIXME: tls->frame_count = 0; @@ -3265,10 +3632,12 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) * the still valid stack frames. */ for (i = 0; i < tls->frame_count; ++i) { +#ifndef RUNTIME_IL2CPP if (MONO_CONTEXT_GET_SP (&tls->frames [i]->ctx) == MONO_CONTEXT_GET_SP (&f->ctx)) { f->id = tls->frames [i]->id; break; } +#endif // !RUNTIME_IL2CPP } if (i >= tls->frame_count) @@ -3382,6 +3751,7 @@ dbg_path_get_basename (const char *filename) return g_strdup (&r[1]); } +#ifndef RUNTIME_IL2CPP static void init_jit_info_dbg_attrs (MonoJitInfo *ji) { @@ -3426,6 +3796,27 @@ init_jit_info_dbg_attrs (MonoJitInfo *ji) mono_memory_barrier (); ji->dbg_attrs_inited = TRUE; } +#endif // !RUNTIME_IL2CPP + +static gboolean find_source_file_in_hash_table(const char* needle, GHashTable* haystack) +{ + gboolean found = FALSE; + char *s = strdup_tolower(needle); + if (g_hash_table_lookup(haystack, s)) { + found = TRUE; + } else { + char *s2 = dbg_path_get_basename(needle); + char *s3 = strdup_tolower(s2); + + if (g_hash_table_lookup(haystack, s3)) + found = TRUE; + g_free(s2); + g_free(s3); + } + g_free(s); + + return found; +} /* * EVENT HANDLING @@ -3442,10 +3833,14 @@ init_jit_info_dbg_attrs (MonoJitInfo *ji) * LOCKING: Assumes the loader lock is held. */ static GSList* -create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy) +create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, DebuggerEventInfo *ei, int *suspend_policy) { int i, j; GSList *events = NULL; +#ifdef RUNTIME_IL2CPP + Il2CppSequencePoint *sp = (Il2CppSequencePoint*)ji; + MonoMethod *sp_method = ji ? il2cpp_get_seq_point_method(sp) : NULL; +#endif // RUNTIME_IL2CPP *suspend_policy = SUSPEND_POLICY_NONE; @@ -3477,9 +3872,9 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo if (mod->data.thread != mono_thread_internal_current ()) filtered = TRUE; } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) { - if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass)) + if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, mono_object_get_class (ei->exc))) filtered = TRUE; - if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != ei->exc->vtable->klass) + if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != mono_object_get_class (ei->exc)) filtered = TRUE; if (ei->caught && !mod->caught) filtered = TRUE; @@ -3492,58 +3887,76 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo if (assemblies) { for (k = 0; assemblies [k]; ++k) +#ifdef RUNTIME_IL2CPP + { + if (assemblies[k] == mono_image_get_assembly (mono_class_get_image (mono_method_get_class (sp_method)))) + found = TRUE; + } +#else if (assemblies [k] == jinfo_get_method (ji)->klass->image->assembly) found = TRUE; +#endif // RUNTIME_IL2CPP } if (!found) filtered = TRUE; } else if (mod->kind == MOD_KIND_SOURCE_FILE_ONLY && ei && ei->klass) { gpointer iter = NULL; MonoMethod *method; +#ifndef RUNTIME_IL2CPP MonoDebugSourceInfo *sinfo; - char *source_file, *s; +#endif // !RUNTIME_IL2CPP + char *s; gboolean found = FALSE; int i; GPtrArray *source_file_list; - while ((method = mono_class_get_methods (ei->klass, &iter))) { +#ifdef RUNTIME_IL2CPP + int fileCount; + const char **files; + + files = il2cpp_get_source_files_for_type(ei->klass, &fileCount); + for (int i = 0; i < fileCount; ++i) + { + char *s = strdup_tolower(files[i]); + + found = find_source_file_in_hash_table(s, mod->data.source_files); + g_free(s); + if (found) + break; + } +#else + while (!found && (method = mono_class_get_methods (ei->klass, &iter))) { MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); if (minfo) { - mono_debug_get_seq_points (minfo, &source_file, &source_file_list, NULL, NULL, NULL); + mono_debug_get_seq_points (minfo, NULL, &source_file_list, NULL, NULL, NULL); for (i = 0; i < source_file_list->len; ++i) { sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, i); - /* - * Do a case-insesitive match by converting the file name to - * lowercase. - */ - s = strdup_tolower (sinfo->source_file); - if (g_hash_table_lookup (mod->data.source_files, s)) - found = TRUE; - else { - char *s2 = dbg_path_get_basename (sinfo->source_file); - char *s3 = strdup_tolower (s2); - - if (g_hash_table_lookup (mod->data.source_files, s3)) - found = TRUE; - g_free (s2); - g_free (s3); - } - g_free (s); + found = find_source_file_in_hash_table(sinfo->source_file, mod->data.source_files); + if (found) + break; } g_ptr_array_free (source_file_list, TRUE); } } +#endif // RUNTIME_IL2CPP if (!found) filtered = TRUE; } else if (mod->kind == MOD_KIND_TYPE_NAME_ONLY && ei && ei->klass) { char *s; - s = mono_type_full_name (&ei->klass->byval_arg); + s = mono_type_full_name (mono_class_get_type(ei->klass)); if (!g_hash_table_lookup (mod->data.type_names, s)) filtered = TRUE; g_free (s); } else if (mod->kind == MOD_KIND_STEP) { +#ifdef RUNTIME_IL2CPP + if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && sp && + (sp_method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && + !strcmp (sp_method->name, ".cctor") && + (sp_method != ((SingleStepReq*)req->info)->start_method)) + filtered = TRUE; +#else if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && ji && (jinfo_get_method (ji)->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && !strcmp (jinfo_get_method (ji)->name, ".cctor") && @@ -3564,6 +3977,7 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo if (ji->dbg_non_user_code) filtered = TRUE; } +#endif // RUNTIME_IL2CPP } } @@ -3739,7 +4153,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx buffer_add_int (&buf, mono_environment_exitcode_get ()); break; case EVENT_KIND_EXCEPTION: { - EventInfo *ei = (EventInfo *)arg; + DebuggerEventInfo *ei = (DebuggerEventInfo *)arg; buffer_add_objid (&buf, ei->exc); /* * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it @@ -3751,7 +4165,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_USER_BREAK: break; case EVENT_KIND_USER_LOG: { - EventInfo *ei = (EventInfo *)arg; + DebuggerEventInfo *ei = (DebuggerEventInfo *)arg; buffer_add_int (&buf, ei->level); buffer_add_string (&buf, ei->category ? ei->category : ""); buffer_add_string (&buf, ei->message ? ei->message : ""); @@ -3830,7 +4244,7 @@ process_profiler_event (EventKind event, gpointer arg) { int suspend_policy; GSList *events; - EventInfo ei, *ei_arg = NULL; + DebuggerEventInfo ei, *ei_arg = NULL; if (event == EVENT_KIND_TYPE_LOAD) { ei.klass = (MonoClass *)arg; @@ -3898,11 +4312,27 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) } tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); - g_assert (!tls); - // FIXME: Free this somewhere + if (tls) { +#ifndef RUNTIME_IL2CPP + if (!tls->terminated) + MONO_GC_UNREGISTER_ROOT(tls->thread); +#endif // !RUNTIME_IL2CPP +#ifdef RUNTIME_IL2CPP + il2cpp_gc_free_fixed (tls); +#else + g_free (tls); +#endif // RUNTIME_IL2CPP + } +#ifdef RUNTIME_IL2CPP + tls = il2cpp_gc_alloc_fixed(sizeof(DebuggerTlsData)); +#else tls = g_new0 (DebuggerTlsData, 1); +#endif // RUNTIME_IL2CPP MONO_GC_REGISTER_ROOT_SINGLE (tls->thread, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Reference"); - tls->thread = thread; + mono_gc_wbarrier_generic_store((void**)&tls->thread, thread); +#ifdef RUNTIME_IL2CPP + tls->il2cpp_context = il2cpp_debugger_get_thread_context (); +#endif // RUNTIME_IL2CPP mono_native_tls_set_value (debugger_tls_id, tls); DEBUG_PRINTF (1, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls); @@ -3932,12 +4362,14 @@ thread_end (MonoProfiler *prof, uintptr_t tid) if (thread) { mono_g_hash_table_remove (tid_to_thread_obj, GUINT_TO_POINTER (tid)); tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - if (tls) { + if (tls && !tls->terminated) { /* FIXME: Maybe we need to free this instead, but some code can't handle that */ tls->terminated = TRUE; /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */ +#ifndef RUNTIME_IL2CPP MONO_GC_UNREGISTER_ROOT (tls->thread); - tls->thread = NULL; +#endif // !RUNTIME_IL2CPP + mono_gc_wbarrier_generic_store((void**)&tls->thread, NULL); } } mono_loader_unlock (); @@ -4030,6 +4462,7 @@ assembly_load (MonoProfiler *prof, MonoAssembly *assembly) dbg_unlock (); } +#ifndef RUNTIME_IL2CPP static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly) { @@ -4041,6 +4474,7 @@ assembly_unload (MonoProfiler *prof, MonoAssembly *assembly) clear_event_requests_for_assembly (assembly); clear_types_for_assembly (assembly); } +#endif // !RUNTIME_IL2CPP static void send_type_load (MonoClass *klass) @@ -4075,6 +4509,9 @@ send_types_for_domain (MonoDomain *domain, void *user_data) MonoDomain* old_domain; AgentDomainInfo *info = NULL; + if (mono_domain_is_unloading (domain)) + return; + info = get_agent_domain_info (domain); g_assert (info); @@ -4095,15 +4532,20 @@ send_assemblies_for_domain (MonoDomain *domain, void *user_data) GSList *tmp; MonoDomain* old_domain; + if (mono_domain_is_unloading (domain)) + return; + old_domain = mono_domain_get (); mono_domain_set (domain, TRUE); mono_domain_assemblies_lock (domain); - for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { - MonoAssembly* ass = (MonoAssembly *)tmp->data; - emit_assembly_load (ass, NULL); - } + + void *iter = NULL; + MonoAssembly *ass; + while (ass = mono_domain_get_assemblies_iter(domain, &iter)) + emit_assembly_load(ass, NULL); + mono_domain_assemblies_unlock (domain); mono_domain_set (old_domain, TRUE); @@ -4124,6 +4566,10 @@ jit_failed (MonoProfiler *prof, MonoMethod *method) static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) { +#ifdef RUNTIME_IL2CPP + g_hash_table_insert(s_jit_info_hashtable, method, jinfo); +#endif // RUNTIME_IL2CPP + /* * We emit type load events when the first method of the type is JITted, * since the class load profiler callbacks might be called with the @@ -4166,6 +4612,9 @@ typedef struct { guint8 *ip; MonoJitInfo *ji; MonoDomain *domain; +#ifdef RUNTIME_IL2CPP + Il2CppSequencePoint* seq_point; +#endif // RUNTIME_IL2CPP } BreakpointInstance; /* @@ -4208,6 +4657,7 @@ breakpoints_init (void) * Insert the breakpoint described by BP into the method described by * JI. */ +#ifndef RUNTIME_IL2CPP static void insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error) { @@ -4287,6 +4737,8 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo mini_get_interp_callbacks ()->set_breakpoint (ji, inst->ip); } else { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED + if (ji->dbg_ignore) + return; mono_arch_set_breakpoint (ji, inst->ip); #else NOT_IMPLEMENTED; @@ -4323,6 +4775,13 @@ remove_breakpoint (BreakpointInstance *inst) NOT_IMPLEMENTED; #endif } +#endif // !RUNTIME_IL2CPP + +#ifdef RUNTIME_IL2CPP +int32_t il2cpp_mono_methods_match(const MethodInfo* left, const MethodInfo* right); +MonoImage* il2cpp_mono_assembly_get_image(MonoAssembly* assembly); +void il2cpp_mono_free_method_signatures(void); +#endif // RUNTIME_IL2CPP /* * This doesn't take any locks. @@ -4334,6 +4793,9 @@ bp_matches_method (MonoBreakpoint *bp, MonoMethod *method) if (!bp->method) return TRUE; +#ifdef RUNTIME_IL2CPP + return il2cpp_mono_methods_match(bp->method, method); +#else if (method == bp->method) return TRUE; if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method) @@ -4357,6 +4819,7 @@ bp_matches_method (MonoBreakpoint *bp, MonoMethod *method) } return FALSE; +#endif // RUNTIME_IL2CPP } /* @@ -4367,6 +4830,7 @@ bp_matches_method (MonoBreakpoint *bp, MonoMethod *method) static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) { +#ifndef RUNTIME_IL2CPP int i, j; MonoSeqPointInfo *seq_points; MonoDomain *domain; @@ -4415,8 +4879,11 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) } mono_loader_unlock (); +#endif // !RUNTIME_IL2CPP } +#ifndef RUNTIME_IL2CPP + static void set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp, MonoError *error) { @@ -4446,6 +4913,8 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_ insert_breakpoint (seq_points, domain, ji, bp, error); } +#endif // !RUNTIME_IL2CPP + static void clear_breakpoint (MonoBreakpoint *bp); @@ -4494,11 +4963,44 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError method_seq_points = g_ptr_array_new (); mono_loader_lock (); - g_hash_table_iter_init (&iter, domains); - while (g_hash_table_iter_next (&iter, (void**)&domain, NULL)) { - mono_domain_lock (domain); - g_hash_table_iter_init (&iter2, domain_jit_info (domain)->seq_points); - while (g_hash_table_iter_next (&iter2, (void**)&m, (void**)&seq_points)) { +#ifdef RUNTIME_IL2CPP + void *seqPointIter = NULL; + Il2CppSequencePoint *seqPoint; + while(seqPoint = il2cpp_get_method_sequence_points(method, &seqPointIter)) + { + if (seqPoint->ilOffset == bp->il_offset) + { + if (req->event_kind == EVENT_KIND_BREAKPOINT && seqPoint->kind == kSequencePointKind_StepOut) + continue; + + if (req->event_kind == EVENT_KIND_STEP) + { + SingleStepReq *ssreq = (SingleStepReq*)req->info; + if (ssreq->depth == STEP_DEPTH_OUT && seqPoint->kind != kSequencePointKind_StepOut) + continue; + } + + BreakpointInstance* inst = g_new0(BreakpointInstance, 1); + inst->il_offset = bp->il_offset;// it.seq_point.il_offset; + inst->native_offset = 0;// it.seq_point.native_offset; + inst->domain = mono_domain_get(); + inst->seq_point = seqPoint; + + mono_atomic_inc_i32(&seqPoint->isActive); + + mono_loader_lock(); + g_ptr_array_add(bp->children, inst); + mono_loader_unlock(); + } + } +#else + g_hash_table_iter_init (&iter, domains); + while (g_hash_table_iter_next (&iter, (void**)&domain, NULL)) { + if (mono_domain_is_unloading (domain)) + continue; + mono_domain_lock (domain); + g_hash_table_iter_init (&iter2, domain_jit_info (domain)->seq_points); + while (g_hash_table_iter_next (&iter2, (void**)&m, (void**)&seq_points)) { if (bp_matches_method (bp, m)) { /* Save the info locally to simplify the code inside the domain lock */ g_ptr_array_add (methods, m); @@ -4515,6 +5017,7 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError seq_points = (MonoSeqPointInfo *)g_ptr_array_index (method_seq_points, i); set_bp_in_method (domain, m, seq_points, bp, error); } +#endif // RUNTIME_IL2CPP g_ptr_array_add (breakpoints, bp); mono_loader_unlock (); @@ -4531,6 +5034,71 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError return bp; } +#ifdef RUNTIME_IL2CPP +static MonoBreakpoint* set_breakpoint_fast(Il2CppSequencePoint *sp, EventRequest *req, MonoError *error) +{ + MonoBreakpoint *bp; + GHashTableIter iter, iter2; + MonoDomain *domain; + MonoMethod *m; + MonoSeqPointInfo *seq_points; + GPtrArray *methods; + GPtrArray *method_domains; + GPtrArray *method_seq_points; + int i; + + if (error) + error_init(error); + + // FIXME: + // - suspend/resume the vm to prevent code patching problems + // - multiple breakpoints on the same location + // - dynamic methods + // - races + + bp = g_new0(MonoBreakpoint, 1); + bp->method = il2cpp_get_seq_point_method(sp); + bp->il_offset = sp->ilOffset; + bp->req = req; + bp->children = g_ptr_array_new(); + + DEBUG_PRINTF(1, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", bp->method ? mono_method_full_name(bp->method, TRUE) : "", (int)bp->il_offset); + + methods = g_ptr_array_new(); + method_domains = g_ptr_array_new(); + method_seq_points = g_ptr_array_new(); + + mono_loader_lock(); + + BreakpointInstance* inst = g_new0(BreakpointInstance, 1); + inst->il_offset = bp->il_offset;// it.seq_point.il_offset; + inst->native_offset = 0;// it.seq_point.native_offset; + inst->domain = mono_domain_get(); + inst->seq_point = sp; + + mono_atomic_inc_i32(&sp->isActive); + + mono_loader_lock(); + g_ptr_array_add(bp->children, inst); + mono_loader_unlock(); + + g_ptr_array_add(breakpoints, bp); + mono_loader_unlock(); + + g_ptr_array_free(methods, TRUE); + g_ptr_array_free(method_domains, TRUE); + g_ptr_array_free(method_seq_points, TRUE); + + if (error && !mono_error_ok(error)) + { + clear_breakpoint(bp); + return NULL; + } + + return bp; +} +#endif // RUNTIME_IL2CPP + static void clear_breakpoint (MonoBreakpoint *bp) { @@ -4540,7 +5108,11 @@ clear_breakpoint (MonoBreakpoint *bp) for (i = 0; i < bp->children->len; ++i) { BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, i); - remove_breakpoint (inst); +#ifdef RUNTIME_IL2CPP + mono_atomic_dec_i32(&inst->seq_point->isActive); +#else + remove_breakpoint(inst); +#endif // RUNTIME_IL2CPP g_free (inst); } @@ -4572,14 +5144,20 @@ breakpoints_cleanup (void) } } - for (i = 0; i < breakpoints->len; ++i) - g_free (g_ptr_array_index (breakpoints, i)); + if (breakpoints) + { + for (i = 0; i < breakpoints->len; ++i) + g_free (g_ptr_array_index (breakpoints, i)); - g_ptr_array_free (breakpoints, TRUE); - g_hash_table_destroy (bp_locs); + g_ptr_array_free (breakpoints, TRUE); + breakpoints = NULL; + } - breakpoints = NULL; - bp_locs = NULL; + if (bp_locs) + { + g_hash_table_destroy (bp_locs); + bp_locs = NULL; + } mono_loader_unlock (); } @@ -4607,7 +5185,11 @@ clear_breakpoints_for_domain (MonoDomain *domain) BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j); if (inst->domain == domain) { +#ifdef RUNTIME_IL2CPP + mono_atomic_dec_i32(&inst->seq_point->isActive); +#else remove_breakpoint (inst); +#endif // RUNTIME_IL2CPP g_free (inst); @@ -4629,7 +5211,7 @@ static void ss_calculate_framecount (DebuggerTlsData *tls, MonoContext *ctx) { if (!tls->context.valid) mono_thread_state_init_from_monoctx (&tls->context, ctx); - compute_frame_info (tls->thread, tls); + compute_frame_info (tls->thread, tls, FALSE); } static gboolean @@ -4660,12 +5242,14 @@ ensure_jit (StackFrame* frame) static gboolean ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx, MonoMethod* method) { +#ifndef RUNTIME_IL2CPP MonoDebugMethodInfo *minfo; MonoDebugSourceLocation *loc = NULL; +#endif // !RUNTIME_IL2CPP gboolean hit = TRUE; if (req->async_stepout_method == method) { - DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name); + DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), mono_method_get_name(method)); return FALSE; } @@ -4700,6 +5284,7 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t } } +#ifndef RUNTIME_IL2CPP MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method); if (async_method) { for (int i = 0; i < async_method->num_awaits; i++) { @@ -4710,11 +5295,16 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t } mono_debug_free_method_async_debug_info (async_method); } +#endif // !RUNTIME_IL2CPP if (req->size != STEP_SIZE_LINE) return TRUE; /* Have to check whenever a different source line was reached */ + +#ifdef RUNTIME_IL2CPP + NOT_IMPLEMENTED; +#else minfo = mono_debug_lookup_method (method); if (minfo) @@ -4731,25 +5321,93 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t hit = FALSE; } } - + if (loc) { ss_req->last_method = method; ss_req->last_line = loc->row; mono_debug_free_source_location (loc); } +#endif // !RUNTIME_IL2CPP + + return hit; +} + +#ifdef RUNTIME_IL2CPP +/* +* ss_update_il2cpp: +* +* Return FALSE if single stepping needs to continue. +*/ +static gboolean +ss_update_il2cpp(SingleStepReq *req, DebuggerTlsData *tls, MonoContext *ctx, Il2CppSequencePoint *sequencePoint) +{ + gboolean hit = TRUE; + MonoMethod *sp_method = il2cpp_get_seq_point_method(sequencePoint); + + if (il2cpp_mono_methods_match(req->async_stepout_method, sp_method)) + { + DEBUG_PRINTF(1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get(), mono_method_get_name(sp_method)); + return FALSE; + } + + if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) + { + gboolean is_step_out = req->depth == STEP_DEPTH_OUT; + + // Because functions can call themselves recursively, we need to make sure we're stopping at the right stack depth. + // In case of step out, the target is the frame *enclosing* the one where the request was made. + int target_frames = req->nframes + (is_step_out ? -1 : 0); + if (req->nframes > 0 && tls->il2cpp_context->frameCount > 0 && tls->il2cpp_context->frameCount > target_frames) + { + /* Hit the breakpoint in a recursive call, don't halt */ + DEBUG_PRINTF(1, "[%p] Breakpoint at lower frame while stepping %s, continuing single stepping.\n", (gpointer)(gsize)mono_native_thread_id_get(), is_step_out ? "out" : "over"); + return FALSE; + } + } + + if (req->size != STEP_SIZE_LINE) + return TRUE; + + /* Have to check whenever a different source line was reached */ + + if (sequencePoint->lineEnd < 0) + { + DEBUG_PRINTF(1, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer)(gsize)mono_native_thread_id_get(), sequencePoint->ilOffset); + ss_req->last_method = sp_method; + hit = FALSE; + } + else if (sequencePoint->lineEnd >= 0 && sp_method == ss_req->last_method && sequencePoint->lineEnd == ss_req->last_line) + { + if (tls->il2cpp_context->frameCount == req->nframes) + { // If the frame has changed we're clearly not on the same source line. + DEBUG_PRINTF(1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer)(gsize)mono_native_thread_id_get(), sequencePoint->lineEnd); + hit = FALSE; + } + } + + if (sequencePoint->lineEnd >= 0) + { + ss_req->last_method = sp_method; + ss_req->last_line = sequencePoint->lineEnd; + } return hit; } +#endif // RUNTIME_IL2CPP static gboolean breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) { - return bp->method && bp->method->klass->image->assembly == assembly; + return bp->method && mono_image_get_assembly (mono_class_get_image (mono_method_get_class (bp->method))) == assembly; } static gpointer get_this_addr (StackFrame *frame) { +#ifdef RUNTIME_IL2CPP + g_assert_not_reached (); + return NULL; +#else if (frame->ji->is_interp) return mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame); @@ -4760,6 +5418,7 @@ get_this_addr (StackFrame *frame) guint8 *addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS); addr += (gint32)var->offset; return addr; +#endif // RUNTIME_IL2CPP } static MonoMethod* @@ -4768,7 +5427,10 @@ get_set_notification_method (MonoClass* async_builder_class) MonoError error; GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, FALSE, FALSE, &error); mono_error_assert_ok (&error); - g_assert (array->len == 1); + if (array->len == 0) { + g_ptr_array_free (array, TRUE); + return NULL; + } MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0); g_ptr_array_free (array, TRUE); return set_notification_method; @@ -4780,12 +5442,47 @@ get_object_id_for_debugger_method (MonoClass* async_builder_class) MonoError error; GPtrArray *array = mono_class_get_methods_by_name (async_builder_class, "get_ObjectIdForDebugger", 0x24, FALSE, FALSE, &error); mono_error_assert_ok (&error); - g_assert (array->len == 1); + if (array->len != 1) { + g_ptr_array_free (array, TRUE); + //if we don't find method get_ObjectIdForDebugger we try to find the property Task to continue async debug. + MonoProperty *prop = mono_class_get_property_from_name (async_builder_class, "Task"); + if (!prop) { + DEBUG_PRINTF (1, "Impossible to debug async methods.\n"); + return NULL; + } + return prop->get; + } MonoMethod *method = (MonoMethod *)g_ptr_array_index (array, 0); g_ptr_array_free (array, TRUE); return method; } +static MonoClass * +get_class_to_get_builder_field(StackFrame *frame) +{ + MonoError error; + gpointer this_addr = get_this_addr (frame); + MonoClass *original_class = frame->method->klass; + MonoClass *ret; + if (!mono_class_is_valuetype(original_class) && mono_class_is_open_constructed_type (&original_class->byval_arg)) { + MonoObject *this_obj = *(MonoObject**)this_addr; + MonoGenericContext context; + MonoType *inflated_type; + + if (!this_obj) + return NULL; + + context = mono_get_generic_context_from_stack_frame (frame->ji, this_obj->vtable); + inflated_type = mono_class_inflate_generic_type_checked (&original_class->byval_arg, &context, &error); + mono_error_assert_ok (&error); /* FIXME don't swallow the error */ + + ret = mono_class_from_mono_type (inflated_type); + mono_metadata_free_type (inflated_type); + return ret; + } + return original_class; +} + /* Return the address of the AsyncMethodBuilder struct belonging to the state machine method pointed to by FRAME */ static gpointer get_async_method_builder (StackFrame *frame) @@ -4794,20 +5491,23 @@ get_async_method_builder (StackFrame *frame) MonoClassField *builder_field; gpointer builder; guint8 *this_addr; + MonoClass* klass = frame->method->klass; - builder_field = mono_class_get_field_from_name (frame->method->klass, "<>t__builder"); - g_assert (builder_field); + klass = get_class_to_get_builder_field(frame); + builder_field = mono_class_get_field_from_name_full (klass, "<>t__builder", NULL); + if (!builder_field) + return NULL; this_addr = get_this_addr (frame); if (!this_addr) return NULL; - if (frame->method->klass->valuetype) { + if (mono_class_is_valuetype (klass)) { guint8 *vtaddr = *(guint8**)this_addr; - builder = (char*)vtaddr + builder_field->offset - sizeof (MonoObject); + builder = (char*)vtaddr + mono_field_get_offset (builder_field) - sizeof (MonoObject); } else { this_obj = *(MonoObject**)this_addr; - builder = (char*)this_obj + builder_field->offset; + builder = (char*)this_obj + mono_field_get_offset (builder_field); } return builder; @@ -4835,8 +5535,9 @@ get_this_async_id (StackFrame *frame) if (!builder) return 0; - builder_field = mono_class_get_field_from_name (frame->method->klass, "<>t__builder"); - g_assert (builder_field); + builder_field = mono_class_get_field_from_name (get_class_to_get_builder_field(frame), "<>t__builder"); + if (!builder_field) + return 0; tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); if (tls) { @@ -4844,7 +5545,12 @@ get_this_async_id (StackFrame *frame) tls->disable_breakpoints = TRUE; } - method = get_object_id_for_debugger_method (mono_class_from_mono_type (builder_field->type)); + method = get_object_id_for_debugger_method (mono_class_from_mono_type (mono_field_get_type (builder_field))); + if (!method) { + if (tls) + tls->disable_breakpoints = old_disable_breakpoints; + return 0; + } obj = mono_runtime_try_invoke (method, builder, NULL, &ex, &error); mono_error_assert_ok (&error); @@ -4854,27 +5560,38 @@ get_this_async_id (StackFrame *frame) return get_objid (obj); } -static void +// Returns true if TaskBuilder has NotifyDebuggerOfWaitCompletion method +// false if not(AsyncVoidBuilder) +static gboolean set_set_notification_for_wait_completion_flag (StackFrame *frame) { - MonoClassField *builder_field = mono_class_get_field_from_name (frame->method->klass, "<>t__builder"); - g_assert (builder_field); + MonoClassField *builder_field = mono_class_get_field_from_name (get_class_to_get_builder_field(frame), "<>t__builder"); + if (!builder_field) + return FALSE; gpointer builder = get_async_method_builder (frame); - g_assert (builder); + if (!builder) + return FALSE; void* args [1]; gboolean arg = TRUE; MonoError error; args [0] = &arg; - mono_runtime_invoke_checked (get_set_notification_method (mono_class_from_mono_type (builder_field->type)), builder, args, &error); + MonoMethod *method = get_set_notification_method (mono_class_from_mono_type (mono_field_get_type (builder_field))); + if (method == NULL) + return FALSE; + mono_runtime_invoke_checked (method, builder, args, &error); mono_error_assert_ok (&error); + return TRUE; } -static MonoMethod* notify_debugger_of_wait_completion_method_cache = NULL; +static MonoMethod* notify_debugger_of_wait_completion_method_cache; static MonoMethod* get_notify_debugger_of_wait_completion_method (void) { +#if IL2CPP_DOTS + return NULL; +#else if (notify_debugger_of_wait_completion_method_cache != NULL) return notify_debugger_of_wait_completion_method_cache; MonoError error; @@ -4885,8 +5602,10 @@ get_notify_debugger_of_wait_completion_method (void) notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0); g_ptr_array_free (array, TRUE); return notify_debugger_of_wait_completion_method_cache; +#endif // IL2CPP_DOTS } +#ifndef RUNTIME_IL2CPP static void process_breakpoint (DebuggerTlsData *tls, gboolean from_signal) { @@ -4957,7 +5676,7 @@ process_breakpoint (DebuggerTlsData *tls, gboolean from_signal) g_assert (found_sp); - DEBUG_PRINTF (1, "[%p] Breakpoint hit, method=%s, ip=%p, [il=0x%x,native=0x%x].\n", (gpointer) (gsize) mono_native_thread_id_get (), method->name, ip, sp.il_offset, native_offset); + DEBUG_PRINTF (1, "[%p] Breakpoint hit, method=%s, ip=%p, [il=0x%x,native=0x%x].\n", (gpointer) (gsize) mono_native_thread_id_get (), mono_method_get_name(method), ip, sp.il_offset, native_offset); bp = NULL; for (i = 0; i < breakpoints->len; ++i) { @@ -5039,9 +5758,9 @@ process_breakpoint (DebuggerTlsData *tls, gboolean from_signal) if (ss_reqs->len > 0) ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy); - if (bp_reqs->len > 0) + else if (bp_reqs->len > 0) bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy); - if (kind != EVENT_KIND_BREAKPOINT) + else if (kind != EVENT_KIND_BREAKPOINT) enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy); mono_loader_unlock (); @@ -5054,12 +5773,13 @@ process_breakpoint (DebuggerTlsData *tls, gboolean from_signal) * resume. */ if (ss_events) - process_event (EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy); + process_event (EVENT_KIND_STEP, method, sp.il_offset, ctx, ss_events, suspend_policy); if (bp_events) - process_event (kind, method, 0, ctx, bp_events, suspend_policy); + process_event (kind, method, sp.il_offset, ctx, bp_events, suspend_policy); if (enter_leave_events) - process_event (kind, method, 0, ctx, enter_leave_events, suspend_policy); + process_event (kind, method, sp.il_offset, ctx, enter_leave_events, suspend_policy); } +#endif // !RUNTIME_IL2CPP /* Process a breakpoint/single step event after resuming from a signal handler */ static void @@ -5083,11 +5803,13 @@ process_signal_event (void (*func) (DebuggerTlsData*, gboolean)) g_assert_not_reached (); } +#ifndef RUNTIME_IL2CPP static void process_breakpoint_from_signal (void) { process_signal_event (process_breakpoint); } +#endif // !RUNTIME_IL2CPP static void resume_from_signal_handler (void *sigctx, void *func) @@ -5120,6 +5842,7 @@ resume_from_signal_handler (void *sigctx, void *func) #endif } +#ifndef RUNTIME_IL2CPP void mono_debugger_agent_breakpoint_hit (void *sigctx) { @@ -5130,6 +5853,7 @@ mono_debugger_agent_breakpoint_hit (void *sigctx) */ resume_from_signal_handler (sigctx, process_breakpoint_from_signal); } +#endif // !RUNTIME_IL2CPP typedef struct { gboolean found; @@ -5169,10 +5893,12 @@ mono_debugger_agent_user_break (void) memset (&data, 0, sizeof (UserBreakCbData)); data.ctx = &ctx; +#ifndef RUNTIME_IL2CPP /* Obtain a context */ MONO_CONTEXT_SET_IP (&ctx, NULL); mono_walk_stack_with_ctx (user_break_cb, NULL, (MonoUnwindOptions)0, &data); g_assert (data.found); +#endif // !RUNTIME_IL2CPP mono_loader_lock (); events = create_event_list (EVENT_KIND_USER_BREAK, NULL, NULL, NULL, &suspend_policy); @@ -5200,6 +5926,71 @@ ss_depth_to_string (StepDepth depth) } } +#ifdef RUNTIME_IL2CPP +static void +process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal, Il2CppSequencePoint* sequencePoint) +{ + GPtrArray *reqs; + int suspend_policy; + GSList *events; + MonoContext *ctx = &tls->restore_state.ctx; + + if (suspend_count > 0) { + /* Fastpath during invokes, see in process_suspend () */ + if (suspend_count - tls->resume_count == 0) + return; + process_suspend (tls, ctx); + return; + } + + if (!ss_req) + // FIXME: A suspend race + return; + + if (mono_thread_internal_current () != ss_req->thread) + return; + + reqs = g_ptr_array_new (); + + mono_loader_lock (); + + g_ptr_array_add (reqs, ss_req->req); + + events = create_event_list(EVENT_KIND_STEP, reqs, sequencePoint, NULL, &suspend_policy); + + g_ptr_array_free (reqs, TRUE); + + mono_loader_unlock (); + + Il2CppSequencePoint* sequence_pt = tls->il2cpp_context->executionContexts[tls->il2cpp_context->frameCount - 1]->currentSequencePoint; + MonoMethod *sp_method = il2cpp_get_seq_point_method(sequence_pt); + + /* + * This could be in ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method, + * hence we prefer this check here. + */ + if (ss_req->user_assemblies) + { + gboolean found = FALSE; + for (int k = 0; ss_req->user_assemblies[k]; k++) + { + if (ss_req->user_assemblies[k] == mono_image_get_assembly (mono_class_get_image (mono_method_get_class (sp_method)))) + { + found = TRUE; + break; + } + } + + if (!found) + return; + } + + if(!ss_update_il2cpp(ss_req,tls,ctx,sequence_pt)) + return; + + process_event(EVENT_KIND_STEP, sp_method, sequence_pt->ilOffset, NULL, events, suspend_policy); +} +#else static void process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) { @@ -5253,7 +6044,7 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) * Stopping in memset makes half-initialized vtypes visible. * Stopping in memcpy makes half-copied vtypes visible. */ - if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) + if (method->klass == mono_defaults.string_class && (!strcmp (mono_method_get_name(method), "memset") || strstr (mono_method_get_name(method), "memcpy"))) return; /* @@ -5271,7 +6062,6 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) return; } - /* * The ip points to the instruction causing the single step event, which is before * the offset recorded in the seq point map, so find the next seq point after ip. @@ -5291,7 +6081,7 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) && (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && - !strcmp (method->name, ".cctor")) + !strcmp (mono_method_get_name(method), ".cctor")) return; // FIXME: Has to lock earlier @@ -5310,6 +6100,7 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) process_event (EVENT_KIND_STEP, jinfo_get_method (ji), il_offset, ctx, events, suspend_policy); } +#endif // RUNTIME_IL2CPP static void process_single_step (void) @@ -5348,6 +6139,28 @@ mono_debugger_agent_single_step_event (void *sigctx) resume_from_signal_handler (sigctx, process_single_step); } +#ifdef RUNTIME_IL2CPP +void +debugger_agent_single_step_from_context (MonoContext *ctx, Il2CppSequencePoint* sequencePoint) +{ + DebuggerTlsData *tls; + MonoThreadUnwindState orig_restore_state; + + tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + /* Fastpath during invokes, see in process_suspend () */ + if (tls && suspend_count && suspend_count - tls->resume_count == 0) + return; + + if (is_debugger_thread ()) + return; + + g_assert (tls); + + save_thread_context(NULL); + + process_single_step_inner(tls, FALSE, sequencePoint); +} +#else void debugger_agent_single_step_from_context (MonoContext *ctx) { @@ -5374,7 +6187,9 @@ debugger_agent_single_step_from_context (MonoContext *ctx) memcpy (ctx, &tls->restore_state.ctx, sizeof (MonoContext)); memcpy (&tls->restore_state, &orig_restore_state, sizeof (MonoThreadUnwindState)); } +#endif // RUNTIME_IL2CPP +#ifndef RUNTIME_IL2CPP void debugger_agent_breakpoint_from_context (MonoContext *ctx) { @@ -5401,6 +6216,7 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx) if (MONO_CONTEXT_GET_IP (ctx) == orig_ip - 1) MONO_CONTEXT_SET_IP (ctx, orig_ip); } +#endif // !RUNTIME_IL2CPP /* * start_single_stepping: @@ -5413,10 +6229,13 @@ start_single_stepping (void) { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED int val = mono_atomic_inc_i32 (&ss_count); + UPDATE_PAUSE_STATE(); if (val == 1) { mono_arch_start_single_stepping (); +#ifndef RUNTIME_IL2CPP mini_get_interp_callbacks ()->start_single_stepping (); +#endif // !RUNTIME_IL2CPP } #else g_assert_not_reached (); @@ -5428,10 +6247,13 @@ stop_single_stepping (void) { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED int val = mono_atomic_dec_i32 (&ss_count); + UPDATE_PAUSE_STATE(); if (val == 0) { mono_arch_stop_single_stepping (); +#ifndef RUNTIME_IL2CPP mini_get_interp_callbacks ()->stop_single_stepping (); +#endif // !RUNTIME_IL2CPP } #else g_assert_not_reached (); @@ -5461,6 +6283,7 @@ ss_stop (SingleStepReq *ss_req) if (ss_req->global) { stop_single_stepping (); ss_req->global = FALSE; + UPDATE_PAUSE_STATE(); } } @@ -5522,7 +6345,7 @@ ss_bp_hash (gconstpointer data) */ static void ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache, - MonoMethod *method, guint32 il_offset) + MonoMethod *method, guint32 il_offset) { // This list is getting too long, switch to using the hash table if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) { @@ -5543,6 +6366,35 @@ ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_ } } +#ifdef RUNTIME_IL2CPP +static void ss_bp_add_one_il2cpp(SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache, Il2CppSequencePoint *sp) +{ + // This list is getting too long, switch to using the hash table + if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) + { + *ss_req_bp_cache = g_hash_table_new(ss_bp_hash, ss_bp_eq); + for (GSList *l = ss_req->bps; l; l = l->next) + g_hash_table_insert(*ss_req_bp_cache, l->data, l->data); + } + + MonoMethod *sp_method = il2cpp_get_seq_point_method(sp); + + if (ss_bp_is_unique(ss_req->bps, *ss_req_bp_cache, sp_method, sp->ilOffset)) + { + // Create and add breakpoint + MonoBreakpoint *bp = set_breakpoint_fast(sp, ss_req->req, NULL); + ss_req->bps = g_slist_append(ss_req->bps, bp); + if (*ss_req_bp_cache) + g_hash_table_insert(*ss_req_bp_cache, bp, bp); + (*ss_req_bp_count)++; + } + else + { + DEBUG_PRINTF(1, "[dbg] Candidate breakpoint at %s:[il=0x%x] is a duplicate for this step request, will not add.\n", mono_method_full_name(sp_method, TRUE), (int)sp->ilOffset); + } +} +#endif // RUNTIME_IL2CPP + static gboolean is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info) { @@ -5573,6 +6425,76 @@ is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info) * belong to the same thread as CTX. * If FRAMES is not-null, use that instead of tls->frames for placing breakpoints etc. */ +#ifdef RUNTIME_IL2CPP +static void +ss_start_il2cpp(SingleStepReq *ss_req, DebuggerTlsData *tls, Il2CppSequencePoint *catchFrameSp) +{ + // When 8 or more entries are in bps, we build a hash table to serve as a set of breakpoints. + // Recreating this on each pass is a little wasteful but at least keeps behavior linear. + int ss_req_bp_count = g_slist_length(ss_req->bps); + GHashTable *ss_req_bp_cache = NULL; + gboolean enable_global = FALSE; + + /* Stop the previous operation */ + ss_stop(ss_req); + + DEBUG_PRINTF(0, "Step depth: %d\n", ss_req->depth); + + if (catchFrameSp) { + ss_bp_add_one_il2cpp (ss_req, &ss_req_bp_count, &ss_req_bp_cache, catchFrameSp); + } else { + if (ss_req->depth == STEP_DEPTH_OVER) + { + MonoMethod* currentMethod = il2cpp_get_seq_point_method(tls->il2cpp_context->executionContexts[tls->il2cpp_context->frameCount - 1]->currentSequencePoint); + + void *seqPointIter = NULL; + Il2CppSequencePoint *seqPoint; + while(seqPoint = il2cpp_get_method_sequence_points(currentMethod, &seqPointIter)) + { + if (seqPoint->kind != kSequencePointKind_Normal) + continue; + + if (il2cpp_mono_methods_match(il2cpp_get_seq_point_method(seqPoint), currentMethod)) + ss_bp_add_one_il2cpp(ss_req, &ss_req_bp_count, &ss_req_bp_cache, seqPoint); + } + } + + if (tls->il2cpp_context->frameCount > 1) + { + Il2CppSequencePoint* sequencePointForStepOut = tls->il2cpp_context->executionContexts[tls->il2cpp_context->frameCount - 2]->currentSequencePoint; + g_assert(sequencePointForStepOut->kind == kSequencePointKind_StepOut); + ss_bp_add_one_il2cpp(ss_req, &ss_req_bp_count, &ss_req_bp_cache, sequencePointForStepOut); + } + + if (ss_req->depth == STEP_DEPTH_INTO) + { + /* Enable global stepping so we stop at method entry too */ + enable_global = TRUE; + } + } + + if (ss_req_bp_cache) + g_hash_table_destroy(ss_req_bp_cache); + + if (enable_global) + { + DEBUG_PRINTF(1, "[dbg] Turning on global single stepping.\n"); + ss_req->global = TRUE; + start_single_stepping(); + } + else if (!ss_req->bps) + { + DEBUG_PRINTF(1, "[dbg] Turning on global single stepping.\n"); + ss_req->global = TRUE; + start_single_stepping(); + } + else + { + ss_req->global = FALSE; + } + UPDATE_PAUSE_STATE(); +} +#else static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch, StackFrame **frames, int nframes) @@ -5593,6 +6515,8 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI /* Stop the previous operation */ ss_stop (ss_req); + gboolean locked = FALSE; + /* * Implement single stepping using breakpoints if possible. */ @@ -5605,7 +6529,11 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI /* Need parent frames */ if (!tls->context.valid) mono_thread_state_init_from_monoctx (&tls->context, ctx); - compute_frame_info (tls->thread, tls); + + mono_loader_lock (); + locked = TRUE; + + compute_frame_info (tls->thread, tls, FALSE); frames = tls->frames; nframes = tls->frame_count; } @@ -5643,6 +6571,8 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI if (ss_req_bp_cache) g_hash_table_destroy (ss_req_bp_cache); mono_debug_free_method_async_debug_info (asyncMethod); + if (locked) + mono_loader_unlock (); return; } } @@ -5652,14 +6582,18 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this } if (ss_req->depth == STEP_DEPTH_OUT) { - set_set_notification_for_wait_completion_flag (frames [0]); - ss_req->async_id = get_this_async_id (frames [0]); - ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method (); - ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0); - if (ss_req_bp_cache) - g_hash_table_destroy (ss_req_bp_cache); - mono_debug_free_method_async_debug_info (asyncMethod); - return; + //If we are inside `async void` method, do normal step-out + if (set_set_notification_for_wait_completion_flag (frames [0])) { + ss_req->async_id = get_this_async_id (frames [0]); + ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method (); + ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0); + if (ss_req_bp_cache) + g_hash_table_destroy (ss_req_bp_cache); + mono_debug_free_method_async_debug_info (asyncMethod); + if (locked) + mono_loader_unlock (); + return; + } } } @@ -5752,6 +6686,26 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI ss_req->depth = STEP_DEPTH_INTO; } + if (ss_req->depth == STEP_DEPTH_OVER) { + /* Need to stop in catch clauses as well */ + for (i = 0; i < nframes; ++i) { + StackFrame *frame = frames[i]; + + if (frame->ji) { + MonoJitInfo *jinfo = frame->ji; + for (j = 0; j < jinfo->num_clauses; ++j) { + MonoJitExceptionInfo *ei = &jinfo->clauses[j]; + + found_sp = mono_find_next_seq_point_for_native_offset(frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp); + sp = (found_sp) ? &local_sp : NULL; + + if (found_sp) + ss_bp_add_one(ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, sp->il_offset); + } + } + } + } + if (ss_req->depth == STEP_DEPTH_INTO) { /* Enable global stepping so we stop at method entry too */ enable_global = TRUE; @@ -5776,10 +6730,15 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI } else { ss_req->global = FALSE; } + UPDATE_PAUSE_STATE(); if (ss_req_bp_cache) g_hash_table_destroy (ss_req_bp_cache); + + if (locked) + mono_loader_unlock (); } +#endif // RUNTIME_IL2CPP /* * Start single stepping of thread THREAD @@ -5793,7 +6752,9 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte SeqPoint local_sp; gboolean found_sp; MonoMethod *method = NULL; +#ifndef RUNTIME_IL2CPP MonoDebugMethodInfo *minfo; +#endif // !RUNTIME_IL2CPP gboolean step_to_catch = FALSE; gboolean set_ip = FALSE; StackFrame **frames = NULL; @@ -5831,6 +6792,25 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); mono_loader_unlock (); g_assert (tls); + +#ifdef RUNTIME_IL2CPP + ss_req->nframes = tls->il2cpp_context->frameCount; + Il2CppSequencePoint *catchFrameSp = NULL; + + if (tls->il2cpp_context->frameCount > 0) + { + Il2CppSequencePoint* seq_point = tls->il2cpp_context->executionContexts[tls->il2cpp_context->frameCount - 1]->currentSequencePoint; + MonoMethod *sp_method = il2cpp_get_seq_point_method(seq_point); + ss_req->start_method = sp_method; + ss_req->last_method = sp_method; + ss_req->last_line = seq_point->lineEnd; + + if (tls->exception) + catchFrameSp = il2cpp_find_catch_sequence_point(tls); + } + + ss_start_il2cpp(ss_req, tls, catchFrameSp); +#else if (!tls->context.valid) { DEBUG_PRINTF (1, "Received a single step request on a thread with no managed frames."); return ERR_INVALID_ARGUMENT; @@ -5853,7 +6833,10 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte * We are stopped at a throw site. Stepping should go to the catch site. */ frame = tls->catch_frame; - g_assert (frame.type == FRAME_TYPE_MANAGED || frame.type == FRAME_TYPE_INTERP); + if (frame.type != FRAME_TYPE_MANAGED && frame.type != FRAME_TYPE_INTERP) { + DEBUG_PRINTF (1, "Current frame is not managed nor interpreter.\n"); + return ERR_INVALID_ARGUMENT; + } /* * Find the seq point corresponding to the landing site ip, which is the first seq @@ -5863,7 +6846,11 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte sp = (found_sp)? &local_sp : NULL; if (!sp) no_seq_points_found (frame.method, frame.native_offset); - g_assert (sp); + + if (!sp) { + DEBUG_PRINTF (1, "Could not find next sequence point.\n"); + return ERR_INVALID_ARGUMENT; + } method = frame.method; @@ -5879,7 +6866,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte if (frames && nframes) frame = frames [0]; } else { - compute_frame_info (thread, tls); + compute_frame_info (thread, tls, FALSE); if (tls->frame_count) frame = tls->frames [0]; @@ -5909,7 +6896,10 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte sp = (found_sp)? &local_sp : NULL; if (!sp) no_seq_points_found (frame->method, frame->native_offset); - g_assert (sp); + if (!sp) { + DEBUG_PRINTF (1, "Could not find next sequence point.\n"); + return ERR_INVALID_ARGUMENT; + } method = frame->method; } } @@ -5921,6 +6911,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte if (frames) free_frames (frames, nframes); +#endif // RUNTIME_IL2CPP return ERR_NONE; } @@ -5965,7 +6956,7 @@ mono_debugger_agent_debug_log (int level, MonoString *category, MonoString *mess MonoError error; int suspend_policy; GSList *events; - EventInfo ei; + DebuggerEventInfo ei; if (!agent_config.enabled) return; @@ -6005,12 +6996,12 @@ mono_debugger_agent_unhandled_exception (MonoException *exc) { int suspend_policy; GSList *events; - EventInfo ei; + DebuggerEventInfo ei; if (!inited) return; - memset (&ei, 0, sizeof (EventInfo)); + memset (&ei, 0, sizeof (DebuggerEventInfo)); ei.exc = (MonoObject*)exc; mono_loader_lock (); @@ -6021,6 +7012,168 @@ mono_debugger_agent_unhandled_exception (MonoException *exc) } #endif +#ifdef RUNTIME_IL2CPP +static Il2CppCatchPoint* il2cpp_find_catch_point_in_method(const MonoMethod *method, int8_t tryId, MonoException *exc) +{ + Il2CppCatchPoint *cp; + int8_t nextId = tryId, id; + + while(nextId >= 0) + { + id = nextId; + nextId = -1; + void *catchPointIter = NULL; + while (cp = il2cpp_get_method_catch_points(method, &catchPointIter)) + { + if (cp->tryId == id) + { + MonoClass *catchType = il2cpp_get_class_from_index(cp->catchTypeIndex); + nextId = cp->parentTryId; + if (catchType != NULL && mono_class_is_assignable_from (catchType, mono_object_get_class (&exc->object))) + return cp; + } + } + } + + return NULL; +} + +static Il2CppSequencePoint* il2cpp_find_catch_sequence_point(DebuggerTlsData *tls) +{ + int frameIndex = tls->il2cpp_context->frameCount - 1; + while (frameIndex >= 0) + { + Il2CppCatchPoint* cp = il2cpp_find_catch_point_in_method(tls->il2cpp_context->executionContexts[frameIndex]->method, tls->il2cpp_context->executionContexts[frameIndex]->tryId, tls->exception); + if (cp) + { + Il2CppSequencePoint* sp = il2cpp_get_seq_point_from_catch_point(cp); + if (sp) + return sp; + else + return NULL; + } + + --frameIndex; + } + + return NULL; +} + +static void il2cpp_find_catch_sequence_point_from_exeption(DebuggerTlsData *tls, MonoException *exc, Il2CppCatchPoint **catchPt, Il2CppSequencePoint **seqPt) +{ + *catchPt = NULL; + *seqPt = NULL; + + int frameIndex = tls->il2cpp_context->frameCount - 1; + while (frameIndex >= 0) + { + *catchPt = il2cpp_find_catch_point_in_method(tls->il2cpp_context->executionContexts[frameIndex]->method, tls->il2cpp_context->executionContexts[frameIndex]->tryId, exc); + if (*catchPt) + { + *seqPt = il2cpp_get_seq_point_from_catch_point(*catchPt); + return; + } + + --frameIndex; + } + + return; +} + +void +unity_debugger_agent_handle_exception(MonoException *exc) +{ + int i, j, suspend_policy; + GSList *events; + DebuggerEventInfo ei; + DebuggerTlsData *tls = NULL; + + if (thread_to_tls != NULL) + { + MonoInternalThread *thread = mono_thread_internal_current(); + + mono_loader_lock(); + tls = (DebuggerTlsData *)mono_g_hash_table_lookup(thread_to_tls, thread); + mono_loader_unlock(); + + if (tls && tls->abort_requested) + return; + if (tls && tls->disable_breakpoints) + return; + } + + memset(&ei, 0, sizeof(DebuggerEventInfo)); + + ei.exc = (MonoObject*)exc; + ei.caught = FALSE; + Il2CppCatchPoint *catchPt; + Il2CppSequencePoint *seqPt; + + if (tls) + { + il2cpp_find_catch_sequence_point_from_exeption(tls, exc, &catchPt, &seqPt); + if (catchPt) + ei.caught = TRUE; + } + + mono_loader_lock(); + + MonoMethod *method = tls->il2cpp_context->executionContexts[tls->il2cpp_context->frameCount - 1]->method; + + /* Treat exceptions which are caught in non-user code as unhandled */ + for (i = 0; i < event_requests->len; ++i) + { + EventRequest *req = (EventRequest *)g_ptr_array_index(event_requests, i); + if (req->event_kind != EVENT_KIND_EXCEPTION) + continue; + + for (j = 0; j < req->nmodifiers; ++j) + { + Modifier *mod = &req->modifiers[j]; + + if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && method) + { + int k; + gboolean found = FALSE; + MonoAssembly **assemblies = mod->data.assemblies; + + if (assemblies) + { + for (k = 0; assemblies[k]; ++k) + if (assemblies[k] == mono_image_get_assembly (mono_class_get_image (mono_method_get_class (method)))) + found = TRUE; + } + if (!found) + ei.caught = FALSE; + } + } + } + + events = create_event_list(EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy); + mono_loader_unlock(); + + if (tls && ei.caught) + { + if (!ss_req || !ss_req->bps) { + tls->exception = exc; + } else if (ss_req->bps && seqPt) { + int ss_req_bp_count = g_slist_length(ss_req->bps); + GHashTable *ss_req_bp_cache = NULL; + + ss_bp_add_one_il2cpp(ss_req, &ss_req_bp_count, &ss_req_bp_cache, seqPt); + + if (ss_req_bp_cache) + g_hash_table_destroy(ss_req_bp_cache); + } + } + + process_event(EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy); + + if (tls) + tls->exception = NULL; +} +#endif // RUNTIME_IL2CPP + void mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) @@ -6028,7 +7181,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx int i, j, suspend_policy; GSList *events; MonoJitInfo *ji, *catch_ji; - EventInfo ei; + DebuggerEventInfo ei; DebuggerTlsData *tls = NULL; if (thread_to_tls != NULL) { @@ -6044,7 +7197,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx return; } - memset (&ei, 0, sizeof (EventInfo)); + memset (&ei, 0, sizeof (DebuggerEventInfo)); /* Just-In-Time debugging */ if (!catch_ctx) { @@ -6065,7 +7218,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx for (l = agent_config.onthrow; l; l = l->next) { char *ex_type = (char *)l->data; - char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg); + char *f = mono_type_full_name (mono_class_get_type (mono_object_get_class (&exc->object))); if (!strcmp (ex_type, "") || !strcmp (ex_type, f)) found = TRUE; @@ -6116,7 +7269,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx if (assemblies) { for (k = 0; assemblies [k]; ++k) - if (assemblies [k] == jinfo_get_method (catch_ji)->klass->image->assembly) + if (assemblies [k] == mono_image_get_assembly (mono_class_get_image (mono_method_get_class (jinfo_get_method (catch_ji))))) found = TRUE; } if (!found) @@ -6204,8 +7357,219 @@ mono_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, * AS_VTYPE determines whenever to treat primitive types as primitive types or * vtypes. */ -static void -buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, +#ifdef RUNTIME_IL2CPP +static void buffer_add_value_full(Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, + gboolean as_vtype, GHashTable *parent_vtypes) +{ + MonoObject *obj; + gboolean boxed_vtype = FALSE; + + if (il2cpp_type_is_byref(t)) + { + if (!(*(void**)addr)) + { + /* This can happen with compiler generated locals */ + //printf ("%s\n", mono_type_full_name (t)); + buffer_add_byte(buf, VALUE_TYPE_ID_NULL); + return; + } + g_assert(*(void**)addr); + addr = *(void**)addr; + } + + if (as_vtype) + { + switch (il2cpp_type_get_type(t)) + { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + goto handle_vtype; + break; + default: + break; + } + } + + switch (il2cpp_type_get_type(t)) + { + case MONO_TYPE_VOID: + buffer_add_byte(buf, il2cpp_type_get_type(t)); + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + buffer_add_byte(buf, il2cpp_type_get_type(t)); + buffer_add_int(buf, *(gint8*)addr); + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + buffer_add_byte(buf, il2cpp_type_get_type(t)); + buffer_add_int(buf, *(gint16*)addr); + break; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + buffer_add_byte(buf, il2cpp_type_get_type(t)); + buffer_add_int(buf, *(gint32*)addr); + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + buffer_add_byte(buf, il2cpp_type_get_type(t)); + buffer_add_long(buf, *(gint64*)addr); + break; + case MONO_TYPE_I: + case MONO_TYPE_U: + /* Treat it as a vtype */ + goto handle_vtype; + case MONO_TYPE_PTR: { + gssize val = *(gssize*)addr; + + buffer_add_byte(buf, il2cpp_type_get_type(t)); + buffer_add_long(buf, val); + break; + } + handle_ref: + case MONO_TYPE_STRING: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + obj = *(MonoObject**)addr; + + if (!obj) + { + buffer_add_byte(buf, VALUE_TYPE_ID_NULL); + } + else + { + MonoClass *klass = il2cpp_object_get_class(obj); + if (il2cpp_class_is_valuetype(klass)) + { + t = il2cpp_class_get_type(il2cpp_object_get_class(obj)); + addr = mono_object_unbox(obj); + boxed_vtype = TRUE; + goto handle_vtype; + } + else if (il2cpp_class_get_rank(klass)) + { + buffer_add_byte(buf, il2cpp_type_get_type(il2cpp_class_get_type(klass))); + } + else if (il2cpp_type_get_type(il2cpp_class_get_type(klass)) == MONO_TYPE_GENERICINST) + { + buffer_add_byte(buf, MONO_TYPE_CLASS); + } + else + { + buffer_add_byte(buf, il2cpp_type_get_type(il2cpp_class_get_type(klass))); + } + buffer_add_objid(buf, obj); + } + break; + handle_vtype: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_TYPEDBYREF: { + int nfields; + gpointer iter; + MonoClassField *f; + MonoClass *klass = mono_class_from_mono_type(t); + int vtype_index; + + if (boxed_vtype) + { + /* + * Handle boxed vtypes recursively referencing themselves using fields. + */ + if (!parent_vtypes) + parent_vtypes = g_hash_table_new(NULL, NULL); + vtype_index = GPOINTER_TO_INT(g_hash_table_lookup(parent_vtypes, addr)); + if (vtype_index) + { + if (CHECK_PROTOCOL_VERSION(2, 33)) + { + buffer_add_byte(buf, VALUE_TYPE_ID_PARENT_VTYPE); + buffer_add_int(buf, vtype_index - 1); + } + else + { + /* The client can't handle PARENT_VTYPE */ + buffer_add_byte(buf, VALUE_TYPE_ID_NULL); + } + break; + } + else + { + g_hash_table_insert(parent_vtypes, addr, GINT_TO_POINTER(g_hash_table_size(parent_vtypes) + 1)); + } + } + + buffer_add_byte(buf, MONO_TYPE_VALUETYPE); + buffer_add_byte(buf, mono_class_is_enum (klass)); + buffer_add_typeid(buf, domain, klass); + + nfields = 0; + iter = NULL; + while ((f = mono_class_get_fields(klass, &iter))) + { + if (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted(f)) + continue; + nfields++; + } + buffer_add_int(buf, nfields); + + iter = NULL; + while ((f = mono_class_get_fields(klass, &iter))) + { + if (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted(f)) + continue; + buffer_add_value_full(buf, mono_field_get_type (f), (guint8*)addr + mono_field_get_offset (f) - sizeof(MonoObject), domain, FALSE, parent_vtypes); + } + + if (boxed_vtype) + { + g_hash_table_remove(parent_vtypes, addr); + if (g_hash_table_size(parent_vtypes) == 0) + { + g_hash_table_destroy(parent_vtypes); + parent_vtypes = NULL; + } + } + break; + } + case MONO_TYPE_GENERICINST: + if (mono_type_generic_inst_is_valuetype(t)) + { + goto handle_vtype; + } + else + { + goto handle_ref; + } + break; + default: + NOT_IMPLEMENTED; + } +} +#else +static void buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, gboolean as_vtype, GHashTable *parent_vtypes) { MonoObject *obj; @@ -6283,6 +7647,8 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, buffer_add_byte (buf, t->type); buffer_add_long (buf, val); + if (CHECK_PROTOCOL_VERSION(2, 46)) + buffer_add_typeid (buf, domain, mono_class_from_mono_type (t)); break; } handle_ref: @@ -6296,7 +7662,7 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, if (!obj) { buffer_add_byte (buf, VALUE_TYPE_ID_NULL); } else { - if (obj->vtable->klass->valuetype) { + if (mono_class_is_valuetype (mono_object_get_class (obj))) { t = &obj->vtable->klass->byval_arg; addr = mono_object_unbox (obj); boxed_vtype = TRUE; @@ -6385,6 +7751,7 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, NOT_IMPLEMENTED; } } +#endif // RUNTIME_IL2CPP static void buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) @@ -6395,7 +7762,7 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) static gboolean obj_is_of_type (MonoObject *obj, MonoType *t) { - MonoClass *klass = obj->vtable->klass; + MonoClass *klass = mono_object_get_class (obj); if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) { if (mono_class_is_transparent_proxy (klass)) { klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class; @@ -6432,7 +7799,7 @@ decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 if (t && klass != mono_class_from_mono_type (t)) { char *name = mono_type_full_name (t); - char *name2 = mono_type_full_name (&klass->byval_arg); + char *name2 = mono_type_full_name (mono_class_get_type(klass)); DEBUG_PRINTF (1, "[%p] Expected value of type %s, got %s.\n", (gpointer) (gsize) mono_native_thread_id_get (), name, name2); g_free (name); g_free (name2); @@ -6441,11 +7808,11 @@ decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 nfields = decode_int (buf, &buf, limit); while ((f = mono_class_get_fields (klass, &iter))) { - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) + if (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC) continue; - if (mono_field_is_deleted (f)) + if (mono_field_is_deleted(f)) continue; - err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit); + err = decode_value (mono_field_get_type (f), domain, (guint8*)addr + mono_field_get_offset (f) - sizeof (MonoObject), buf, &buf, limit); if (err != ERR_NONE) return err; nfields --; @@ -6542,11 +7909,11 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, return err; if (!obj) return ERR_INVALID_ARGUMENT; - if (obj->vtable->klass != mono_class_from_mono_type (t)) { - DEBUG_PRINTF (1, "Expected type '%s', got object '%s'\n", mono_type_full_name (t), obj->vtable->klass->name); + if (mono_object_get_class (obj) != mono_class_from_mono_type (t)) { + DEBUG_PRINTF (1, "Expected type '%s', got object '%s'\n", mono_type_full_name (t), mono_class_get_name (mono_object_get_class (obj))); return ERR_INVALID_ARGUMENT; } - memcpy (addr, mono_object_unbox (obj), mono_class_value_size (obj->vtable->klass, NULL)); + memcpy (addr, mono_object_unbox (obj), mono_class_value_size (mono_object_get_class (obj), NULL)); } else { err = decode_vtype (t, domain, addr, buf, &buf, limit); if (err != ERR_NONE) @@ -6567,11 +7934,11 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, if (obj) { if (!obj_is_of_type (obj, t)) { - DEBUG_PRINTF (1, "Expected type '%s', got '%s'\n", mono_type_full_name (t), obj->vtable->klass->name); + DEBUG_PRINTF (1, "Expected type '%s', got '%s'\n", mono_type_full_name (t), mono_class_get_name (mono_object_get_class (obj))); return ERR_INVALID_ARGUMENT; } } - if (obj && obj->vtable->domain != domain) + if (obj && VM_OBJECT_GET_DOMAIN(obj) != domain) return ERR_INVALID_ARGUMENT; mono_gc_wbarrier_generic_store (addr, obj); @@ -6610,7 +7977,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, g_free (vtype_buf); return err; } - *(MonoObject**)addr = mono_value_box_checked (d, klass, vtype_buf, &error); + mono_gc_wbarrier_generic_store((void**)addr, mono_value_box_checked (d, klass, vtype_buf, &error)); mono_error_cleanup (&error); g_free (vtype_buf); } else { @@ -6619,6 +7986,12 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, g_free (name); return ERR_INVALID_ARGUMENT; } + } else if ((t->type == MONO_TYPE_GENERICINST) && + mono_metadata_generic_class_is_valuetype (t->data.generic_class) && + t->data.generic_class->container_class->enumtype){ + err = decode_vtype (t, domain, addr, buf, &buf, limit); + if (err != ERR_NONE) + return err; } else { NOT_IMPLEMENTED; } @@ -6638,7 +8011,7 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 int type = decode_byte (buf, &buf, limit); if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { - MonoType *targ = t->data.generic_class->context.class_inst->type_argv [0]; + MonoType *targ = mono_generic_class_get_context (m_type_get_generic_class (t))->class_inst->type_argv[0]; guint8 *nullable_buf; /* @@ -6677,6 +8050,13 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 return decode_value_internal (t, type, domain, addr, buf, endbuf, limit); } +#ifdef RUNTIME_IL2CPP +static void +add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype) +{ + g_assert_not_reached (); +} +#else static void add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype) { @@ -6763,7 +8143,15 @@ add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo g_assert_not_reached (); } } +#endif // RUNTIME_IL2CPP +#ifdef RUNTIME_IL2CPP +static void +set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, guint8 *val, mgreg_t **reg_locations, MonoContext *restore_ctx) +{ + g_assert_not_reached (); +} +#else static void set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, guint8 *val, mgreg_t **reg_locations, MonoContext *restore_ctx) { @@ -6867,6 +8255,7 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai g_assert_not_reached (); } } +#endif // RUNTIME_IL2CPP static void set_interp_var (MonoType *t, gpointer addr, guint8 *val_buf) @@ -6917,7 +8306,7 @@ clear_assembly_from_modifier (EventRequest *req, Modifier *m, MonoAssembly *asse { int i; - if (m->kind == MOD_KIND_EXCEPTION_ONLY && m->data.exc_class && m->data.exc_class->image->assembly == assembly) + if (m->kind == MOD_KIND_EXCEPTION_ONLY && m->data.exc_class && mono_image_get_assembly (mono_class_get_image (m->data.exc_class)) == assembly) m->kind = MOD_KIND_NONE; if (m->kind == MOD_KIND_ASSEMBLY_ONLY && m->data.assemblies) { int count = 0, match_count = 0, pos; @@ -6997,7 +8386,7 @@ clear_event_requests_for_assembly (MonoAssembly *assembly) static gboolean type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly) { - return (mono_class_get_image ((MonoClass*)klass) == mono_assembly_get_image ((MonoAssembly*)assembly)); + return mono_type_in_image (mono_class_get_type ((MonoClass*)klass), mono_assembly_get_image ((MonoAssembly*)assembly)); } /* @@ -7005,6 +8394,7 @@ type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly * * Clears types from loaded_classes for a given assembly */ +#ifndef RUNTIME_IL2CPP static void clear_types_for_assembly (MonoAssembly *assembly) { @@ -7021,6 +8411,7 @@ clear_types_for_assembly (MonoAssembly *assembly) g_hash_table_foreach_remove (info->loaded_classes, type_comes_from_assembly, assembly); mono_loader_unlock (); } +#endif // !RUNTIME_IL2CPP static void add_thread (gpointer key, gpointer value, gpointer user_data) @@ -7045,9 +8436,11 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 MonoObject *this_arg, *res, *exc = NULL; MonoDomain *domain; guint8 *this_buf; -#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED +#ifndef RUNTIME_IL2CPP +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED MonoLMFExt ext; #endif +#endif // !RUNTIME_IL2CPP MonoStopwatch watch; if (invoke->method) { @@ -7055,7 +8448,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 * Invoke this method directly, currently only Environment.Exit () is supported. */ this_arg = NULL; - DEBUG_PRINTF (1, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer) (gsize) mono_native_thread_id_get (), mono_method_full_name (invoke->method, TRUE), this_arg ? this_arg->vtable->klass->name : ""); + DEBUG_PRINTF (1, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer) (gsize) mono_native_thread_id_get (), mono_method_full_name (invoke->method, TRUE), this_arg ? mono_class_get_name (mono_object_get_class (this_arg)) : ""); mono_runtime_try_invoke (invoke->method, NULL, invoke->args, &exc, &error); mono_error_assert_ok (&error); @@ -7068,11 +8461,15 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 return err; sig = mono_method_signature (m); - if (m->klass->valuetype) + if (mono_class_is_valuetype (m->klass)) this_buf = (guint8 *)g_alloca (mono_class_instance_size (m->klass)); else this_buf = (guint8 *)g_alloca (sizeof (MonoObject*)); - if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) { + + if (m->is_generic) { + DEBUG_PRINTF (1, "[%p] Error: Attemtping to invoke uninflated generic method %s.\n", (gpointer)(gsize)mono_native_thread_id_get (), mono_method_full_name (m, TRUE)); + return ERR_INVALID_ARGUMENT; + } else if (mono_class_is_valuetype (m->klass) && (m->flags & METHOD_ATTRIBUTE_STATIC)) { /* Should be null */ int type = decode_byte (p, &p, end); if (type != VALUE_TYPE_ID_NULL) { @@ -7080,7 +8477,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 return ERR_INVALID_ARGUMENT; } memset (this_buf, 0, mono_class_instance_size (m->klass)); - } else if (m->klass->valuetype && !strcmp (m->name, ".ctor")) { + } else if (mono_class_is_valuetype(m->klass) && !strcmp (m->name, ".ctor")) { /* Could be null */ guint8 *tmp_p; @@ -7089,17 +8486,19 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 memset (this_buf, 0, mono_class_instance_size (m->klass)); p = tmp_p; } else { - err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end); + err = decode_value(mono_class_get_type (m->klass), domain, this_buf, p, &p, end); + if (err != ERR_NONE) return err; } } else { - err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end); + err = decode_value (mono_class_get_type (m->klass), domain, this_buf, p, &p, end); + if (err != ERR_NONE) return err; } - if (!m->klass->valuetype) + if (!mono_class_is_valuetype (m->klass)) this_arg = *(MonoObject**)this_buf; else this_arg = NULL; @@ -7111,42 +8510,43 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 } m = mono_object_get_virtual_method (this_arg, m); /* Transform this to the format the rest of the code expects it to be */ - if (m->klass->valuetype) { + if (mono_class_is_valuetype (m->klass)) { this_buf = (guint8 *)g_alloca (mono_class_instance_size (m->klass)); memcpy (this_buf, mono_object_unbox (this_arg), mono_class_instance_size (m->klass)); } - } else if ((m->flags & METHOD_ATTRIBUTE_VIRTUAL) && !m->klass->valuetype && invoke->flags & INVOKE_FLAG_VIRTUAL) { + } else if ((m->flags & METHOD_ATTRIBUTE_VIRTUAL) && !mono_class_is_valuetype (m->klass) && invoke->flags & INVOKE_FLAG_VIRTUAL) { if (!this_arg) { DEBUG_PRINTF (1, "[%p] Error: invoke with INVOKE_FLAG_VIRTUAL flag set without this argument.\n", (gpointer) (gsize) mono_native_thread_id_get ()); return ERR_INVALID_ARGUMENT; } m = mono_object_get_virtual_method (this_arg, m); - if (m->klass->valuetype) { + if (mono_class_is_valuetype (m->klass)) { this_buf = (guint8 *)g_alloca (mono_class_instance_size (m->klass)); memcpy (this_buf, mono_object_unbox (this_arg), mono_class_instance_size (m->klass)); } } - DEBUG_PRINTF (1, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer) (gsize) mono_native_thread_id_get (), mono_method_full_name (m, TRUE), this_arg ? this_arg->vtable->klass->name : ""); + DEBUG_PRINTF (1, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer) (gsize) mono_native_thread_id_get (), mono_method_full_name (m, TRUE), this_arg ? mono_class_get_name (mono_object_get_class (this_arg)): ""); - if (this_arg && this_arg->vtable->domain != domain) + if (this_arg && VM_OBJECT_GET_DOMAIN(this_arg) != domain) NOT_IMPLEMENTED; - if (!m->klass->valuetype && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this_arg) { + if (!mono_class_is_valuetype (m->klass) && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this_arg) { if (!strcmp (m->name, ".ctor")) { if (mono_class_is_abstract (m->klass)) return ERR_INVALID_ARGUMENT; else { MonoError error; this_arg = mono_object_new_checked (domain, m->klass, &error); - mono_error_assert_ok (&error); + if (!is_ok (&error)) + return ERR_INVALID_ARGUMENT; } } else { return ERR_INVALID_ARGUMENT; } } - if (this_arg && !obj_is_of_type (this_arg, &m->klass->byval_arg)) + if (this_arg && !obj_is_of_type (this_arg, mono_class_get_type (m->klass))) return ERR_INVALID_ARGUMENT; nargs = decode_int (p, &p, end); @@ -7161,7 +8561,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 err = decode_value (sig->params [i], domain, (guint8*)&args [i], p, &p, end); if (err != ERR_NONE) break; - if (args [i] && ((MonoObject*)args [i])->vtable->domain != domain) + if (args [i] && VM_OBJECT_GET_DOMAIN(((MonoObject*)args [i])) != domain) NOT_IMPLEMENTED; if (sig->params [i]->byref) { @@ -7176,8 +8576,12 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 if (err != ERR_NONE) break; if (mono_class_is_nullable (arg_class)) { +#ifdef RUNTIME_IL2CPP + g_assert_not_reached (); +#else args [i] = mono_nullable_box (arg_buf [i], arg_class, &error); mono_error_assert_ok (&error); +#endif // RUNTIME_IL2CPP } else { args [i] = arg_buf [i]; } @@ -7195,7 +8599,8 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 /* * Add an LMF frame to link the stack frames on the invoke method with our caller. */ -#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED +#ifndef RUNTIME_IL2CPP +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED if (invoke->has_ctx) { /* Setup our lmf */ memset (&ext, 0, sizeof (ext)); @@ -7205,19 +8610,20 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 mono_push_lmf (&ext); } #endif +#endif // !RUNTIME_IL2CPP mono_stopwatch_start (&watch); - res = mono_runtime_try_invoke (m, m->klass->valuetype ? (gpointer) this_buf : (gpointer) this_arg, args, &exc, &error); + res = mono_runtime_try_invoke (m, mono_class_is_valuetype (m->klass) ? (gpointer)this_buf : (gpointer)this_arg, args, &exc, &error); if (!mono_error_ok (&error) && exc == NULL) { exc = (MonoObject*) mono_error_convert_to_exception (&error); } else { mono_error_cleanup (&error); /* FIXME report error */ } mono_stopwatch_stop (&watch); - DEBUG_PRINTF (1, "[%p] Invoke result: %p, exc: %s, time: %ld ms.\n", (gpointer) (gsize) mono_native_thread_id_get (), res, exc ? exc->vtable->klass->name : NULL, (long)mono_stopwatch_elapsed_ms (&watch)); + DEBUG_PRINTF (1, "[%p] Invoke result: %p, exc: %s, time: %ld ms.\n", (gpointer) (gsize) mono_native_thread_id_get (), res, exc ? mono_class_get_name (mono_object_get_class (exc)) : NULL, (long)mono_stopwatch_elapsed_ms (&watch)); if (exc) { buffer_add_byte (buf, 0); - buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &exc, domain); + buffer_add_value (buf, mono_class_get_type (VM_DEFAULTS_OBJECT_CLASS), &exc, domain); } else { gboolean out_this = FALSE; gboolean out_args = FALSE; @@ -7227,20 +8633,25 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 if ((invoke->flags & INVOKE_FLAG_RETURN_OUT_ARGS) && CHECK_PROTOCOL_VERSION (2, 35)) out_args = TRUE; buffer_add_byte (buf, 1 + (out_this ? 2 : 0) + (out_args ? 4 : 0)); - if (m->string_ctor) { - buffer_add_value (buf, &mono_get_string_class ()->byval_arg, &res, domain); - } else if (sig->ret->type == MONO_TYPE_VOID && !m->string_ctor) { + if (VM_METHOD_IS_STRING_CTOR(m)) { + buffer_add_value (buf, mono_class_get_type (mono_get_string_class ()), &res, domain); + } else if ( sig->ret->type == MONO_TYPE_VOID && !VM_METHOD_IS_STRING_CTOR(m)) { if (!strcmp (m->name, ".ctor")) { - if (!m->klass->valuetype) - buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this_arg, domain); + if (!mono_class_is_valuetype (m->klass)) + buffer_add_value (buf, mono_class_get_type (VM_DEFAULTS_OBJECT_CLASS), &this_arg, domain); else - buffer_add_value (buf, &m->klass->byval_arg, this_buf, domain); + buffer_add_value (buf, mono_class_get_type (m->klass), this_buf, domain); } else { - buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain); + buffer_add_value (buf, mono_class_get_type (VM_DEFAULTS_VOID_CLASS), NULL, domain); } } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) { - buffer_add_value (buf, sig->ret, &res, domain); - } else if (mono_class_from_mono_type (sig->ret)->valuetype || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) { + if (sig->ret->byref) { + MonoType* ret_byval = mono_class_get_type (mono_class_from_mono_type (sig->ret)); + buffer_add_value (buf, ret_byval, &res, domain); + } else { + buffer_add_value (buf, sig->ret, &res, domain); + } + } else if (mono_class_is_valuetype (mono_class_from_mono_type (sig->ret)) || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) { if (mono_class_is_nullable (mono_class_from_mono_type (sig->ret))) { MonoClass *k = mono_class_from_mono_type (sig->ret); guint8 *nullable_buf = (guint8 *)g_alloca (mono_class_value_size (k, NULL)); @@ -7250,14 +8661,20 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 buffer_add_value (buf, sig->ret, nullable_buf, domain); } else { g_assert (res); - buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain); + + if (sig->ret->byref) { + MonoType* ret_byval = mono_class_get_type (mono_class_from_mono_type (sig->ret)); + buffer_add_value (buf, ret_byval, mono_object_unbox (res), domain); + } else { + buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain); + } } } else { NOT_IMPLEMENTED; } if (out_this) /* Return the new value of the receiver after the call */ - buffer_add_value (buf, &m->klass->byval_arg, this_buf, domain); + buffer_add_value (buf, mono_class_get_type (m->klass), this_buf, domain); if (out_args) { buffer_add_int (buf, nargs); for (i = 0; i < nargs; ++i) { @@ -7274,10 +8691,12 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 tls->disable_breakpoints = FALSE; +#ifndef RUNTIME_IL2CPP #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED if (invoke->has_ctx) mono_pop_lmf ((MonoLMF*)&ext); #endif +#endif // !RUNTIME_IL2CPP *endp = p; // FIXME: byref arguments @@ -7386,8 +8805,6 @@ invoke_method (void) g_free (invoke->p); g_free (invoke); - - suspend_current (); } static gboolean @@ -7411,13 +8828,20 @@ get_source_files_for_type (MonoClass *klass) { gpointer iter = NULL; MonoMethod *method; +#ifndef RUNTIME_IL2CPP MonoDebugSourceInfo *sinfo; +#endif // !RUNTIME_IL2CPP GPtrArray *files; int i, j; files = g_ptr_array_new (); - while ((method = mono_class_get_methods (klass, &iter))) { +#ifdef RUNTIME_IL2CPP + const char **fileNames = il2cpp_get_source_files_for_type(klass, &i); + for (j = 0; j < i; ++j) + g_ptr_array_add(files, g_strdup(fileNames[j])); +#else + while ((method = mono_class_get_methods(klass, &iter))) { MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); GPtrArray *source_file_list; @@ -7434,6 +8858,7 @@ get_source_files_for_type (MonoClass *klass) g_ptr_array_free (source_file_list, TRUE); } } +#endif // RUNTIME_IL2CPP return files; } @@ -7481,6 +8906,8 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) clear_suspended_objs (); break; case CMD_VM_DISPOSE: + suspend_vm (); + wait_for_suspend (); /* Clear all event requests */ mono_loader_lock (); while (event_requests->len > 0) { @@ -7532,7 +8959,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) wait_for_suspend (); #ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT - env_class = mono_class_try_load_from_name (mono_defaults.corlib, "System", "Environment"); + env_class = mono_class_try_load_from_name (VM_DEFAULTS_CORLIB_IMAGE, "System", "Environment"); if (env_class) exit_method = mono_class_get_method_from_name (env_class, "Exit", 1); #endif @@ -7570,12 +8997,20 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) mono_environment_exitcode_set (exit_code); + /* + * We don't have a good way to suspend threads in IL2CPP, so just + * skip this code and call exit below. We will not shut down cleanly + * (e.g. run finalizers) but we don't care too much about that with + * IL2CPP. + */ +#ifndef RUNTIME_IL2CPP /* Suspend all managed threads since the runtime is going away */ DEBUG_PRINTF (1, "Suspending all threads...\n"); mono_thread_suspend_all_other_threads (); DEBUG_PRINTF (1, "Shutting down the runtime...\n"); mono_runtime_quit (); transport_close2 (); +#endif // !RUNTIME_IL2CPP DEBUG_PRINTF (1, "Exiting...\n"); exit (exit_code); @@ -7719,7 +9154,9 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) mono_loader_lock (); g_hash_table_iter_init (&iter, domains); while (g_hash_table_iter_next (&iter, NULL, (void**)&domain)) { - AgentDomainInfo *info = (AgentDomainInfo *)domain_jit_info (domain)->agent_info; + if (mono_domain_is_unloading (domain)) + continue; + AgentDomainInfo *info = (AgentDomainInfo *)VM_DOMAIN_GET_AGENT_INFO(domain); /* Update 'source_file_to_class' cache */ g_hash_table_iter_init (&kiter, info->loaded_classes); @@ -7802,6 +9239,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) ignore_case = decode_byte (p, &p, end); if (!mono_reflection_parse_type_checked (name, &info, &error)) { + buffer_add_string (buf, mono_error_get_message (&error)); mono_error_cleanup (&error); g_free (name); mono_reflection_free_type_info (&info); @@ -7819,11 +9257,14 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) MonoType *t; GSList *tmp; + if (mono_domain_is_unloading (domain)) + continue; mono_domain_assemblies_lock (domain); - for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { - ass = (MonoAssembly *)tmp->data; - - if (ass->image) { + void *iter = NULL; + while (ass = mono_domain_get_assemblies_iter (domain, &iter)) + { + if (ass->image) + { MonoError probe_type_error; /* FIXME really okay to call while holding locks? */ t = mono_reflection_get_type_checked (ass->image, ass->image, &info, ignore_case, &type_resolve, &probe_type_error); @@ -7931,11 +9372,11 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) req->modifiers [i].subclasses = decode_byte (p, &p, end); else req->modifiers [i].subclasses = TRUE; - DEBUG_PRINTF (1, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : ""); + DEBUG_PRINTF (1, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? mono_class_get_name (exc_class) : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : ""); if (exc_class) { req->modifiers [i].data.exc_class = exc_class; - if (!mono_class_is_assignable_from (mono_defaults.exception_class, exc_class)) { + if (!mono_class_is_assignable_from (VM_DEFAULTS_EXCEPTION_CLASS, exc_class)) { g_free (req); return ERR_INVALID_ARGUMENT; } @@ -8006,6 +9447,21 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return err; } +#if RUNTIME_IL2CPP + { + DebuggerTlsData* tls; + mono_loader_lock(); + tls = (DebuggerTlsData *)mono_g_hash_table_lookup(thread_to_tls, THREAD_TO_INTERNAL(step_thread)); + mono_loader_unlock(); + + if (tls->il2cpp_context->frameCount == 1 && depth == STEP_DEPTH_OUT) + { + g_free(req); + return ERR_NONE; + } + } +#endif // RUNTIME_IL2CPP + err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, filter, req); if (err != ERR_NONE) { g_free (req); @@ -8097,6 +9553,7 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { ErrorCode err; MonoDomain *domain; + void *iter = NULL; switch (command) { case CMD_APPDOMAIN_GET_ROOT_DOMAIN: { @@ -8120,12 +9577,20 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return err; mono_loader_lock (); count = 0; - for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { +#ifdef RUNTIME_IL2CPP + while(il2cpp_domain_get_assemblies_iter(domain, &iter)) { +#else + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { +#endif // RUNTIME_IL2CPP count ++; } buffer_add_int (buf, count); +#ifdef RUNTIME_IL2CPP + while(ass = il2cpp_domain_get_assemblies_iter(domain, &iter)) { +#else for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { ass = (MonoAssembly *)tmp->data; +#endif // RUNTIME_IL2CPP buffer_add_assemblyid (buf, domain, ass); } mono_loader_unlock (); @@ -8135,8 +9600,11 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) domain = decode_domainid (p, &p, end, NULL, &err); if (err != ERR_NONE) return err; - +#ifdef RUNTIME_IL2CPP + buffer_add_assemblyid (buf, domain, NULL); +#else buffer_add_assemblyid (buf, domain, domain->entry_assembly); +#endif // RUNTIME_IL2CPP break; } case CMD_APPDOMAIN_GET_CORLIB: { @@ -8144,7 +9612,7 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (err != ERR_NONE) return err; - buffer_add_assemblyid (buf, domain, domain->domain->mbr.obj.vtable->klass->image->assembly); + buffer_add_assemblyid (buf, domain, m_domain_get_corlib (domain)); break; } case CMD_APPDOMAIN_CREATE_STRING: { @@ -8182,10 +9650,13 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) // FIXME: g_assert (domain == domain2); + if (!klass->valuetype) + return ERR_INVALID_ARGUMENT; + o = mono_object_new_checked (domain, klass, &error); mono_error_assert_ok (&error); - err = decode_value (&klass->byval_arg, domain, (guint8 *)mono_object_unbox (o), p, &p, end); + err = decode_value (mono_class_get_type(klass), domain, (guint8 *)mono_object_unbox (o), p, &p, end); if (err != ERR_NONE) return err; @@ -8202,6 +9673,14 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) static ErrorCode get_assembly_object_command (MonoDomain *domain, MonoAssembly *ass, Buffer *buf, MonoError *error) { +#ifdef RUNTIME_IL2CPP + MonoReflectionAssemblyHandle o = il2cpp_mono_assembly_get_object_handle(domain, ass, error); + if (o == NULL) { + return ERR_INVALID_OBJECT; + } + buffer_add_objid(buf, (MonoObject*)o); + return ERR_NONE; +#else HANDLE_FUNCTION_ENTER(); ErrorCode err = ERR_NONE; error_init (error); @@ -8213,6 +9692,7 @@ get_assembly_object_command (MonoDomain *domain, MonoAssembly *ass, Buffer *buf, buffer_add_objid (buf, MONO_HANDLE_RAW (MONO_HANDLE_CAST (MonoObject, o))); leave: HANDLE_FUNCTION_RETURN_VAL (err); +#endif // RUNTIME_IL2CPP } @@ -8230,15 +9710,22 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) switch (command) { case CMD_ASSEMBLY_GET_LOCATION: { buffer_add_string (buf, mono_image_get_filename (ass->image)); - break; + break; } case CMD_ASSEMBLY_GET_ENTRY_POINT: { guint32 token; MonoMethod *m; - if (ass->image->dynamic) { + if (mono_image_is_dynamic (ass->image)) { buffer_add_id (buf, 0); } else { +#ifdef RUNTIME_IL2CPP + m = il2cpp_mono_image_get_entry_point(ass->image); + if (m == NULL) + buffer_add_id (buf, 0); + else + buffer_add_methodid (buf, domain, m); +#else token = mono_image_get_entry_point (ass->image); if (token == 0) { buffer_add_id (buf, 0); @@ -8249,6 +9736,7 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_error_cleanup (&error); /* FIXME don't swallow the error */ buffer_add_methodid (buf, domain, m); } +#endif // RUNTIME_IL2CPP } break; } @@ -8269,6 +9757,8 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_ASSEMBLY_GET_TYPE: { MonoError error; char *s = decode_string (p, &p, end); + char* original_s = g_strdup_printf ("\"%s\"", s); + gboolean ignorecase = decode_byte (p, &p, end); MonoTypeNameParse info; MonoType *t; @@ -8283,20 +9773,33 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_error_cleanup (&error); t = NULL; } else { - if (info.assembly.name) - NOT_IMPLEMENTED; + if (info.assembly.name) { + mono_reflection_free_type_info (&info); + g_free (s); + mono_domain_set (d, TRUE); + char* error_msg = g_strdup_printf ("Unexpected assembly-qualified type %s was provided", original_s); + buffer_add_string (buf, error_msg); + g_free (error_msg); + g_free (original_s); + return ERR_INVALID_ARGUMENT; + } t = mono_reflection_get_type_checked (ass->image, ass->image, &info, ignorecase, &type_resolve, &error); if (!is_ok (&error)) { mono_error_cleanup (&error); /* FIXME don't swallow the error */ mono_reflection_free_type_info (&info); g_free (s); + mono_domain_set (d, TRUE); + char* error_msg = g_strdup_printf ("Invalid type name %s", original_s); + buffer_add_string (buf, error_msg); + g_free (error_msg); + g_free (original_s); return ERR_INVALID_ARGUMENT; } } buffer_add_typeid (buf, domain, t ? mono_class_from_mono_type (t) : NULL); mono_reflection_free_type_info (&info); g_free (s); - + g_free (original_s); mono_domain_set (d, TRUE); break; @@ -8304,7 +9807,9 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_ASSEMBLY_GET_NAME: { gchar *name; MonoAssembly *mass = ass; - +#ifdef RUNTIME_IL2CPP + name = il2cpp_assembly_get_full_name(mass); +#else name = g_strdup_printf ( "%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s", mass->aname.name, @@ -8312,7 +9817,7 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mass->aname.culture && *mass->aname.culture? mass->aname.culture: "neutral", mass->aname.public_key_token [0] ? (char *)mass->aname.public_key_token : "null", (mass->aname.flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : ""); - +#endif // RUNTIME_IL2CPP buffer_add_string (buf, name); g_free (name); break; @@ -8335,12 +9840,12 @@ module_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoImage *image = decode_moduleid (p, &p, end, &domain, &err); char *basename; - basename = g_path_get_basename (image->name); + basename = g_path_get_basename (mono_image_get_filename (image)); buffer_add_string (buf, basename); // name - buffer_add_string (buf, image->module_name); // scopename - buffer_add_string (buf, image->name); // fqname + buffer_add_string (buf, VM_IMAGE_GET_MODULE_NAME(image)); // scopename + buffer_add_string (buf, mono_image_get_filename (image)); // fqname buffer_add_string (buf, mono_image_get_guid (image)); // guid - buffer_add_assemblyid (buf, domain, image->assembly); // assembly + buffer_add_assemblyid (buf, domain, mono_image_get_assembly (image)); // assembly g_free (basename); break; } @@ -8361,10 +9866,10 @@ field_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_FIELD_GET_INFO: { MonoClassField *f = decode_fieldid (p, &p, end, &domain, &err); - buffer_add_string (buf, f->name); - buffer_add_typeid (buf, domain, f->parent); - buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type)); - buffer_add_int (buf, f->type->attrs); + buffer_add_string (buf, mono_field_get_name (f)); + buffer_add_typeid (buf, domain, mono_field_get_parent (f)); + buffer_add_typeid (buf, domain, mono_class_from_mono_type (mono_field_get_type (f))); + buffer_add_int (buf, mono_type_get_attrs (mono_field_get_type (f))); break; } default: @@ -8377,7 +9882,7 @@ field_commands (int command, guint8 *p, guint8 *end, Buffer *buf) static void buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val) { - if (val && val->vtable->klass == mono_defaults.runtimetype_class) { + if (val && mono_object_get_class(val) == mono_defaults.runtimetype_class) { /* Special case these so the client doesn't have to handle Type objects */ buffer_add_byte (buf, VALUE_TYPE_ID_TYPE); @@ -8388,6 +9893,7 @@ buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject * buffer_add_value (buf, t, mono_object_unbox (val), domain); } +#ifndef RUNTIME_IL2CPP static ErrorCode buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass *attr_klass, MonoCustomAttrInfo *cinfo) { @@ -8464,6 +9970,7 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass return ERR_NONE; } +#endif // !RUNTIME_IL2CPP /* FIXME: Code duplication with icall.c */ static void @@ -8476,8 +9983,8 @@ collect_interfaces (MonoClass *klass, GHashTable *ifaces, MonoError *error) if (!mono_error_ok (error)) return; - for (i = 0; i < klass->interface_count; i++) { - ic = klass->interfaces [i]; + gpointer iter = NULL; + while (ic = mono_class_get_interfaces(klass, &iter)) { g_hash_table_insert (ifaces, ic, ic); collect_interfaces (ic, ifaces, error); @@ -8492,6 +9999,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint MonoError error; MonoClass *nested; MonoType *type; + int type_tag; gpointer iter; guint8 b; int nnested; @@ -8500,34 +10008,36 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint switch (command) { case CMD_TYPE_GET_INFO: { - buffer_add_string (buf, klass->name_space); - buffer_add_string (buf, klass->name); + buffer_add_string (buf, mono_class_get_namespace(klass)); + buffer_add_string (buf, mono_class_get_name(klass)); // FIXME: byref - name = mono_type_get_name_full (&klass->byval_arg, MONO_TYPE_NAME_FORMAT_FULL_NAME); + type = mono_class_get_type(klass); + name = mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_FULL_NAME); buffer_add_string (buf, name); g_free (name); - buffer_add_assemblyid (buf, domain, klass->image->assembly); - buffer_add_moduleid (buf, domain, klass->image); - buffer_add_typeid (buf, domain, klass->parent); - if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR) - buffer_add_typeid (buf, domain, klass->element_class); + buffer_add_assemblyid (buf, domain, mono_image_get_assembly(mono_class_get_image(klass))); + buffer_add_moduleid (buf, domain, mono_class_get_image(klass)); + buffer_add_typeid (buf, domain, mono_class_get_parent(klass)); + + type_tag = mono_type_get_type(type); + if (mono_class_get_rank(klass) || type_tag == MONO_TYPE_PTR) + buffer_add_typeid (buf, domain, mono_class_get_element_class(klass)); else buffer_add_id (buf, 0); - buffer_add_int (buf, klass->type_token); - buffer_add_byte (buf, klass->rank); + buffer_add_int (buf, mono_class_get_type_token(klass)); + buffer_add_byte (buf, mono_class_get_rank(klass)); buffer_add_int (buf, mono_class_get_flags (klass)); b = 0; - type = &klass->byval_arg; // FIXME: Can't decide whenever a class represents a byref type if (FALSE) b |= (1 << 0); - if (type->type == MONO_TYPE_PTR) + if (type_tag == MONO_TYPE_PTR) b |= (1 << 1); - if (!type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U))) + if (!mono_type_is_byref(type) && (((type_tag >= MONO_TYPE_BOOLEAN) && (type_tag <= MONO_TYPE_R8)) || (type_tag == MONO_TYPE_I) || (type_tag == MONO_TYPE_U))) b |= (1 << 2); - if (type->type == MONO_TYPE_VALUETYPE) + if (type_tag == MONO_TYPE_VALUETYPE) b |= (1 << 3); - if (klass->enumtype) + if (mono_class_is_enum(klass)) b |= (1 << 4); if (mono_class_is_gtd (klass)) b |= (1 << 5); @@ -8546,7 +10056,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint if (mono_class_is_gtd (klass)) buffer_add_typeid (buf, domain, klass); else if (mono_class_is_ginst (klass)) - buffer_add_typeid (buf, domain, mono_class_get_generic_class (klass)->container_class); + buffer_add_typeid (buf, domain, VM_GENERIC_CLASS_GET_CONTAINER_CLASS(mono_class_get_generic_class (klass))); else buffer_add_id (buf, 0); } @@ -8554,7 +10064,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint int count, i; if (mono_class_is_ginst (klass)) { - MonoGenericInst *inst = mono_class_get_generic_class (klass)->context.class_inst; + MonoGenericInst *inst = mono_generic_class_get_context (mono_class_get_generic_class (klass))->class_inst; count = inst->type_argc; buffer_add_int (buf, count); @@ -8607,9 +10117,9 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint while ((f = mono_class_get_fields (klass, &iter))) { buffer_add_fieldid (buf, domain, f); - buffer_add_string (buf, f->name); - buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type)); - buffer_add_int (buf, f->type->attrs); + buffer_add_string (buf, mono_field_get_name (f)); + buffer_add_typeid (buf, domain, mono_class_from_mono_type (mono_field_get_type (f))); + buffer_add_int (buf, mono_type_get_attrs (mono_field_get_type (f))); i ++; } g_assert (i == nfields); @@ -8627,16 +10137,20 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint while ((p = mono_class_get_properties (klass, &iter))) { buffer_add_propertyid (buf, domain, p); - buffer_add_string (buf, p->name); - buffer_add_methodid (buf, domain, p->get); - buffer_add_methodid (buf, domain, p->set); - buffer_add_int (buf, p->attrs); + buffer_add_string (buf, mono_property_get_name (p)); + buffer_add_methodid (buf, domain, mono_property_get_get_method (p)); + buffer_add_methodid (buf, domain, mono_property_get_set_method (p)); + buffer_add_int (buf, mono_type_get_attrs (mono_property_get_parent (p))); i ++; } g_assert (i == nprops); break; } case CMD_TYPE_GET_CATTRS: { +#ifdef RUNTIME_IL2CPP + buffer_add_int (buf, 0); + return ERR_NONE; +#else MonoClass *attr_klass; MonoCustomAttrInfo *cinfo; @@ -8651,12 +10165,17 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint return ERR_LOADER_ERROR; } - err = buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo); + err = buffer_add_cattrs (buf, domain, mono_class_get_image(klass), attr_klass, cinfo); if (err != ERR_NONE) return err; break; +#endif // RUNTIME_IL2CPP } case CMD_TYPE_GET_FIELD_CATTRS: { +#ifdef RUNTIME_IL2CPP + buffer_add_int (buf, 0); + return ERR_NONE; +#else MonoClass *attr_klass; MonoCustomAttrInfo *cinfo; MonoClassField *field; @@ -8674,12 +10193,17 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint return ERR_LOADER_ERROR; } - err = buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo); + err = buffer_add_cattrs (buf, domain, mono_class_get_image(klass), attr_klass, cinfo); if (err != ERR_NONE) return err; break; +#endif // RUNTIME_IL2CPP } case CMD_TYPE_GET_PROPERTY_CATTRS: { +#ifdef RUNTIME_IL2CPP + buffer_add_int (buf, 0); + return ERR_NONE; +#else MonoClass *attr_klass; MonoCustomAttrInfo *cinfo; MonoProperty *prop; @@ -8697,10 +10221,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint return ERR_LOADER_ERROR; } - err = buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo); + err = buffer_add_cattrs (buf, domain, mono_class_get_image(klass), attr_klass, cinfo); if (err != ERR_NONE) return err; break; +#endif // RUNTIME_IL2CPP } case CMD_TYPE_GET_VALUES: case CMD_TYPE_GET_VALUES_2: { @@ -8731,18 +10256,23 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint if (err != ERR_NONE) return err; - if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC)) + if (!(mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC)) + return ERR_INVALID_FIELDID; + +#ifdef RUNTIME_IL2CPP + if (!thread && mono_field_get_offset (f) == THREAD_STATIC_FIELD_OFFSET) return ERR_INVALID_FIELDID; +#else special_static_type = mono_class_field_get_special_static_type (f); if (special_static_type != SPECIAL_STATIC_NONE) { if (!(thread && special_static_type == SPECIAL_STATIC_THREAD)) return ERR_INVALID_FIELDID; } - +#endif // RUNTIME_IL2CPP /* Check that the field belongs to the object */ found = FALSE; - for (k = klass; k; k = k->parent) { - if (k == f->parent) { + for (k = klass; k; k = mono_class_get_parent(k)) { + if (k == mono_field_get_parent(f)) { found = TRUE; break; } @@ -8750,12 +10280,12 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint if (!found) return ERR_INVALID_FIELDID; - vtable = mono_class_vtable (domain, f->parent); - val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type))); + vtable = mono_class_vtable (domain, mono_field_get_parent (f)); + val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (mono_field_get_type (f)))); mono_field_static_get_value_for_thread (thread ? thread : mono_thread_internal_current (), vtable, f, val, &error); if (!is_ok (&error)) return ERR_INVALID_FIELDID; - buffer_add_value (buf, f->type, val, domain); + buffer_add_value (buf, mono_field_get_type (f), val, domain); g_free (val); } break; @@ -8774,15 +10304,15 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint if (err != ERR_NONE) return err; - if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC)) + if (!(mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC)) return ERR_INVALID_FIELDID; if (mono_class_field_is_special_static (f)) return ERR_INVALID_FIELDID; /* Check that the field belongs to the object */ found = FALSE; - for (k = klass; k; k = k->parent) { - if (k == f->parent) { + for (k = klass; k; k = mono_class_get_parent(k)) { + if (k == mono_field_get_parent(f)) { found = TRUE; break; } @@ -8792,14 +10322,14 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint // FIXME: Check for literal/const - vtable = mono_class_vtable (domain, f->parent); - val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type))); - err = decode_value (f->type, domain, val, p, &p, end); + vtable = mono_class_vtable (domain, mono_field_get_parent(f)); + val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (mono_field_get_type(f)))); + err = decode_value (mono_field_get_type(f), domain, val, p, &p, end); if (err != ERR_NONE) { g_free (val); return err; } - if (MONO_TYPE_IS_REFERENCE (f->type)) + if (MONO_TYPE_IS_REFERENCE (mono_field_get_type(f))) mono_field_static_set_value (vtable, f, *(gpointer*)val); else mono_field_static_set_value (vtable, f, val); @@ -8808,7 +10338,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint break; } case CMD_TYPE_GET_OBJECT: { - MonoObject *o = (MonoObject*)mono_type_get_object_checked (domain, &klass->byval_arg, &error); + MonoObject *o = (MonoObject*)mono_type_get_object_checked (domain, mono_class_get_type(klass), &error); if (!mono_error_ok (&error)) { mono_error_cleanup (&error); return ERR_INVALID_OBJECT; @@ -8880,7 +10410,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint tclass = klass; - for (parent = tclass; parent; parent = parent->parent) { + for (parent = tclass; parent; parent = mono_class_get_parent (parent)) { mono_class_setup_interfaces (parent, &error); if (!mono_error_ok (&error)) return ERR_LOADER_ERROR; @@ -8912,11 +10442,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint iclass = decode_typeid (p, &p, end, NULL, &err); if (err != ERR_NONE) return err; - +#ifndef RUNTIME_IL2CPP ioffset = mono_class_interface_offset_with_variance (klass, iclass, &variance_used); if (ioffset == -1) return ERR_INVALID_ARGUMENT; - +#endif // !RUNTIME_IL2CPP nmethods = mono_class_num_methods (iclass); buffer_add_int (buf, nmethods); @@ -8925,7 +10455,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_methodid (buf, domain, method); } for (i = 0; i < nmethods; ++i) +#ifdef RUNTIME_IL2CPP + buffer_add_methodid(buf, domain, il2cpp_get_interface_method(klass, iclass, i)); +#else buffer_add_methodid (buf, domain, klass->vtable [i + ioffset]); +#endif // !RUNTIME_IL2CPP } break; } @@ -8933,7 +10467,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint MonoVTable *vtable = mono_class_vtable (domain, klass); if (vtable) +#ifdef RUNTIME_IL2CPP + buffer_add_int (buf, m_class_is_initialized (klass) ? 1 : 0); +#else buffer_add_int (buf, (vtable->initialized || vtable->init_failed) ? 1 : 0); +#endif // RUNTIME_IL2CPP else buffer_add_int (buf, 0); break; @@ -8947,6 +10485,13 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_objid (buf, obj); break; } + case CMD_TYPE_GET_VALUE_SIZE: { + int32_t value_size; + + value_size = mono_class_value_size (klass, NULL); + buffer_add_int (buf, value_size); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -8977,6 +10522,64 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return err; } +#ifdef RUNTIME_IL2CPP +gint CompareSeqPointsByIlOffset(gconstpointer a, gconstpointer b) +{ + Il2CppSequencePoint *seqPointA = *(Il2CppSequencePoint**)a; + Il2CppSequencePoint *seqPointB = *(Il2CppSequencePoint**)b; + + if (seqPointA->ilOffset < seqPointB->ilOffset) + return -1; + else if (seqPointA->ilOffset > seqPointB->ilOffset) + return 1; + else + return 0; +} + +static void GetSequencePointsAndSourceFilesUniqueSequencePoints(MonoMethod* method, GPtrArray** sequencePoints, GPtrArray** uniqueFileSequencePoints, GArray** uniqueFileSequencePointIndices) +{ + int i; + *sequencePoints = g_ptr_array_new(); + *uniqueFileSequencePoints = g_ptr_array_new(); + *uniqueFileSequencePointIndices = g_array_new(FALSE, FALSE, sizeof(int)); + + if (method->is_inflated) + method = method->genericMethod->methodDefinition; + + void *seqPointIter = NULL; + Il2CppSequencePoint *seqPoint; + while (seqPoint = il2cpp_get_method_sequence_points(method, &seqPointIter)) + { + if(seqPoint->ilOffset == METHOD_ENTRY_IL_OFFSET || seqPoint->ilOffset == METHOD_EXIT_IL_OFFSET) + continue; + + if (il2cpp_mono_methods_match(il2cpp_get_seq_point_method(seqPoint), method)) + g_ptr_array_add(*sequencePoints, seqPoint); + } + + g_ptr_array_sort(*sequencePoints, CompareSeqPointsByIlOffset); + + for(i=0;i < (*sequencePoints)->len;++i) + { + int j; + seqPoint = (Il2CppSequencePoint*)(*sequencePoints)->pdata[i]; + + for (j = 0; j < (*uniqueFileSequencePoints)->len; j++) + { + Il2CppSequencePoint* uniqueSequencePoint = g_ptr_array_index(*uniqueFileSequencePoints, j); + if (uniqueSequencePoint->sourceFileIndex == seqPoint->sourceFileIndex) + break; + } + + if (j == (*uniqueFileSequencePoints)->len) + g_ptr_array_add(*uniqueFileSequencePoints, seqPoint); + + int index = (*uniqueFileSequencePoints)->len - 1; + g_array_append_val(*uniqueFileSequencePointIndices, index); + } +} +#endif // RUNTIME_IL2CPP + static ErrorCode method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf) { @@ -8985,18 +10588,82 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g switch (command) { case CMD_METHOD_GET_NAME: { - buffer_add_string (buf, method->name); + buffer_add_string (buf, mono_method_get_name(method)); break; } case CMD_METHOD_GET_DECLARING_TYPE: { - buffer_add_typeid (buf, domain, method->klass); + buffer_add_typeid (buf, domain, mono_method_get_class(method)); break; } case CMD_METHOD_GET_DEBUG_INFO: { MonoError error; + int i, j; +#ifdef RUNTIME_IL2CPP + GPtrArray* sequencePoints; + GPtrArray* uniqueFileSequencePoints; + GArray* uniqueFileSequencePointIndices; + GetSequencePointsAndSourceFilesUniqueSequencePoints(method, &sequencePoints, &uniqueFileSequencePoints, &uniqueFileSequencePointIndices); + + buffer_add_int(buf, 0); + if (CHECK_PROTOCOL_VERSION(2, 13)) { + buffer_add_int(buf, uniqueFileSequencePoints->len); + for (i = 0; i < uniqueFileSequencePoints->len; ++i) { + Il2CppSequencePoint* sequencePoint = g_ptr_array_index(uniqueFileSequencePoints, i); + Il2CppSequencePointSourceFile* sourceFile = il2cpp_debug_get_source_file (mono_class_get_image (mono_method_get_class (method)), sequencePoint->sourceFileIndex); + buffer_add_string(buf, sourceFile->file); + if (CHECK_PROTOCOL_VERSION(2, 14)) { + buffer_add_data(buf, sourceFile->hash, 16); + } + } + } + else { + if (uniqueFileSequencePoints->len > 0) { + Il2CppSequencePointSourceFile* sourceFile = il2cpp_debug_get_source_file (mono_class_get_image (mono_method_get_class (method)), ((Il2CppSequencePoint*)g_ptr_array_index(uniqueFileSequencePoints, 0))->sourceFileIndex); + buffer_add_string(buf, sourceFile->file); + } else { + buffer_add_string(buf, ""); + } + } + + int numSeqPoints = 0; + + for (i = 0; i < sequencePoints->len; ++i) { + Il2CppSequencePoint* sequencePoint = g_ptr_array_index(sequencePoints, i); + if (sequencePoint->kind == kSequencePointKind_StepOut) + continue; + else + ++numSeqPoints; + } + + buffer_add_int(buf, numSeqPoints); + DEBUG_PRINTF(10, "Line number table for method %s:\n", mono_method_full_name(method, TRUE)); + for (i = 0; i < sequencePoints->len; ++i) { + Il2CppSequencePoint* sequencePoint = g_ptr_array_index(sequencePoints, i); + + if (sequencePoint->kind == kSequencePointKind_StepOut) + continue; + + DEBUG_PRINTF(10, "IL%x -> %s:%d %d %d %d\n", sequencePoint->ilOffset, il2cpp_debug_get_source_file (mono_class_get_image (mono_method_get_class (method)), sequencePoint->sourceFileIndex)->file, + sequencePoint->lineStart, sequencePoint->columnStart, sequencePoint->lineEnd, sequencePoint->columnEnd); + buffer_add_int(buf, sequencePoint->ilOffset); + buffer_add_int(buf, sequencePoint->lineStart); + if (CHECK_PROTOCOL_VERSION(2, 13)) + buffer_add_int(buf, g_array_index(uniqueFileSequencePointIndices, int, i)); + if (CHECK_PROTOCOL_VERSION(2, 19)) + buffer_add_int(buf, sequencePoint->columnStart); + if (CHECK_PROTOCOL_VERSION(2, 32)) { + buffer_add_int(buf, sequencePoint->lineEnd); + buffer_add_int(buf, sequencePoint->columnEnd); + } + } + + g_ptr_array_free(sequencePoints, TRUE); + g_ptr_array_free(uniqueFileSequencePoints, TRUE); + g_array_free(uniqueFileSequencePointIndices, TRUE); +#else MonoDebugMethodInfo *minfo; char *source_file; - int i, j, n_il_offsets; + int n_il_offsets; int *source_files; GPtrArray *source_file_list; MonoSymSeqPoint *sym_seq_points; @@ -9061,6 +10728,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g g_free (sym_seq_points); g_ptr_array_free (source_file_list, TRUE); mono_metadata_free_mh (header); +#endif // RUNTIME_IL2CPP break; } case CMD_METHOD_GET_PARAM_INFO: { @@ -9097,6 +10765,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g header = mono_method_get_header_checked (method, &error); if (!header) { + buffer_add_string (buf, mono_error_get_message (&error)); mono_error_cleanup (&error); /* FIXME don't swallow the error */ return ERR_INVALID_ARGUMENT; } @@ -9167,33 +10836,42 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g break; } - case CMD_METHOD_GET_INFO: - buffer_add_int (buf, method->flags); - buffer_add_int (buf, method->iflags); - buffer_add_int (buf, method->token); + case CMD_METHOD_GET_INFO: { + int iflags; + buffer_add_int (buf, mono_method_get_flags(method, &iflags)); + buffer_add_int (buf, iflags); + buffer_add_int (buf, mono_method_get_token(method)); + gboolean is_generic = FALSE; + gboolean is_inflated = FALSE; if (CHECK_PROTOCOL_VERSION (2, 12)) { guint8 attrs = 0; - if (method->is_generic) + is_generic = method->is_generic; + is_inflated = method->is_inflated; + if (is_generic) attrs |= (1 << 0); if (mono_method_signature (method)->generic_param_count) attrs |= (1 << 1); buffer_add_byte (buf, attrs); - if (method->is_generic || method->is_inflated) { + if (is_generic || is_inflated) { MonoMethod *result; - if (method->is_generic) { + if (is_generic) { result = method; } else { MonoMethodInflated *imethod = (MonoMethodInflated *)method; - result = imethod->declaring; - if (imethod->context.class_inst) { + result = VM_INFLATED_METHOD_GET_DECLARING(imethod); + if (VM_INFLATED_METHOD_GET_CLASS_INST(imethod)) { MonoClass *klass = ((MonoMethod *) imethod)->klass; /*Generic methods gets the context of the GTD.*/ if (mono_class_get_context (klass)) { MonoError error; result = mono_class_inflate_generic_method_full_checked (result, klass, mono_class_get_context (klass), &error); - g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + if (!mono_error_ok (&error)) { + buffer_add_string (buf, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return ERR_INVALID_ARGUMENT; + } } } } @@ -9206,7 +10884,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g if (mono_method_signature (method)->generic_param_count) { int count, i; - if (method->is_inflated) { + if (is_inflated) { MonoGenericInst *inst = mono_method_get_context (method)->method_inst; if (inst) { count = inst->type_argc; @@ -9217,7 +10895,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g } else { buffer_add_int (buf, 0); } - } else if (method->is_generic) { + } else if (is_generic) { MonoGenericContainer *container = mono_method_get_generic_container (method); count = mono_method_signature (method)->generic_param_count; @@ -9236,7 +10914,11 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g } } break; + } case CMD_METHOD_GET_BODY: { +#ifdef RUNTIME_IL2CPP + return ERR_NOT_IMPLEMENTED; +#else MonoError error; int i; @@ -9273,6 +10955,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g } break; +#endif // RUNTIME_IL2CPP } case CMD_METHOD_RESOLVE_TOKEN: { guint32 token = decode_int (p, &p, end); @@ -9284,7 +10967,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g MonoString *s; char *s2; - s = mono_ldstr_checked (domain, method->klass->image, mono_metadata_token_index (token), &error); + s = mono_ldstr_checked (domain, mono_class_get_image (method->klass), mono_metadata_token_index (token), &error); mono_error_assert_ok (&error); /* FIXME don't swallow the error */ s2 = mono_string_to_utf8_checked (s, &error); @@ -9310,7 +10993,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g break; } } else { - val = mono_ldtoken_checked (method->klass->image, token, &handle_class, NULL, &error); + val = mono_ldtoken_checked (mono_class_get_image (method->klass), token, &handle_class, NULL, &error); if (!val) g_error ("Could not load token due to %s", mono_error_get_message (&error)); } @@ -9331,7 +11014,12 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g char *s; s = mono_string_to_utf8_checked ((MonoString *)val, &error); - mono_error_assert_ok (&error); + if (!mono_error_ok (&error)) { + buffer_add_string (buf, mono_error_get_message (&error)); + mono_error_cleanup (&error); + g_free (s); + return ERR_INVALID_ARGUMENT; + } buffer_add_byte (buf, TOKEN_TYPE_STRING); buffer_add_string (buf, s); g_free (s); @@ -9344,6 +11032,10 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g break; } case CMD_METHOD_GET_CATTRS: { +#ifdef RUNTIME_IL2CPP + buffer_add_int (buf, 0); + return ERR_NONE; +#else MonoError error; MonoClass *attr_klass; MonoCustomAttrInfo *cinfo; @@ -9363,6 +11055,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g if (err != ERR_NONE) return err; break; +#endif // RUNTIME_IL2CPP } case CMD_METHOD_MAKE_GENERIC_METHOD: { MonoError error; @@ -9386,15 +11079,19 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g g_free (type_argv); return ERR_INVALID_ARGUMENT; } - type_argv [i] = &klass->byval_arg; + type_argv [i] = mono_class_get_type(klass); } ginst = mono_metadata_get_generic_inst (type_argc, type_argv); g_free (type_argv); - tmp_context.class_inst = mono_class_is_ginst (method->klass) ? mono_class_get_generic_class (method->klass)->context.class_inst : NULL; + tmp_context.class_inst = mono_class_is_ginst (method->klass) ? mono_generic_class_get_context (mono_class_get_generic_class (method->klass))->class_inst : NULL; tmp_context.method_inst = ginst; inflated = mono_class_inflate_generic_method_checked (method, &tmp_context, &error); - g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */ + if (!mono_error_ok (&error)) { + buffer_add_string (buf, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return ERR_INVALID_ARGUMENT; + } if (!mono_verifier_is_method_valid_generic_instantiation (inflated)) return ERR_INVALID_ARGUMENT; buffer_add_methodid (buf, domain, inflated); @@ -9489,9 +11186,10 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_loader_lock (); tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); mono_loader_unlock (); - g_assert (tls); + if (tls == NULL) + return ERR_UNLOADED; - compute_frame_info (thread, tls); + compute_frame_info (thread, tls, TRUE); //the last parameter is TRUE to force that the frame info that will be send is synchronised with the debugged thread buffer_add_int (buf, tls->frame_count); for (i = 0; i < tls->frame_count; ++i) { @@ -9522,6 +11220,9 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_long (buf, (guint64)thread->tid); break; case CMD_THREAD_SET_IP: { +#ifdef RUNTIME_IL2CPP + return ERR_NOT_IMPLEMENTED; +#else DebuggerTlsData *tls; MonoMethod *method; MonoDomain *domain; @@ -9545,7 +11246,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_loader_unlock (); g_assert (tls); - compute_frame_info (thread, tls); + compute_frame_info (thread, tls, FALSE); if (tls->frame_count == 0 || tls->frames [0]->actual_method != method) return ERR_INVALID_ARGUMENT; @@ -9566,6 +11267,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } else { MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset); } +#endif // RUNTIME_IL2CPP break; } default: @@ -9575,6 +11277,43 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +#ifdef RUNTIME_IL2CPP +static void GetVariable(DebuggerTlsData* tls, StackFrame* frame, MethodVariableKind variableKind, uint32_t variablePosition, MonoType** type, void** var) +{ + switch (variableKind) + { + case kMethodVariableKind_Parameter: { + *var = frame->frame_context->params[variablePosition]; + *type = il2cpp_method_get_param (frame->actual_method, variablePosition); + } + break; + case kMethodVariableKind_LocalVariable: { + uint32_t executionInfoCount, localVariableCount = 0, i; + const Il2CppMethodExecutionContextInfo* executionContextInfo = NULL; + const Il2CppMethodHeaderInfo* headerInfo; + const Il2CppMethodScope* scopes; + + il2cpp_debugger_get_method_execution_context_and_header_info (frame->actual_method, &executionInfoCount, &executionContextInfo, &headerInfo, &scopes); + + *var = frame->frame_context->locals[variablePosition]; + *type = il2cpp_type_inflate ( il2cpp_get_type_from_index (executionContextInfo[variablePosition].typeIndex), il2cpp_mono_method_get_context (frame->actual_method)); + } + break; + case kMethodVariableKind_This: + default: + g_assert_not_reached (); + } +} + +static void SendVariableData(DebuggerTlsData* tls, StackFrame* frame, Buffer* buf, MethodVariableKind variableKind, uint32_t variablePosition) +{ + MonoType* localVariableType; + void* localVariableValue; + GetVariable(tls, frame, variableKind, variablePosition, &localVariableType, &localVariableValue); + buffer_add_value_full(buf, localVariableType, localVariableValue, frame->domain, FALSE, NULL); +} +#endif // RUNTIME_IL2CPP + static ErrorCode frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { @@ -9611,6 +11350,9 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (i == tls->frame_count) return ERR_INVALID_FRAMEID; + /* The thread is still running native code, can't get frame variables info */ + if (!tls->really_suspended && !tls->async_state.valid) + return ERR_NOT_SUSPENDED; frame_idx = i; frame = tls->frames [frame_idx]; @@ -9621,8 +11363,10 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +#ifndef RUNTIME_IL2CPP if (!frame->has_ctx) return ERR_ABSENT_INFORMATION; +#endif // !RUNTIME_IL2CPP if (!ensure_jit (frame)) return ERR_ABSENT_INFORMATION; @@ -9631,11 +11375,13 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) sig = mono_method_signature (frame->actual_method); +#ifndef RUNTIME_IL2CPP if (!(jit->has_var_info || frame->ji->is_interp) || !mono_get_seq_points (frame->domain, frame->actual_method)) /* * The method is probably from an aot image compiled without soft-debug, variables might be dead, etc. */ return ERR_ABSENT_INFORMATION; +#endif // !RUNTIME_IL2CPP switch (command) { case CMD_STACK_FRAME_GET_VALUES: { @@ -9694,10 +11440,10 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_STACK_FRAME_GET_THIS: { if (frame->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) return ERR_ABSENT_INFORMATION; - if (frame->api_method->klass->valuetype) { + if (mono_class_is_valuetype (frame->api_method->klass)) { if (!sig->hasthis) { MonoObject *p = NULL; - buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain); + buffer_add_value (buf, mono_class_get_type (VM_DEFAULTS_OBJECT_CLASS), &p, frame->domain); } else { if (frame->ji->is_interp) { guint8 *addr; @@ -9712,7 +11458,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } else { if (!sig->hasthis) { MonoObject *p = NULL; - buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain); + buffer_add_value (buf, mono_class_get_type (frame->actual_method->klass), &p, frame->domain); } else { if (frame->ji->is_interp) { guint8 *addr; @@ -9795,9 +11541,13 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_STACK_FRAME_SET_THIS: { guint8 *val_buf; MonoType *t; +#ifndef RUNTIME_IL2CPP MonoDebugVarInfo *var; +#else + void *var; +#endif // !RUNTIME_IL2CPP - t = &frame->actual_method->klass->byval_arg; + t = mono_class_get_type (frame->actual_method->klass); /* Checked by the sender */ g_assert (MONO_TYPE_ISSTRUCT (t)); @@ -9813,7 +11563,10 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) set_interp_var (&frame->actual_method->klass->this_arg, addr, val_buf); } else { var = jit->this_var; - g_assert (var); + if (!var) { + buffer_add_string (buf, "Invalid this object"); + return ERR_INVALID_ARGUMENT; + } set_var (&frame->actual_method->klass->this_arg, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx); } @@ -9826,7 +11579,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } -static ErrorCode +static int array_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { MonoArray *arr; @@ -9841,12 +11594,12 @@ array_commands (int command, guint8 *p, guint8 *end, Buffer *buf) switch (command) { case CMD_ARRAY_REF_GET_LENGTH: - buffer_add_int (buf, arr->obj.vtable->klass->rank); + buffer_add_int (buf, mono_class_get_rank (mono_object_get_class (&arr->obj))); if (!arr->bounds) { buffer_add_int (buf, arr->max_length); buffer_add_int (buf, 0); } else { - for (i = 0; i < arr->obj.vtable->klass->rank; ++i) { + for (i = 0; i < mono_class_get_rank (mono_object_get_class (&arr->obj)); ++i) { buffer_add_int (buf, arr->bounds [i].length); buffer_add_int (buf, arr->bounds [i].lower_bound); } @@ -9856,29 +11609,32 @@ array_commands (int command, guint8 *p, guint8 *end, Buffer *buf) index = decode_int (p, &p, end); len = decode_int (p, &p, end); - g_assert (index >= 0 && len >= 0); + if (index < 0 || len < 0) + return ERR_INVALID_ARGUMENT; // Reordered to avoid integer overflow - g_assert (!(index > arr->max_length - len)); + if (index > arr->max_length - len) + return ERR_INVALID_ARGUMENT; - esize = mono_array_element_size (arr->obj.vtable->klass); + esize = mono_array_element_size (mono_object_get_class (&arr->obj)); for (i = index; i < index + len; ++i) { elem = (gpointer*)((char*)arr->vector + (i * esize)); - buffer_add_value (buf, &arr->obj.vtable->klass->element_class->byval_arg, elem, arr->obj.vtable->domain); + buffer_add_value (buf, mono_class_get_type (mono_class_get_element_class (mono_object_get_class (&arr->obj))), elem, VM_OBJECT_GET_DOMAIN(arr)); } break; case CMD_ARRAY_REF_SET_VALUES: index = decode_int (p, &p, end); len = decode_int (p, &p, end); - g_assert (index >= 0 && len >= 0); + if (index < 0 || len < 0) + return ERR_INVALID_ARGUMENT; // Reordered to avoid integer overflow - g_assert (!(index > arr->max_length - len)); + if (index > arr->max_length - len) + return ERR_INVALID_ARGUMENT; - esize = mono_array_element_size (arr->obj.vtable->klass); + esize = mono_array_element_size (mono_object_get_class (&arr->obj)); for (i = index; i < index + len; ++i) { elem = (gpointer*)((char*)arr->vector + (i * esize)); - - decode_value (&arr->obj.vtable->klass->element_class->byval_arg, arr->obj.vtable->domain, (guint8 *)elem, p, &p, end); + decode_value (mono_class_get_type (mono_class_get_element_class (mono_object_get_class (&arr->obj))), VM_OBJECT_GET_DOMAIN(arr), (guint8 *)elem, p, &p, end); } break; default: @@ -9918,7 +11674,13 @@ string_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } else { MonoError error; s = mono_string_to_utf8_checked (str, &error); - mono_error_assert_ok (&error); + if (!mono_error_ok (&error)) { + if (s) + g_free (s); + buffer_add_string (buf, mono_error_get_message (&error)); + return ERR_INVALID_ARGUMENT; + } + buffer_add_string (buf, s); g_free (s); } @@ -9942,6 +11704,34 @@ string_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +static ErrorCode +pointer_commands (int command, guint8 *p, guint8 *end, Buffer *buf) +{ + ErrorCode err; + gint64 addr; + MonoClass* klass; + MonoDomain* domain = NULL; + + switch (command) { + case CMD_POINTER_GET_VALUE: + addr = decode_long (p, &p, end); + klass = decode_typeid (p, &p, end, &domain, &err); + if (err != ERR_NONE) + return err; + + if (mono_class_get_type (klass)->type != MONO_TYPE_PTR) + return ERR_INVALID_ARGUMENT; + + buffer_add_value (buf, mono_class_get_type (mono_class_get_element_class (klass)), (gpointer)addr, domain); + + break; + default: + return ERR_NOT_IMPLEMENTED; + } + + return ERR_NONE; +} + static ErrorCode object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { @@ -9972,7 +11762,7 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoClass *obj_type; gboolean remote_obj = FALSE; - obj_type = obj->vtable->klass; + obj_type = mono_object_get_class (obj); if (mono_class_is_transparent_proxy (obj_type)) { obj_type = ((MonoTransparentProxy *)obj)->remote_class->proxy_class; remote_obj = TRUE; @@ -9982,8 +11772,12 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) switch (command) { case CMD_OBJECT_REF_GET_TYPE: +#ifdef RUNTIME_IL2CPP + buffer_add_typeid (buf, VM_OBJECT_GET_DOMAIN(obj), mono_object_get_class (obj)); +#else /* This handles transparent proxies too */ - buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type)); + buffer_add_typeid (buf, VM_OBJECT_GET_DOMAIN(obj), mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type)); +#endif // RUNTIME_IL2CPP break; case CMD_OBJECT_REF_GET_VALUES: len = decode_int (p, &p, end); @@ -9995,8 +11789,8 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) /* Check that the field belongs to the object */ found = FALSE; - for (k = obj_type; k; k = k->parent) { - if (k == f->parent) { + for (k = obj_type; k; k = mono_class_get_parent (k)) { + if (k == mono_field_get_parent (f)) { found = TRUE; break; } @@ -10004,22 +11798,22 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (!found) return ERR_INVALID_FIELDID; - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) { + if (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC) { guint8 *val; MonoVTable *vtable; if (mono_class_field_is_special_static (f)) return ERR_INVALID_FIELDID; - g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC); - vtable = mono_class_vtable (obj->vtable->domain, f->parent); - val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type))); + g_assert (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC); + vtable = mono_class_vtable (VM_OBJECT_GET_DOMAIN(obj), mono_field_get_parent (f)); + val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (mono_field_get_type (f)))); mono_field_static_get_value_checked (vtable, f, val, &error); if (!is_ok (&error)) { mono_error_cleanup (&error); /* FIXME report the error */ return ERR_INVALID_OBJECT; } - buffer_add_value (buf, f->type, val, obj->vtable->domain); + buffer_add_value (buf, mono_field_get_type (f), val, VM_OBJECT_GET_DOMAIN(obj)); g_free (val); } else { guint8 *field_value = NULL; @@ -10036,9 +11830,9 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_assert_not_reached (); #endif } else - field_value = (guint8*)obj + f->offset; + field_value = (guint8*)(obj) + mono_field_get_offset (f); - buffer_add_value (buf, f->type, field_value, obj->vtable->domain); + buffer_add_value (buf, mono_field_get_type (f), field_value, VM_OBJECT_GET_DOMAIN(obj)); } } break; @@ -10052,8 +11846,8 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) /* Check that the field belongs to the object */ found = FALSE; - for (k = obj_type; k; k = k->parent) { - if (k == f->parent) { + for (k = obj_type; k; k = mono_class_get_parent (k)) { + if (k == mono_field_get_parent (f)) { found = TRUE; break; } @@ -10061,18 +11855,18 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (!found) return ERR_INVALID_FIELDID; - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) { + if (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC) { guint8 *val; MonoVTable *vtable; if (mono_class_field_is_special_static (f)) return ERR_INVALID_FIELDID; - g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC); - vtable = mono_class_vtable (obj->vtable->domain, f->parent); + g_assert (mono_type_get_attrs (mono_field_get_type (f)) & FIELD_ATTRIBUTE_STATIC); + vtable = mono_class_vtable (VM_OBJECT_GET_DOMAIN(obj), mono_field_get_parent (f)); - val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type))); - err = decode_value (f->type, obj->vtable->domain, val, p, &p, end); + val = (guint8 *)g_malloc (mono_class_instance_size (mono_class_from_mono_type (mono_field_get_type (f)))); + err = decode_value (mono_field_get_type (f), VM_OBJECT_GET_DOMAIN(obj), val, p, &p, end); if (err != ERR_NONE) { g_free (val); return err; @@ -10080,7 +11874,7 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_field_static_set_value (vtable, f, val); g_free (val); } else { - err = decode_value (f->type, obj->vtable->domain, (guint8*)obj + f->offset, p, &p, end); + err = decode_value (mono_field_get_type (f), VM_OBJECT_GET_DOMAIN(obj), (guint8*)obj + mono_field_get_offset (f), p, &p, end); if (err != ERR_NONE) return err; } @@ -10090,11 +11884,11 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_long (buf, (gssize)obj); break; case CMD_OBJECT_REF_GET_DOMAIN: - buffer_add_domainid (buf, obj->vtable->domain); + buffer_add_domainid (buf, VM_OBJECT_GET_DOMAIN(obj)); break; case CMD_OBJECT_REF_GET_INFO: - buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type)); - buffer_add_domainid (buf, obj->vtable->domain); + buffer_add_typeid (buf, VM_OBJECT_GET_DOMAIN(obj), mono_class_from_mono_type(VM_OBJECT_GET_TYPE(obj))); + buffer_add_domainid (buf, VM_OBJECT_GET_DOMAIN(obj)); break; default: return ERR_NOT_IMPLEMENTED; @@ -10135,6 +11929,8 @@ command_set_to_string (CommandSet command_set) return "FIELD"; case CMD_SET_EVENT: return "EVENT"; + case CMD_SET_POINTER: + return "POINTER"; default: return ""; } @@ -10231,7 +12027,9 @@ static const char* type_cmds_str[] = { "GET_METHODS_BY_NAME_FLAGS", "GET_INTERFACES", "GET_INTERFACE_MAP", - "IS_INITIALIZED" + "IS_INITIALIZED", + "CREATE_INSTANCE", + "GET_VALUE_SIZE" }; static const char* stack_frame_cmds_str[] = { @@ -10254,6 +12052,10 @@ static const char* string_cmds_str[] = { "GET_CHARS" }; +static const char* pointer_cmds_str[] = { + "GET_VALUE" +}; + static const char* object_cmds_str[] = { "GET_TYPE", "GET_VALUES", @@ -10327,6 +12129,10 @@ cmd_to_string (CommandSet set, int command) cmds = event_cmds_str; cmds_len = G_N_ELEMENTS (event_cmds_str); break; + case CMD_SET_POINTER: + cmds = pointer_cmds_str; + cmds_len = G_N_ELEMENTS (pointer_cmds_str); + break; default: return NULL; } @@ -10340,19 +12146,8 @@ static gboolean wait_for_attach (void) { #ifndef DISABLE_SOCKET_TRANSPORT - if (listen_fd == -1) { - DEBUG_PRINTF (1, "[dbg] Invalid listening socket\n"); - return FALSE; - } - - /* Block and wait for client connection */ - conn_fd = socket_transport_accept (listen_fd); - - DEBUG_PRINTF (1, "Accepted connection on %d\n", conn_fd); - if (conn_fd == -1) { - DEBUG_PRINTF (1, "[dbg] Bad client connection\n"); + if (!transport_wait_for_attach()) return FALSE; - } #else g_assert_not_reached (); #endif @@ -10389,8 +12184,14 @@ debugger_thread (void *arg) DEBUG_PRINTF (1, "[dbg] Agent thread started, pid=%p\n", (gpointer) (gsize) mono_native_thread_id_get ()); debugger_thread_id = mono_native_thread_id_get (); +#ifdef RUNTIME_IL2CPP + MonoThread *thread = mono_thread_attach (il2cpp_mono_get_root_domain ()); +#endif // RUNTIME_IL2CPP MonoInternalThread *internal = mono_thread_internal_current (); +#ifdef RUNTIME_IL2CPP + internal->debugger_thread = 1; +#endif // RUNTIME_IL2CPP MonoString *str = mono_string_new_checked (mono_domain_get (), "Debugger agent", &error); mono_error_assert_ok (&error); mono_thread_set_name_internal (internal, str, TRUE, FALSE, &error); @@ -10405,11 +12206,26 @@ debugger_thread (void *arg) attach_failed = TRUE; // Don't abort process when we can't listen } else { mono_set_is_debugger_attached (TRUE); + if (attach_func) + attach_func (TRUE); /* Send start event to client */ process_profiler_event (EVENT_KIND_VM_START, mono_thread_get_main ()); +#ifdef RUNTIME_IL2CPP + { + MonoDomain* domain = il2cpp_mono_get_root_domain(); + appdomain_load(NULL, domain); + AgentDomainInfo *info = VM_DOMAIN_GET_AGENT_INFO(domain); + void *iter = NULL; + MonoClass *klass; + while(klass = il2cpp_iterate_loaded_classes(&iter)) + g_hash_table_insert(info->loaded_classes, klass, klass); + } +#endif // RUNTIME_IL2CPP } } else { mono_set_is_debugger_attached (TRUE); + if (attach_func) + attach_func (TRUE); } while (!attach_failed) { @@ -10418,17 +12234,21 @@ debugger_thread (void *arg) /* This will break if the socket is closed during shutdown too */ if (res != HEADER_LENGTH) { DEBUG_PRINTF (1, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH); - break; - } - - p = header; - end = header + HEADER_LENGTH; + len = HEADER_LENGTH; + id = 0; + flags = 0; + command_set = CMD_SET_VM; + command = CMD_VM_DISPOSE; + } else { + p = header; + end = header + HEADER_LENGTH; - len = decode_int (p, &p, end); - id = decode_int (p, &p, end); - flags = decode_byte (p, &p, end); - command_set = (CommandSet)decode_byte (p, &p, end); - command = decode_byte (p, &p, end); + len = decode_int (p, &p, end); + id = decode_int (p, &p, end); + flags = decode_byte (p, &p, end); + command_set = (CommandSet)decode_byte (p, &p, end); + command = decode_byte (p, &p, end); + } g_assert (flags == 0); @@ -10504,6 +12324,9 @@ debugger_thread (void *arg) case CMD_SET_STRING_REF: err = string_commands (command, p, end, &buf); break; + case CMD_SET_POINTER: + err = pointer_commands (command, p, end, &buf); + break; case CMD_SET_OBJECT_REF: err = object_commands (command, p, end, &buf); break; @@ -10537,6 +12360,12 @@ debugger_thread (void *arg) } mono_set_is_debugger_attached (FALSE); + if (attach_func) + attach_func (FALSE); + +#ifdef RUNTIME_IL2CPP + il2cpp_mono_free_method_signatures(); +#endif // RUNTIME_IL2CPP mono_coop_mutex_lock (&debugger_thread_exited_mutex); debugger_thread_exited = TRUE; @@ -10550,8 +12379,231 @@ debugger_thread (void *arg) start_debugger_thread (); } +#ifdef RUNTIME_IL2CPP + mono_thread_detach (thread); +#endif // RUNTIME_IL2CPP + return 0; } +#ifdef RUNTIME_IL2CPP +static void +unity_process_breakpoint_inner(DebuggerTlsData *tls, gboolean from_signal, Il2CppSequencePoint* sequencePoint) +{ + MonoJitInfo *ji = NULL; + guint8 *ip; + int i, j, suspend_policy; + MonoBreakpoint *bp; + BreakpointInstance *inst; + GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs; + GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL; + EventKind kind = EVENT_KIND_BREAKPOINT; + MonoContext *ctx = &tls->restore_state.ctx; + //MonoSeqPointInfo *info; + SeqPoint sp; + gboolean found_sp; + MonoMethod* method = il2cpp_get_seq_point_method(sequencePoint); + + /* + * Skip the instruction causing the breakpoint signal. + */ + unity_mono_jit_find_compiled_method_with_jit_info(mono_domain_get(), method, &ji); + + if (from_signal) + g_assert_not_reached(); + + if (method->wrapper_type || tls->disable_breakpoints) + return; + + bp_reqs = g_ptr_array_new(); + ss_reqs = g_ptr_array_new(); + ss_reqs_orig = g_ptr_array_new(); + + mono_loader_lock(); + + bp = NULL; + for (i = 0; i < breakpoints->len; ++i) { + bp = (MonoBreakpoint *)g_ptr_array_index(breakpoints, i); + + if (!bp->method || !il2cpp_mono_methods_match(bp->method, method)) + continue; + + if (bp->req->event_kind == EVENT_KIND_STEP) + { + SingleStepReq *ss_req = (SingleStepReq *)bp->req->info; + gboolean validFrame = FALSE; + validFrame |= ss_req->depth == STEP_DEPTH_INTO; + validFrame |= ss_req->depth == STEP_DEPTH_OVER && tls->il2cpp_context->frameCount <= ss_req->nframes; + validFrame |= ss_req->depth == STEP_DEPTH_OUT && tls->il2cpp_context->frameCount < ss_req->nframes; + if (!validFrame) + continue; + } + + for (j = 0; j < bp->children->len; ++j) { + inst = (BreakpointInstance *)g_ptr_array_index(bp->children, j); + if (inst->il_offset == sequencePoint->ilOffset) { + if (bp->req->event_kind == EVENT_KIND_STEP) { + for (int k = 0; k < bp->children->len; ++k) + { + BreakpointInstance *inst1 = (BreakpointInstance *)g_ptr_array_index(bp->children, k); + if (inst1->seq_point == sequencePoint) + { + g_ptr_array_add(ss_reqs_orig, bp->req); + break; + } + } + } + else { + g_ptr_array_add(bp_reqs, bp->req); + } + } + } + } + + if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) + { + for (i = 0; i < breakpoints->len; ++i) + { + bp = (MonoBreakpoint *)g_ptr_array_index(breakpoints, i); + + if (bp) + { + /* Maybe a method entry/exit event */ + if ((bp->il_offset == METHOD_ENTRY_IL_OFFSET) && (bp->il_offset == sequencePoint->ilOffset)) + { + kind = EVENT_KIND_METHOD_ENTRY; + break; + } + else if ((bp->il_offset == METHOD_EXIT_IL_OFFSET) && (bp->il_offset == sequencePoint->ilOffset)) + { + kind = EVENT_KIND_METHOD_EXIT; + break; + } + } + } + } + + g_assert(ss_reqs_orig->len <= 1); + if (ss_reqs_orig->len == 1) + { + EventRequest *req = (EventRequest *)g_ptr_array_index(ss_reqs_orig, 0); + SingleStepReq *ss_req = (SingleStepReq *)req->info; + gboolean hit = ss_update_il2cpp(ss_req, tls, ctx, sequencePoint); + if (hit) + { + g_ptr_array_add(ss_reqs, req); + Il2CppSequencePoint *catchFrameSp = NULL; + if (tls->exception) + catchFrameSp = il2cpp_find_catch_sequence_point(tls); + + ss_start_il2cpp(ss_req, tls, catchFrameSp); + } + } + + if (ss_reqs->len > 0) + ss_events = create_event_list(EVENT_KIND_STEP, ss_reqs, sequencePoint, NULL, &suspend_policy); + else if (bp_reqs->len > 0) + bp_events = create_event_list(EVENT_KIND_BREAKPOINT, bp_reqs, sequencePoint, NULL, &suspend_policy); + else if (kind != EVENT_KIND_BREAKPOINT) + enter_leave_events = create_event_list(kind, NULL, sequencePoint, NULL, &suspend_policy); + + mono_loader_unlock(); + + g_ptr_array_free(bp_reqs, TRUE); + g_ptr_array_free(ss_reqs, TRUE); + + /* + * FIXME: The first event will suspend, so the second will only be sent after the + * resume. + */ + if (ss_events) + process_event(EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy); + if (bp_events) + process_event(kind, method, 0, ctx, bp_events, suspend_policy); + if (enter_leave_events) + process_event(kind, method, 0, ctx, enter_leave_events, suspend_policy); +} + +void +unity_debugger_agent_breakpoint(Il2CppSequencePoint* sequencePoint) +{ + DebuggerTlsData *tls; + MonoThreadUnwindState orig_restore_state; + guint8 *orig_ip; + + if (is_debugger_thread()) + return; + + tls = (DebuggerTlsData *)mono_native_tls_get_value(debugger_tls_id); + g_assert(tls); + + save_thread_context(NULL); + + unity_process_breakpoint_inner(tls, FALSE, sequencePoint); +} + +void unity_debugger_agent_pausepoint() +{ + if (is_debugger_thread()) + return; + + save_thread_context(NULL); + suspend_current(); +} + +gboolean unity_pause_point_active() +{ + return unity_debugger_agent_is_global_breakpoint_active() || unity_debugger_agent_is_single_stepping(); +} + +gboolean unity_sequence_point_active_entry(Il2CppSequencePoint *seqPoint) +{ + int i = 0; + while (i < event_requests->len) + { + EventRequest *req = (EventRequest *)g_ptr_array_index (event_requests, i); + + if (req->event_kind == EVENT_KIND_METHOD_ENTRY) + { + return mono_atomic_cas_i32(&seqPoint->isActive, seqPoint->isActive, -1) || g_unity_pause_point_active; + } + + ++i; + } + + return FALSE; +} + +gboolean unity_sequence_point_active_exit(Il2CppSequencePoint *seqPoint) +{ + int i = 0; + while (i < event_requests->len) + { + EventRequest *req = (EventRequest *)g_ptr_array_index (event_requests, i); + + if (req->event_kind == EVENT_KIND_METHOD_EXIT) + { + return mono_atomic_cas_i32(&seqPoint->isActive, seqPoint->isActive, -1) || g_unity_pause_point_active; + } + + ++i; + } + + return FALSE; +} + +void il2cpp_save_current_thread_context_func_exit() +{ + DebuggerTlsData *tls; + + MonoInternalThread *thread = mono_thread_internal_current(); + + mono_loader_lock(); + tls = (DebuggerTlsData *)mono_g_hash_table_lookup(thread_to_tls, thread); + mono_loader_unlock(); + + callbacks.il2cpp_debugger_save_thread_context(&tls->il2cpp_context, -1); +} +#endif // RUNTIME_IL2CPP #else /* DISABLE_DEBUGGER_AGENT */ diff --git a/mono/mini/debugger-agent.h b/mono/mini/debugger-agent.h index 1ff9de11db46..45d67154ff69 100644 --- a/mono/mini/debugger-agent.h +++ b/mono/mini/debugger-agent.h @@ -5,12 +5,36 @@ #ifndef __MONO_DEBUGGER_AGENT_H__ #define __MONO_DEBUGGER_AGENT_H__ +#ifndef RUNTIME_IL2CPP #include "mini.h" +#define VM_DOMAIN_GET_AGENT_INFO(domain) domain_jit_info (domain)->agent_info +#define VM_DOMAIN_SET_AGENT_INFO(domain, value) domain_jit_info (domain)->agent_info = value +#define VM_METHOD_IS_STRING_CTOR(method) method->string_ctor +#define VM_INFLATED_METHOD_GET_DECLARING(imethod) (imethod)->declaring +#define VM_INFLATED_METHOD_GET_CLASS_INST(imethod) (imethod)->context.class_inst +#define VM_OBJECT_GET_DOMAIN(object) ((MonoObject *)object)->vtable->domain +#define VM_OBJECT_GET_TYPE(object) ((MonoReflectionType *)object->vtable->type)->type +#define VM_GENERIC_CLASS_GET_CONTAINER_CLASS(gklass) (gklass)->container_class +#define VM_DEFAULTS_OBJECT_CLASS mono_defaults.object_class +#define VM_DEFAULTS_EXCEPTION_CLASS mono_defaults.exception_class +#define VM_DEFAULTS_CORLIB_IMAGE mono_defaults.corlib +#define VM_DEFAULTS_VOID_CLASS mono_defaults.void_class +#define VM_IMAGE_GET_MODULE_NAME(image) (image)->module_name +#endif + +#ifdef RUNTIME_IL2CPP +#include "il2cpp-compat.h" +#endif #include MONO_API void mono_debugger_agent_parse_options (char *options); +#ifdef RUNTIME_IL2CPP +void +mono_debugger_run_debugger_thread_func(void* arg); +#endif // RUNTIME_IL2CPP + void mono_debugger_agent_init (void); @@ -21,7 +45,11 @@ void mono_debugger_agent_single_step_event (void *sigctx); void +#ifndef RUNTIME_IL2CPP debugger_agent_single_step_from_context (MonoContext *ctx); +#else +debugger_agent_single_step_from_context (MonoContext *ctx, Il2CppSequencePoint* sequencePoint); +#endif void debugger_agent_breakpoint_from_context (MonoContext *ctx); diff --git a/mono/mini/decompose.c b/mono/mini/decompose.c index 93668ac85245..57327a193471 100644 --- a/mono/mini/decompose.c +++ b/mono/mini/decompose.c @@ -1201,6 +1201,7 @@ mono_decompose_vtype_opts (MonoCompile *cfg) if (cfg->verbose_level > 2) mono_print_bb (bb, "BEFORE LOWER-VTYPE-OPTS "); cfg->cbb->code = cfg->cbb->last_ins = NULL; + cfg->cbb->out_of_line = bb->out_of_line; restart = TRUE; while (restart) { @@ -1428,6 +1429,30 @@ mono_decompose_vtype_opts (MonoCompile *cfg) } break; } + case OP_BOX: + case OP_BOX_ICONST: { + MonoInst *src; + + /* Temporary value required by emit_box () */ + if (ins->opcode == OP_BOX_ICONST) { + NEW_ICONST (cfg, src, ins->inst_c0); + src->klass = ins->klass; + MONO_ADD_INS (cfg->cbb, src); + } else { + MONO_INST_NEW (cfg, src, OP_LOCAL); + src->type = STACK_MP; + src->klass = ins->klass; + src->dreg = ins->sreg1; + } + MonoInst *tmp = mini_emit_box (cfg, src, ins->klass, mini_class_check_context_used (cfg, ins->klass)); + g_assert (tmp); + + MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, tmp->dreg); + + /* This might be decomposed into other vtype opcodes */ + restart = TRUE; + break; + } default: break; } diff --git a/mono/mini/exceptions-amd64.c b/mono/mini/exceptions-amd64.c index 6870052bc3bd..c3ee04f508bb 100644 --- a/mono/mini/exceptions-amd64.c +++ b/mono/mini/exceptions-amd64.c @@ -58,7 +58,7 @@ LPTOP_LEVEL_EXCEPTION_FILTER mono_old_win_toplevel_exception_filter; void *mono_win_vectored_exception_handle; #define W32_SEH_HANDLE_EX(_ex) \ - if (_ex##_handler) _ex##_handler(0, ep, ctx) + if (_ex##_handler) _ex##_handler(er->ExceptionCode, &info, ctx) static LONG CALLBACK seh_unhandled_exception_filter(EXCEPTION_POINTERS* ep) { @@ -130,19 +130,19 @@ get_win32_restore_stack (void) * Unhandled Exception Filter * Top-level per-process exception handler. */ -static LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) +LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) { EXCEPTION_RECORD* er; CONTEXT* ctx; LONG res; MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); MonoDomain* domain = mono_domain_get (); + MonoWindowsSigHandlerInfo info = { TRUE, ep }; /* If the thread is not managed by the runtime return early */ if (!jit_tls) return EXCEPTION_CONTINUE_SEARCH; - jit_tls->mono_win_chained_exception_needs_run = FALSE; res = EXCEPTION_CONTINUE_EXECUTION; er = ep->ExceptionRecord; @@ -151,6 +151,17 @@ static LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) switch (er->ExceptionCode) { case EXCEPTION_STACK_OVERFLOW: if (!mono_aot_only && restore_stack) { + if (er->NumberParameters == 2) { + /* Use actual stack fault address rather than relying on the + * SP of the current frame. If we try to enter a method with a + * large prolog (many locals) we may try to allocate more stack + * than is available, however the SP has not been updated. This + * means the stack overflow heuristic to free up enough space + * may fail, as the used stack may be smaller than the amount + * of stack that was attempted to be unwound. + */ + ctx->Rsp = er->ExceptionInformation[1]; /* FaultAddress */ + } if (mono_arch_handle_exception (ctx, domain->stack_overflow_ex)) { /* need to restore stack protection once stack is unwound * restore_stack will restore stack protection and then @@ -159,7 +170,7 @@ static LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) ctx->Rip = (guint64)restore_stack; } } else { - jit_tls->mono_win_chained_exception_needs_run = TRUE; + info.handled = FALSE; } break; case EXCEPTION_ACCESS_VIOLATION: @@ -177,11 +188,11 @@ static LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) W32_SEH_HANDLE_EX(fpe); break; default: - jit_tls->mono_win_chained_exception_needs_run = TRUE; + info.handled = FALSE; break; } - if (jit_tls->mono_win_chained_exception_needs_run) { + if (!info.handled) { /* Don't copy context back if we chained exception * as the handler may have modfied the EXCEPTION_POINTERS * directly. We don't pass sigcontext to chained handlers. @@ -813,12 +824,20 @@ mono_arch_ip_from_context (void *sigctx) #endif } -static void -restore_soft_guard_pages (void) +static MonoObject* +restore_soft_guard_pages () { MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls (); if (jit_tls->stack_ovf_guard_base) mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE); + + if (jit_tls->stack_ovf_pending) { + MonoDomain *domain = mono_domain_get (); + jit_tls->stack_ovf_pending = 0; + return (MonoObject *) domain->stack_overflow_ex; + } + + return NULL; } /* @@ -851,8 +870,11 @@ altstack_handle_and_restore (MonoContext *ctx, MonoObject *obj, gboolean stack_o mctx = *ctx; mono_handle_exception (&mctx, obj); - if (stack_ovf) + if (stack_ovf) { + MonoJitTlsData *jit_tls = (MonoJitTlsData *) mono_tls_get_jit_tls (); + jit_tls->stack_ovf_pending = 1; prepare_for_guard_pages (&mctx); + } mono_restore_context (&mctx); } @@ -1069,11 +1091,11 @@ mono_arch_unwindinfo_add_alloc_stack (PUNWIND_INFO unwindinfo, MonoUnwindOp *unw static gboolean g_dyn_func_table_inited; // Dynamic function table used when registering unwind info for OS unwind support. -static GList *g_dynamic_function_table_begin; -static GList *g_dynamic_function_table_end; +GList *g_dynamic_function_table_begin; +GList *g_dynamic_function_table_end; // SRW lock (lightweight read/writer lock) protecting dynamic function table. -static SRWLOCK g_dynamic_function_table_lock = SRWLOCK_INIT; +SRWLOCK g_dynamic_function_table_lock = SRWLOCK_INIT; // Module handle used when explicit loading ntdll. static HMODULE g_ntdll; diff --git a/mono/mini/exceptions-arm64.c b/mono/mini/exceptions-arm64.c index 53bb332b6994..ed77433b1c6f 100644 --- a/mono/mini/exceptions-arm64.c +++ b/mono/mini/exceptions-arm64.c @@ -36,6 +36,7 @@ mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) size = 256; code = start = mono_global_codeman_reserve (size); + MONO_SCOPE_ENABLE_JIT_WRITE(); arm_movx (code, ARMREG_IP0, ARMREG_R0); ctx_reg = ARMREG_IP0; @@ -85,6 +86,7 @@ mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) size = 512; start = code = mono_global_codeman_reserve (size); + MONO_SCOPE_ENABLE_JIT_WRITE(); /* Compute stack frame size and offsets */ offset = 0; @@ -171,6 +173,7 @@ get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm int i, offset, gregs_offset, fregs_offset, frame_size, num_fregs; code = start = mono_global_codeman_reserve (size); + MONO_SCOPE_ENABLE_JIT_WRITE(); /* We are being called by JITted code, the exception object/type token is in R0 */ diff --git a/mono/mini/exceptions-x86.c b/mono/mini/exceptions-x86.c index b4df2f2c03b7..ca511434b375 100644 --- a/mono/mini/exceptions-x86.c +++ b/mono/mini/exceptions-x86.c @@ -55,7 +55,7 @@ extern int (*gUnhandledExceptionHandler)(EXCEPTION_POINTERS*); #endif #define W32_SEH_HANDLE_EX(_ex) \ - if (_ex##_handler) _ex##_handler(0, ep, ctx) + if (_ex##_handler) _ex##_handler(er->ExceptionCode, &info, ctx) LONG CALLBACK seh_unhandled_exception_filter(EXCEPTION_POINTERS* ep) { @@ -198,12 +198,12 @@ LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) CONTEXT* ctx; LONG res; MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); + MonoWindowsSigHandlerInfo info = { TRUE, ep }; /* If the thread is not managed by the runtime return early */ if (!jit_tls) return EXCEPTION_CONTINUE_SEARCH; - jit_tls->mono_win_chained_exception_needs_run = FALSE; res = EXCEPTION_CONTINUE_EXECUTION; er = ep->ExceptionRecord; @@ -228,11 +228,11 @@ LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep) W32_SEH_HANDLE_EX(fpe); break; default: - jit_tls->mono_win_chained_exception_needs_run = TRUE; + info.handled = FALSE; break; } - if (jit_tls->mono_win_chained_exception_needs_run) { + if (!info.handled) { /* Don't copy context back if we chained exception * as the handler may have modfied the EXCEPTION_POINTERS * directly. We don't pass sigcontext to chained handlers. @@ -1034,12 +1034,20 @@ mono_arch_handle_exception (void *sigctx, gpointer obj) #endif } -static void -restore_soft_guard_pages (void) +static MonoObject* +restore_soft_guard_pages () { MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); + if (jit_tls->stack_ovf_guard_base) mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE); + + if (jit_tls->stack_ovf_pending) { + MonoDomain *domain = mono_domain_get (); + jit_tls->stack_ovf_pending = 0; + return (MonoObject *) domain->stack_overflow_ex; + } + return NULL; } /* @@ -1054,12 +1062,13 @@ prepare_for_guard_pages (MonoContext *mctx) gpointer *sp; sp = (gpointer)(mctx->esp); sp -= 1; - /* the resturn addr */ + /* the return addr */ sp [0] = (gpointer)(mctx->eip); mctx->eip = (unsigned long)restore_soft_guard_pages; mctx->esp = (unsigned long)sp; } + static void altstack_handle_and_restore (MonoContext *ctx, gpointer obj, gboolean stack_ovf) { @@ -1068,8 +1077,11 @@ altstack_handle_and_restore (MonoContext *ctx, gpointer obj, gboolean stack_ovf) mctx = *ctx; mono_handle_exception (&mctx, obj); - if (stack_ovf) + if (stack_ovf) { + MonoJitTlsData *jit_tls = (MonoJitTlsData *) mono_tls_get_jit_tls (); + jit_tls->stack_ovf_pending = 1; prepare_for_guard_pages (&mctx); + } mono_restore_context (&mctx); } diff --git a/mono/mini/generics.cs b/mono/mini/generics.cs index eafbd1c0e541..10472335825f 100644 --- a/mono/mini/generics.cs +++ b/mono/mini/generics.cs @@ -1391,6 +1391,24 @@ public static int test_0_isreference_intrins () { return 0; } + + class LdobjStobj { + public int counter; + public LdobjStobj buffer1; + public LdobjStobj buffer2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void swap(ref T first, ref T second) { + second = first; + } + + public static int test_42_ldobj_stobj_ref () { + var obj = new LdobjStobj (); + obj.counter = 42; + swap (ref obj.buffer1, ref obj.buffer2); + return obj.counter; + } } #if !__MOBILE__ diff --git a/mono/mini/jit-icalls.c b/mono/mini/jit-icalls.c index 44960ceb04e1..3e4fb32b05f8 100644 --- a/mono/mini/jit-icalls.c +++ b/mono/mini/jit-icalls.c @@ -1472,8 +1472,17 @@ ves_icall_mono_delegate_ctor (MonoObject *this_obj_raw, MonoObject *target_raw, MonoError error; MONO_HANDLE_DCL (MonoObject, this_obj); MONO_HANDLE_DCL (MonoObject, target); + + if (!addr) { + error_init (&error); + mono_error_set_argument_null (&error, "method", ""); + mono_error_set_pending_exception (&error); + goto leave; + } mono_delegate_ctor (this_obj, target, addr, &error); mono_error_set_pending_exception (&error); + +leave: HANDLE_FUNCTION_RETURN (); } diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index ed9001779cf9..10f9e9b0a5b0 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -1254,8 +1254,13 @@ check_values_to_signature (MonoInst *args, MonoType *this_ins, MonoMethodSignatu inline static MonoInst * mono_get_domainvar (MonoCompile *cfg) { - if (!cfg->domainvar) + if (!cfg->domainvar) { + /* Make sure we don't generate references after checking whenever to init this */ + g_assert (!cfg->domainvar_inited); cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + /* Avoid optimizing it away */ + cfg->domainvar->flags |= MONO_INST_VOLATILE; + } return cfg->domainvar; } @@ -3731,8 +3736,8 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ /* * Returns NULL and set the cfg exception on error. */ -static MonoInst* -handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) +MonoInst * +mini_emit_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) { MonoInst *alloc, *ins; @@ -3843,14 +3848,14 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) MONO_START_BB (cfg, end_bb); return res; - } else { - alloc = handle_alloc (cfg, klass, TRUE, context_used); - if (!alloc) - return NULL; - - EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); - return alloc; } + + alloc = handle_alloc (cfg, klass, TRUE, context_used); + if (!alloc) + return NULL; + + EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); + return alloc; } static gboolean @@ -3863,8 +3868,8 @@ method_needs_stack_walk (MonoCompile *cfg, MonoMethod *cmethod) return FALSE; } -static G_GNUC_UNUSED MonoInst* -handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag) +G_GNUC_UNUSED MonoInst* +mini_handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, int enum_val_reg, MonoInst *enum_flag) { MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg); guint32 load_opc = mono_type_to_load_membase (cfg, enum_type); @@ -3890,7 +3895,12 @@ handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, M int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg); int dest_reg = alloc_ireg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0); + if (enum_this) { + EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0); + } else { + g_assert (enum_val_reg != -1); + enum_reg = enum_val_reg; + } EMIT_NEW_BIALU (cfg, and_, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg); EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg); EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1); @@ -5780,6 +5790,40 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (mono_type_is_native_blittable (arg0)) return mini_emit_memory_load (cfg, arg0, args [0], 0, 0); } + } else if (cmethod->klass == mono_defaults.enum_class && !strcmp (cmethod->name, "HasFlag") && + args[0]->opcode == OP_BOX && args[1]->opcode == OP_BOX_ICONST && args[0]->klass == args[1]->klass) { + args[1]->opcode = OP_ICONST; + ins = mini_handle_enum_has_flag (cfg, args[0]->klass, NULL, args[0]->sreg1, args[1]); + NULLIFY_INS (args[0]); + return ins; + } else if (!strcmp (cmethod->klass->image->assembly->aname.name, "System") && + !strcmp (cmethod->klass->name_space, "System") && + !strcmp (cmethod->klass->name, "Platform") && + !strcmp (cmethod->name, "get_IsMacOS")) { +#if defined(TARGET_OSX) + EMIT_NEW_ICONST (cfg, ins, 1); +#else + EMIT_NEW_ICONST (cfg, ins, 0); +#endif + return ins; + } else if (!strcmp (cmethod->klass->image->assembly->aname.name, "System") && + !strcmp (cmethod->klass->name_space, "System") && + !strcmp (cmethod->klass->name, "Platform") && + !strcmp (cmethod->name, "get_IsFreeBSD")) { +#if defined(__FreeBSD__) + EMIT_NEW_ICONST (cfg, ins, 1); +#else + EMIT_NEW_ICONST (cfg, ins, 0); +#endif + return ins; + } else if (!strcmp (cmethod->klass->image->assembly->aname.name, "System") && + !strcmp (cmethod->klass->name_space, "System.Net.NetworkInformation") && + !strcmp (cmethod->klass->name, "IPGlobalProperties") && + !strcmp (cmethod->name, "get_PlatformNeedsLibCWorkaround")) { +#if defined(HOST_ANDROID) + EMIT_NEW_ICONST (cfg, ins, 1); + return ins; +#endif } #ifdef MONO_ARCH_SIMD_INTRINSICS @@ -7255,7 +7299,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (cfg->method == method) - cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size); + cfg->coverage_info = mono_profiler_coverage_alloc (cfg->domain, cfg->method, header->code_size); if (cfg->compile_aot && cfg->coverage_info) g_error ("Coverage profiling is not supported with AOT."); @@ -8481,7 +8525,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* The called method is not virtual, i.e. Object:GetType (), the receiver is a vtype, has to box */ EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0); ins->klass = constrained_class; - sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); + sp [0] = mini_emit_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); CHECK_CFG_EXCEPTION; } else if (need_box) { MonoInst *box_type; @@ -8512,7 +8556,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_START_BB (cfg, is_ref_bb); EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0); ins->klass = constrained_class; - sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); + sp [0] = mini_emit_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); @@ -8536,7 +8580,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b */ EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0); ins->klass = constrained_class; - sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); + sp [0] = mini_emit_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); CHECK_CFG_EXCEPTION; } else if (!constrained_class->valuetype) { int dreg = alloc_ireg_ref (cfg); @@ -8570,7 +8614,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Enum implements some interfaces, so treat this as the first case */ EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0); ins->klass = constrained_class; - sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); + sp [0] = mini_emit_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class)); CHECK_CFG_EXCEPTION; } } @@ -9744,7 +9788,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } /* Optimize the ldobj+stobj combination */ - if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token)) { + if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass)) { CHECK_STACK (1); sp --; @@ -10162,7 +10206,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b enum_this = sp [0]; enum_flag = sp [1]; - *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag); + *sp++ = mini_handle_enum_has_flag (cfg, klass, enum_this, -1, enum_flag); break; } } @@ -10240,7 +10284,30 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; } - *sp++ = handle_box (cfg, val, klass, context_used); + if (mono_class_is_enum (klass) && !(val->type == STACK_I8 && SIZEOF_VOID_P == 4)) { + + /* Can't do this with 64 bit enums on 32 bit since the vtype decomp pass is ran after the long decomp pass */ + if (val->opcode == OP_ICONST) { + MONO_INST_NEW (cfg, ins, OP_BOX_ICONST); + ins->type = STACK_OBJ; + ins->klass = klass; + ins->inst_c0 = val->inst_c0; + ins->dreg = alloc_dreg (cfg, val->type); + } else { + MONO_INST_NEW (cfg, ins, OP_BOX); + ins->type = STACK_OBJ; + ins->klass = klass; + ins->sreg1 = val->dreg; + ins->dreg = alloc_dreg (cfg, val->type); + } + MONO_ADD_INS (cfg->cbb, ins); + *sp++ = ins; + /* Create domainvar early so it gets initialized earlier than this code */ + if (cfg->opt & MONO_OPT_SHARED) + mono_get_domainvar (cfg); + } else { + *sp++ = mini_emit_box (cfg, val, klass, context_used); + } CHECK_CFG_EXCEPTION; ip += 5; @@ -10740,6 +10807,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg); store->flags |= ins_flag; + if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && + mini_type_is_reference (ftype)) { + /* insert call to write barrier. This is not needed by sgen, as it does not seem + to need write barriers for uncollectable objects (like the vtables storing static + fields), but it is needed for incremental boehm. */ + mini_emit_write_barrier (cfg, store, ins); + } } else { gboolean is_const = FALSE; MonoVTable *vtable = NULL; @@ -12384,7 +12458,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b CHECK_OPSIZE (4); n = read16 (ip + 2); CHECK_LOCAL (n); - if ((ip [4] == CEE_LDFLD) && ip_in_bb (cfg, cfg->cbb, ip + 4) && header->locals [n]->type == MONO_TYPE_VALUETYPE) { + if ((ip [4] == CEE_LDFLD) && ip_in_bb (cfg, cfg->cbb, ip + 4) && MONO_TYPE_ISSTRUCT (header->locals [n])) { /* Avoid loading a struct just to load one of its fields */ EMIT_NEW_LOCLOADA (cfg, ins, n); } else { @@ -12687,6 +12761,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b get_domain = mono_create_tls_get (cfg, TLS_KEY_DOMAIN); NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain); MONO_ADD_INS (cfg->cbb, store); + cfg->domainvar_inited = TRUE; } #if defined(TARGET_POWERPC) || defined(TARGET_X86) diff --git a/mono/mini/mini-amd64.c b/mono/mini/mini-amd64.c index 4b7a7cc77507..309dcd95c2ff 100644 --- a/mono/mini/mini-amd64.c +++ b/mono/mini/mini-amd64.c @@ -606,7 +606,7 @@ add_valuetype_win64 (MonoMethodSignature *signature, ArgInfo *arg_info, MonoType get_valuetype_size_win64 (klass, signature->pinvoke, arg_info, type, &arg_class, &arg_size); /* Only drop value type if its not an empty struct as input that must be represented in call */ - if ((arg_size == 0 && !arg_info->pass_empty_struct) || (arg_size == 0 && arg_info->pass_empty_struct && is_return)) { + if ((arg_size == 0 && !arg_info->pass_empty_struct) || (arg_info->pass_empty_struct && is_return)) { arg_info->storage = ArgValuetypeInReg; arg_info->pair_storage [0] = arg_info->pair_storage [1] = ArgNone; } else { @@ -2228,7 +2228,8 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) g_assert (ainfo->storage == ArgValuetypeAddrInIReg || (ainfo->storage == ArgValuetypeAddrOnStack && ainfo->pair_storage [0] == ArgNone)); vtaddr = mono_compile_create_var (cfg, &ins->klass->byval_arg, OP_LOCAL); - + vtaddr->backend.is_pinvoke = call->signature->pinvoke; + MONO_INST_NEW (cfg, load, OP_LDADDR); cfg->has_indirection = TRUE; load->inst_p0 = vtaddr; diff --git a/mono/mini/mini-amd64.h b/mono/mini/mini-amd64.h index 7fa4e1841348..30291512d4f3 100644 --- a/mono/mini/mini-amd64.h +++ b/mono/mini/mini-amd64.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -32,7 +33,7 @@ struct sigcontext { }; #endif -typedef void (* MonoW32ExceptionHandler) (int _dummy, EXCEPTION_POINTERS *info, void *context); +typedef void MONO_SIG_HANDLER_SIGNATURE ((*MonoW32ExceptionHandler)); void win32_seh_init(void); void win32_seh_cleanup(void); void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler); diff --git a/mono/mini/mini-arm.c b/mono/mini/mini-arm.c index 75e4fd098a4e..b2601706af5d 100644 --- a/mono/mini/mini-arm.c +++ b/mono/mini/mini-arm.c @@ -1524,7 +1524,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) ainfo->storage = RegTypeStructByVal; ainfo->struct_size = size; ainfo->align = align; - /* FIXME: align stack_size if needed */ + if (eabi_supported) { if (align >= 8 && (gr & 1)) gr ++; @@ -1542,9 +1542,8 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) gr += n_in_regs; nwords -= n_in_regs; } - if (sig->call_convention == MONO_CALL_VARARG) - /* This matches the alignment in mono_ArgIterator_IntGetNextArg () */ - stack_size = ALIGN_TO (stack_size, align); + stack_size = ALIGN_TO (stack_size, align); + ainfo->offset = stack_size; /*g_print ("offset for arg %d at %d\n", n, stack_size);*/ stack_size += nwords * sizeof (gpointer); diff --git a/mono/mini/mini-arm64.c b/mono/mini/mini-arm64.c index 092bfd45385a..06602d855489 100644 --- a/mono/mini/mini-arm64.c +++ b/mono/mini/mini-arm64.c @@ -56,6 +56,10 @@ static gpointer bp_trampoline; static gboolean ios_abi; +#if defined(__APPLE__) +__thread jit_protect_mode arm_current_jit_protect_mode = JPM_NONE; +#endif + static __attribute__ ((__warn_unused_result__)) guint8* emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset); const char* @@ -100,6 +104,8 @@ get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *co { guint8 *code, *start; + MONO_SCOPE_ENABLE_JIT_WRITE(); + if (has_target) { start = code = mono_global_codeman_reserve (12); @@ -242,7 +248,7 @@ mono_arch_init (void) mono_arm_gsharedvt_init (); -#if defined(TARGET_IOS) +#if defined(TARGET_IOS) || defined(TARGET_OSX) ios_abi = TRUE; #endif } @@ -751,7 +757,7 @@ mono_arm_emit_aotconst (gpointer ji, guint8 *code, guint8 *code_start, int dreg, gboolean mono_arch_have_fast_tls (void) { -#ifdef TARGET_IOS +#if defined(TARGET_IOS) || defined(TARGET_OSX) return FALSE; #else return TRUE; @@ -5009,6 +5015,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTC else buf = mono_domain_code_reserve (domain, buf_len); code = buf; + MONO_SCOPE_ENABLE_JIT_WRITE(); /* * We are called by JITted code, which passes in the IMT argument in diff --git a/mono/mini/mini-arm64.h b/mono/mini/mini-arm64.h index 960b3e27250a..a1cb91aa921c 100644 --- a/mono/mini/mini-arm64.h +++ b/mono/mini/mini-arm64.h @@ -154,7 +154,7 @@ typedef struct { #define MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION 1 #define MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS 1 -#ifdef TARGET_IOS +#if defined(TARGET_IOS) || defined(TARGET_OSX) #define MONO_ARCH_REDZONE_SIZE 128 @@ -164,10 +164,69 @@ typedef struct { #endif -#if defined(TARGET_APPLETVOS) || defined(TARGET_IOS) +#if defined(TARGET_APPLETVOS) || defined(TARGET_IOS) || defined(TARGET_OSX) #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1 #endif +#if defined(TARGET_OSX) + +typedef enum { + JPM_NONE, + JPM_ENABLED, + JPM_DISABLED, +} jit_protect_mode; + +extern __thread jit_protect_mode arm_current_jit_protect_mode; + +static void mono_arm_jit_write_protect_enable() +{ + if (__builtin_available(macOS 11, *)) { + if (arm_current_jit_protect_mode != JPM_ENABLED) { + pthread_jit_write_protect_np(1); + arm_current_jit_protect_mode = JPM_ENABLED; + } + } +} + +static void mono_arm_jit_write_protect_disable() +{ + if (__builtin_available(macOS 11, *)) { + if (arm_current_jit_protect_mode != JPM_DISABLED) { + pthread_jit_write_protect_np(0); + arm_current_jit_protect_mode = JPM_DISABLED; + } + } +} + +#define MONO_SCOPE_ENABLE_JIT_WRITE() \ + __attribute__((unused, cleanup(mono_arm_restore_jit_protect_mode))) \ + jit_protect_mode scope_restrict_mode = arm_current_jit_protect_mode; \ + mono_arm_jit_write_protect_disable(); \ + +#define MONO_SCOPE_ENABLE_JIT_EXEC() \ + __attribute__((unused, cleanup(mono_arm_restore_jit_protect_mode))) \ + jit_protect_mode scope_restrict_mode = arm_current_jit_protect_mode; \ + mono_arm_jit_write_protect_enable(); \ + +static void mono_arm_restore_jit_protect_mode(jit_protect_mode* previous_jit_protect_mode) +{ + if (*previous_jit_protect_mode == arm_current_jit_protect_mode) + return; + + switch (*previous_jit_protect_mode) + { + case JPM_ENABLED: + mono_arm_jit_write_protect_enable(); + break; + case JPM_DISABLED: + case JPM_NONE: + default: + mono_arm_jit_write_protect_disable(); + } +} + +#endif + /* Relocations */ #define MONO_R_ARM64_B 1 #define MONO_R_ARM64_BCC 2 diff --git a/mono/mini/mini-exceptions.c b/mono/mini/mini-exceptions.c index fa050c7d79f6..03b709025402 100644 --- a/mono/mini/mini-exceptions.c +++ b/mono/mini/mini-exceptions.c @@ -805,8 +805,8 @@ get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx) /* * generic_info is either a MonoMethodRuntimeGenericContext or a MonoVTable. */ -static MonoGenericContext -get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) +MonoGenericContext +mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) { MonoGenericContext context = { NULL, NULL }; MonoClass *klass, *method_container_class; @@ -851,6 +851,7 @@ get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) return context; } + static MonoMethod* get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) { @@ -860,7 +861,7 @@ get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this) return jinfo_get_method (ji); - context = get_generic_context_from_stack_frame (ji, generic_info); + context = mono_get_generic_context_from_stack_frame (ji, generic_info); method = jinfo_get_method (ji); method = mono_method_get_declaring_generic_method (method); @@ -1386,7 +1387,7 @@ get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContex if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this) return catch_class; - context = get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx)); + context = mono_get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx)); /* FIXME: we shouldn't inflate but instead put the type in the rgctx and fetch it from there. It @@ -2712,6 +2713,33 @@ static void print_process_map (void) static gboolean handle_crash_loop = FALSE; +MONO_API int +mono_unity_backtrace_from_context (void* context, void* array[], int count) +{ +#ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX + MonoContext mctx; + void* ip = 0; + void** bp = 0; + int idx = 0; + + mono_sigctx_to_monoctx(context, &mctx); + + ip = (void*)MONO_CONTEXT_GET_IP(&mctx); + bp = (void**)MONO_CONTEXT_GET_BP(&mctx); + + while(bp && count-- > 0) + { + array[idx++] = ip; + + ip = bp[1]; + bp = (void**)bp[0]; + } + + return idx; +#else + return 0; +#endif +} /* * mono_handle_native_crash: * @@ -3446,7 +3474,7 @@ mono_llvm_match_exception (MonoJitInfo *jinfo, guint32 region_start, guint32 reg MonoType *inflated_type; g_assert (rgctx || this_obj); - context = get_generic_context_from_stack_frame (jinfo, rgctx ? rgctx : this_obj->vtable); + context = mono_get_generic_context_from_stack_frame (jinfo, rgctx ? rgctx : this_obj->vtable); inflated_type = mono_class_inflate_generic_type_checked (&catch_class->byval_arg, &context, &error); mono_error_assert_ok (&error); /* FIXME don't swallow the error */ diff --git a/mono/mini/mini-gc.c b/mono/mini/mini-gc.c index b21dd1be3202..fdafb9f03c82 100644 --- a/mono/mini/mini-gc.c +++ b/mono/mini/mini-gc.c @@ -2627,7 +2627,7 @@ mini_gc_set_slot_type_from_cfa (MonoCompile *cfg, int slot_offset, GCSlotType ty void mini_gc_init_cfg (MonoCompile *cfg) { - if (mono_gc_is_moving ()) { + if (mono_gc_needs_write_barriers()) { cfg->disable_ref_noref_stack_slot_share = TRUE; cfg->gen_write_barriers = TRUE; } diff --git a/mono/mini/mini-generic-sharing.c b/mono/mini/mini-generic-sharing.c index 9d95b3ae207f..d95ebe4d11e5 100644 --- a/mono/mini/mini-generic-sharing.c +++ b/mono/mini/mini-generic-sharing.c @@ -3460,12 +3460,15 @@ shared_gparam_equal (gconstpointer ka, gconstpointer kb) MonoType* mini_get_shared_gparam (MonoType *t, MonoType *constraint) { + MonoImageSet *set; MonoGenericParam *par = t->data.generic_param; MonoGSharedGenericParam *copy, key; MonoType *res; MonoImage *image = NULL; char *name; + set = mono_metadata_merge_image_sets (mono_metadata_get_image_set_for_type (t), mono_metadata_get_image_set_for_type (constraint)); + memset (&key, 0, sizeof (key)); key.parent = par; key.param.param.gshared_constraint = constraint; @@ -3477,23 +3480,24 @@ mini_get_shared_gparam (MonoType *t, MonoType *constraint) * Need a cache to ensure the newly created gparam * is unique wrt T/CONSTRAINT. */ - mono_image_lock (image); - if (!image->gshared_types) { - image->gshared_types_len = MONO_TYPE_INTERNAL; - image->gshared_types = g_new0 (GHashTable*, image->gshared_types_len); - } - if (!image->gshared_types [constraint->type]) - image->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal); - res = (MonoType *)g_hash_table_lookup (image->gshared_types [constraint->type], &key); - mono_image_unlock (image); + mono_image_set_lock (set); + if (!set->gshared_types) { + set->gshared_types_len = MONO_TYPE_INTERNAL; + set->gshared_types = g_new0 (GHashTable*, set->gshared_types_len); + } + if (!set->gshared_types [constraint->type]) + set->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal); + res = (MonoType *)g_hash_table_lookup (set->gshared_types [constraint->type], &key); + mono_image_set_unlock (set); if (res) return res; - copy = (MonoGSharedGenericParam *)mono_image_alloc0 (image, sizeof (MonoGSharedGenericParam)); + copy = (MonoGSharedGenericParam *)mono_image_set_alloc0 (set, sizeof (MonoGSharedGenericParam)); memcpy (©->param, par, sizeof (MonoGenericParamFull)); copy->param.info.pklass = NULL; - constraint = mono_metadata_type_dup (image, constraint); + // FIXME: + constraint = mono_metadata_type_dup (NULL, constraint); name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name); - copy->param.info.name = mono_image_strdup (image, name); + copy->param.info.name = mono_image_set_strdup (set, name); g_free (name); copy->param.param.owner = par->owner; @@ -3503,12 +3507,10 @@ mini_get_shared_gparam (MonoType *t, MonoType *constraint) res = mono_metadata_type_dup (NULL, t); res->data.generic_param = (MonoGenericParam*)copy; - if (image) { - mono_image_lock (image); - /* Duplicates are ok */ - g_hash_table_insert (image->gshared_types [constraint->type], copy, res); - mono_image_unlock (image); - } + mono_image_set_lock (set); + /* Duplicates are ok */ + g_hash_table_insert (set->gshared_types [constraint->type], copy, res); + mono_image_set_unlock (set); return res; } @@ -3545,6 +3547,8 @@ get_shared_type (MonoType *t, MonoType *type) ttype = type->type; if (type->type == MONO_TYPE_VALUETYPE) { ttype = mono_class_enum_basetype (type->data.klass)->type; + } else if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype) { + ttype = mono_class_enum_basetype (mono_generic_class_get_class (type->data.generic_class))->type; } else if (MONO_TYPE_IS_REFERENCE (type)) { ttype = MONO_TYPE_OBJECT; } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) { diff --git a/mono/mini/mini-ops.h b/mono/mini/mini-ops.h index 3381b21cef4f..f71b6c2116b1 100644 --- a/mono/mini/mini-ops.h +++ b/mono/mini/mini-ops.h @@ -42,6 +42,9 @@ MINI_OP(OP_CHECK_THIS, "checkthis", NONE, IREG, NONE) MINI_OP(OP_SEQ_POINT, "seq_point", NONE, NONE, NONE) MINI_OP(OP_IL_SEQ_POINT, "il_seq_point", NONE, NONE, NONE) MINI_OP(OP_IMPLICIT_EXCEPTION, "implicit_exception", NONE, NONE, NONE) +MINI_OP(OP_BOX, "box", IREG, IREG, NONE) +/* A box of the int value in inst_c0 */ +MINI_OP(OP_BOX_ICONST, "box_iconst", IREG, NONE, NONE) /* CALL opcodes need to stay together, see MONO_IS_CALL macro */ MINI_OP(OP_VOIDCALL, "voidcall", NONE, NONE, NONE) diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index 6a27b69fc127..a59e62b28d89 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -85,6 +85,7 @@ #include "mini-llvm.h" #include "debugger-agent.h" #include "lldb.h" +#include "mixed_callstack_plugin.h" #include "mini-runtime.h" #ifdef MONO_ARCH_LLVM_SUPPORTED @@ -498,6 +499,7 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo mono_save_trampoline_xdebug_info (info); mono_lldb_save_trampoline_info (info); + mixed_callstack_plugin_save_trampoline_info (info); #ifdef MONO_ARCH_HAVE_UNWIND_TABLE if (!aot) @@ -2171,6 +2173,8 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_ gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error) { + MONO_SCOPE_ENABLE_JIT_WRITE(); + gpointer code; code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), FALSE, error); @@ -2802,6 +2806,7 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec if (!is_ok (error)) return NULL; } else { + MONO_SCOPE_ENABLE_JIT_EXEC(); runtime_invoke = (MonoObject *(*)(MonoObject *, void **, MonoObject **, void *))info->runtime_invoke; result = runtime_invoke ((MonoObject *)obj, params, exc, info->compiled_method); @@ -3431,6 +3436,8 @@ mini_parse_debug_option (const char *option) debug_options.gdb = TRUE; else if (!strcmp (option, "lldb")) debug_options.lldb = TRUE; + else if (!strcmp (option, "unity-mixed-callstack")) + debug_options.unity_mixed_callstack = TRUE; else if (!strcmp (option, "explicit-null-checks")) debug_options.explicit_null_checks = TRUE; else if (!strcmp (option, "gen-seq-points")) @@ -3920,6 +3927,9 @@ mini_init (const char *filename, const char *runtime_version) mono_lldb_init (""); mono_dont_free_domains = TRUE; } + if (mini_get_debug_options()->unity_mixed_callstack || g_hasenv ("UNITY_MIXED_CALLSTACK")) { + mixed_callstack_plugin_init (""); + } #ifdef XDEBUG_ENABLED char *mono_xdebug = g_getenv ("MONO_XDEBUG"); diff --git a/mono/mini/mini-runtime.h b/mono/mini/mini-runtime.h index c4dcd503f6ab..8aab82d8811a 100644 --- a/mono/mini/mini-runtime.h +++ b/mono/mini/mini-runtime.h @@ -79,6 +79,7 @@ struct MonoJitTlsData { gpointer stack_ovf_guard_base; guint32 stack_ovf_guard_size; guint stack_ovf_valloced : 1; + guint stack_ovf_pending : 1; void (*abort_func) (MonoObject *object); /* Used to implement --debug=casts */ MonoClass *class_cast_from, *class_cast_to; @@ -100,11 +101,6 @@ struct MonoJitTlsData { MonoContext orig_ex_ctx; gboolean orig_ex_ctx_set; - /* - * Stores if we need to run a chained exception in Windows. - */ - gboolean mono_win_chained_exception_needs_run; - /* * The current exception in flight */ diff --git a/mono/mini/mini-trampolines.c b/mono/mini/mini-trampolines.c index 9558c9132576..30fa8daf2914 100644 --- a/mono/mini/mini-trampolines.c +++ b/mono/mini/mini-trampolines.c @@ -21,6 +21,7 @@ #include "mini.h" #include "lldb.h" +#include "mixed_callstack_plugin.h" #include "aot-runtime.h" #include "mini-runtime.h" @@ -1384,6 +1385,7 @@ mono_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, M else code = mono_arch_create_specific_trampoline (arg1, tramp_type, domain, &len); mono_lldb_save_specific_trampoline_info (arg1, tramp_type, domain, code, len); + mixed_callstack_plugin_save_specific_trampoline_info (arg1, tramp_type, domain, code, len); if (code_len) *code_len = len; return code; diff --git a/mono/mini/mini-unity.c b/mono/mini/mini-unity.c new file mode 100644 index 000000000000..84cd5567bbb4 --- /dev/null +++ b/mono/mini/mini-unity.c @@ -0,0 +1,32 @@ +#include +#include + +#ifdef WIN32 + +extern LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep); +LONG mono_unity_seh_handler(EXCEPTION_POINTERS* ep) +{ +#if defined(TARGET_X86) || defined(TARGET_AMD64) + return seh_vectored_exception_handler(ep); +#else + g_assert_not_reached(); +#endif +} + +int (*gUnhandledExceptionHandler)(EXCEPTION_POINTERS*) = NULL; + +void mono_unity_set_unhandled_exception_handler(void* handler) +{ + gUnhandledExceptionHandler = handler; +} + +#endif // WIN32 + +extern gboolean unity_shutting_down; + +MONO_API void +mono_unity_jit_cleanup (MonoDomain *domain) +{ + unity_shutting_down = TRUE; + mono_jit_cleanup (domain); +} diff --git a/mono/mini/mini-windows.c b/mono/mini/mini-windows.c index a10343f44e2b..25ed139e26fc 100644 --- a/mono/mini/mini-windows.c +++ b/mono/mini/mini-windows.c @@ -255,8 +255,8 @@ mono_runtime_cleanup_handlers (void) gboolean MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal) { - MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); - jit_tls->mono_win_chained_exception_needs_run = TRUE; + /* Set to FALSE to indicate that vectored exception handling should continue to look for handler */ + MONO_SIG_HANDLER_GET_INFO ()->handled = FALSE; return TRUE; } diff --git a/mono/mini/mini-x86.h b/mono/mini/mini-x86.h index 3cf44a06b46a..59d74012d8b4 100644 --- a/mono/mini/mini-x86.h +++ b/mono/mini/mini-x86.h @@ -16,7 +16,7 @@ #include #endif -typedef void (* MonoW32ExceptionHandler) (int _dummy, EXCEPTION_POINTERS *info, void *context); +typedef void MONO_SIG_HANDLER_SIGNATURE ((*MonoW32ExceptionHandler)); void win32_seh_init(void); void win32_seh_cleanup(void); diff --git a/mono/mini/mini.c b/mono/mini/mini.c index bdda6265d3e9..949a7436b568 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -78,6 +78,7 @@ #include "llvm-runtime.h" #include "mini-llvm.h" #include "lldb.h" +#include "mixed_callstack_plugin.h" #include "aot-runtime.h" #include "mini-runtime.h" @@ -1156,6 +1157,7 @@ mono_allocate_stack_slots2 (MonoCompile *cfg, gboolean backward, guint32 *stack_ StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info; MonoType *t; int nvtypes; + int vtype_stack_slots_size = 256; gboolean reuse_slot; LSCAN_DEBUG (printf ("Allocate Stack Slots 2 for %s:\n", mono_method_full_name (cfg->method, TRUE))); @@ -1234,14 +1236,22 @@ mono_allocate_stack_slots2 (MonoCompile *cfg, gboolean backward, guint32 *stack_ /* Fall through */ case MONO_TYPE_VALUETYPE: if (!vtype_stack_slots) - vtype_stack_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * 256); + vtype_stack_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * vtype_stack_slots_size); for (i = 0; i < nvtypes; ++i) if (t->data.klass == vtype_stack_slots [i].vtype) break; if (i < nvtypes) slot_info = &vtype_stack_slots [i]; else { - g_assert (nvtypes < 256); + if (nvtypes == vtype_stack_slots_size) { + int new_slots_size = vtype_stack_slots_size * 2; + StackSlotInfo* new_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * new_slots_size); + + memcpy (new_slots, vtype_stack_slots, sizeof (StackSlotInfo) * vtype_stack_slots_size); + + vtype_stack_slots = new_slots; + vtype_stack_slots_size = new_slots_size; + } vtype_stack_slots [nvtypes].vtype = t->data.klass; slot_info = &vtype_stack_slots [nvtypes]; nvtypes ++; @@ -1467,6 +1477,7 @@ mono_allocate_stack_slots (MonoCompile *cfg, gboolean backward, guint32 *stack_s StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info; MonoType *t; int nvtypes; + int vtype_stack_slots_size = 256; gboolean reuse_slot; if ((cfg->num_varinfo > 0) && MONO_VARINFO (cfg, 0)->interval) @@ -1532,14 +1543,22 @@ mono_allocate_stack_slots (MonoCompile *cfg, gboolean backward, guint32 *stack_s /* Fall through */ case MONO_TYPE_VALUETYPE: if (!vtype_stack_slots) - vtype_stack_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * 256); + vtype_stack_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * vtype_stack_slots_size); for (i = 0; i < nvtypes; ++i) if (t->data.klass == vtype_stack_slots [i].vtype) break; if (i < nvtypes) slot_info = &vtype_stack_slots [i]; else { - g_assert (nvtypes < 256); + if (nvtypes == vtype_stack_slots_size) { + int new_slots_size = vtype_stack_slots_size * 2; + StackSlotInfo* new_slots = (StackSlotInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (StackSlotInfo) * new_slots_size); + + memcpy (new_slots, vtype_stack_slots, sizeof (StackSlotInfo) * vtype_stack_slots_size); + + vtype_stack_slots = new_slots; + vtype_stack_slots_size = new_slots_size; + } vtype_stack_slots [nvtypes].vtype = t->data.klass; slot_info = &vtype_stack_slots [nvtypes]; nvtypes ++; @@ -2204,6 +2223,8 @@ mono_codegen (MonoCompile *cfg) MonoDomain *code_domain; guint unwindlen = 0; + MONO_SCOPE_ENABLE_JIT_WRITE(); + if (mono_using_xdebug) /* * Recent gdb versions have trouble processing symbol files containing @@ -2790,6 +2811,8 @@ create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile) jinfo->unwind_info = cfg->used_int_regs; } + jinfo->dbg_ignore = !cfg->gen_sdb_seq_points; + return jinfo; } @@ -3075,6 +3098,8 @@ init_backend (MonoBackend *backend) MonoCompile* mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFlags flags, int parts, int aot_method_index) { + MONO_SCOPE_ENABLE_JIT_WRITE(); + MonoMethodHeader *header; MonoMethodSignature *sig; MonoError err; @@ -3861,6 +3886,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl if (!cfg->compile_aot) { mono_save_xdebug_info (cfg); mono_lldb_save_method_info (cfg); + mixed_callstack_plugin_save_method_info (cfg); } if (cfg->verbose_level >= 2) { diff --git a/mono/mini/mini.h b/mono/mini/mini.h index d1618623b7bd..61b937faf7a1 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -1446,6 +1446,7 @@ typedef struct { guint gsharedvt : 1; guint r4fp : 1; guint llvm_only : 1; + guint domainvar_inited : 1; int r4_stack_type; gpointer debug_info; guint32 lmf_offset; @@ -1849,6 +1850,7 @@ typedef struct { gboolean dyn_runtime_invoke; gboolean gdb; gboolean lldb; + gboolean unity_mixed_callstack; gboolean use_fallback_tls; /* * Whenever data such as next sequence points and flags is required. @@ -2262,6 +2264,7 @@ void mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb); MonoInst *mono_branch_optimize_exception_target (MonoCompile *cfg, MonoBasicBlock *bb, const char * exname); void mono_remove_critical_edges (MonoCompile *cfg); gboolean mono_is_regsize_var (MonoType *t); +MonoInst * mini_emit_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used); void mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align); void mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align); void mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native); @@ -2276,6 +2279,7 @@ void mini_emit_memory_store (MonoCompile *cfg, MonoType *type, Mono void mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag); void mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag); void mini_emit_memory_copy (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native, int ins_flag); +MonoInst * mini_handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, int enum_val_reg, MonoInst *enum_flag); MonoMethod* mini_get_memcpy_method (void); MonoMethod* mini_get_memset_method (void); @@ -2879,4 +2883,15 @@ gboolean MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal); #endif +MonoGenericContext +mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info); + +#ifndef MONO_SCOPE_ENABLE_JIT_WRITE +#define MONO_SCOPE_ENABLE_JIT_WRITE() +#endif + +#ifndef MONO_SCOPE_ENABLE_JIT_EXEC +#define MONO_SCOPE_ENABLE_JIT_EXEC() +#endif + #endif /* __MONO_MINI_H__ */ diff --git a/mono/mini/mixed_callstack_plugin.c b/mono/mini/mixed_callstack_plugin.c new file mode 100644 index 000000000000..0b26dcfa6d88 --- /dev/null +++ b/mono/mini/mixed_callstack_plugin.c @@ -0,0 +1,155 @@ +#include "mixed_callstack_plugin.h" +#include "mono/metadata/mono-debug.h" +#include "mono/metadata/profiler.h" + +#if !defined(DISABLE_JIT) && defined(HOST_WIN32) + +static gboolean enabled; +static mono_mutex_t mutex; +static HANDLE fileHandle; +int pmipFileNum; + +#define mixed_callstack_plugin_lock() mono_os_mutex_lock (&mutex) +#define mixed_callstack_plugin_unlock() mono_os_mutex_unlock (&mutex) + +void +create_next_pmip_file() +{ + char* file_name = g_strdup_printf("pmip_%d_%d.txt", GetCurrentProcessId(), pmipFileNum++); + char* path = g_build_filename(g_get_tmp_dir(), file_name, NULL); + char* version = "UnityMixedCallstacks:1.0\n"; + long bytesWritten = 0; + + mixed_callstack_plugin_lock (); + + if(fileHandle) + CloseHandle(fileHandle); + + fileHandle = CreateFileA(path, + GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_FLAG_DELETE_ON_CLOSE, + NULL); + + if (INVALID_HANDLE_VALUE != fileHandle) + enabled = TRUE; + + WriteFile(fileHandle, version, strlen(version), &bytesWritten, NULL); + + mixed_callstack_plugin_unlock (); + + g_free(file_name); + g_free(path); +} + +void +mixed_callstack_plugin_init (const char *options) +{ + pmipFileNum = 0; + + mono_os_mutex_init_recursive(&mutex); + + MonoProfilerHandle prof = mono_profiler_create(NULL); + mono_profiler_set_domain_unloaded_callback(prof, mixed_callstack_plugin_on_domain_unload_end); + + create_next_pmip_file(); +} + +void +mixed_callstack_plugin_on_domain_unload_end() +{ + if(!enabled) + return; + + create_next_pmip_file(); +} + +void +mixed_callstack_plugin_save_method_info (MonoCompile *cfg) +{ + char* method_name; + long bytesWritten = 0; + char frame[1024]; + int bytes; + + if (!enabled) + return; + + method_name = mono_method_full_name (cfg->method, TRUE); + + bytes = snprintf (frame, sizeof (frame), "%p;%p;[%s] %s\n", cfg->native_code, ((char*)cfg->native_code) + cfg->code_size, cfg->method->klass->image->module_name, method_name); + /* negative value is encoding error */ + if (bytes < 0 || bytes > sizeof (frame)) + return; + + mixed_callstack_plugin_lock (); + WriteFile(fileHandle, frame, bytes, &bytesWritten, NULL); + mixed_callstack_plugin_unlock (); + + g_free(method_name); +} + +void +mixed_callstack_plugin_remove_method (MonoDomain *domain, MonoMethod *method, MonoJitDynamicMethodInfo *info) +{ +} + +void +mixed_callstack_plugin_save_trampoline_info (MonoTrampInfo *info) +{ + char* frame; + long bytesWritten = 0; + + if (!enabled) + return; + + mixed_callstack_plugin_lock (); + frame = g_strdup_printf ("%p;%p;%s\n", info->code, ((char*)info->code) + info->code_size, info->name ? info->name : ""); + WriteFile(fileHandle, frame, strlen(frame), &bytesWritten, NULL); + mixed_callstack_plugin_unlock (); + + g_free(frame); +} + +void +mixed_callstack_plugin_save_specific_trampoline_info (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, gpointer code, guint32 code_len) +{ + +} + +#else + +void +mixed_callstack_plugin_init (const char *options) +{ + g_error ("Only Available On Windows With Jit Enabled"); +} + +void +mixed_callstack_plugin_on_domain_unload_end() +{ +} + +void +mixed_callstack_plugin_save_method_info (MonoCompile *cfg) +{ +} + +void +mixed_callstack_plugin_save_trampoline_info (MonoTrampInfo *info) +{ +} + +void +mixed_callstack_plugin_remove_method (MonoDomain *domain, MonoMethod *method, MonoJitDynamicMethodInfo *info) +{ +} + +void +mixed_callstack_plugin_save_specific_trampoline_info (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, gpointer code, guint32 code_len) +{ +} + +#endif diff --git a/mono/mini/mixed_callstack_plugin.h b/mono/mini/mixed_callstack_plugin.h new file mode 100644 index 000000000000..89935824e7dd --- /dev/null +++ b/mono/mini/mixed_callstack_plugin.h @@ -0,0 +1,18 @@ +#ifndef __MIXED_CALLSTACK_PLUGIN_H__ +#define __MIXED_CALLSTACK_PLUGIN_H__ + +#include "config.h" +#include "mini.h" + +void mixed_callstack_plugin_init (const char *options); + +void mixed_callstack_plugin_save_method_info (MonoCompile *cfg); + +void mixed_callstack_plugin_save_trampoline_info (MonoTrampInfo *info); + +void mixed_callstack_plugin_remove_method (MonoDomain *domain, MonoMethod *method, MonoJitDynamicMethodInfo *info); + +void mixed_callstack_plugin_save_specific_trampoline_info (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, gpointer code, guint32 code_len); + +void mixed_callstack_plugin_on_domain_unload_end(); +#endif diff --git a/mono/mini/objects.cs b/mono/mini/objects.cs index 97ca3c180115..b6e1246fc74a 100644 --- a/mono/mini/objects.cs +++ b/mono/mini/objects.cs @@ -1,6 +1,7 @@ using System; using System.Text; using System.Reflection; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -1829,6 +1830,30 @@ public static int test_0_typedref () { return 0; } + + public interface IFoo + { + int MyInt { get; } + } + + public class IFooImpl : IFoo + { + public int MyInt => 0; + } + + //gh 6266 + public static int test_0_store_to_magic_iface_array () + { + ICollection arr1 = new IFooImpl[1] { new IFooImpl() }; + ICollection arr2 = new IFooImpl[1] { new IFooImpl() }; + + ICollection[] a2d = new ICollection[2] { + arr1, + arr2, + }; + + return 0; + } } #if __MOBILE__ diff --git a/mono/mini/seq-points.c b/mono/mini/seq-points.c index 4bb98711471f..56543f1b8331 100644 --- a/mono/mini/seq-points.c +++ b/mono/mini/seq-points.c @@ -250,6 +250,7 @@ mono_save_seq_point_info (MonoCompile *cfg) cfg->seq_points = NULL; } +/* LOCKING: Acquires the domain lock */ MonoSeqPointInfo* mono_get_seq_points (MonoDomain *domain, MonoMethod *method) { @@ -261,7 +262,7 @@ mono_get_seq_points (MonoDomain *domain, MonoMethod *method) shared_method = mini_get_shared_method (method); } - mono_loader_lock (); + mono_domain_lock (domain); seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method); if (!seq_points && method->is_inflated) { /* generic sharing + aot */ @@ -269,7 +270,7 @@ mono_get_seq_points (MonoDomain *domain, MonoMethod *method) if (!seq_points) seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method); } - mono_loader_unlock (); + mono_domain_unlock (domain); return seq_points; } diff --git a/mono/mini/simd-intrinsics.c b/mono/mini/simd-intrinsics.c index 92d7a4eb5681..4d621650af6b 100644 --- a/mono/mini/simd-intrinsics.c +++ b/mono/mini/simd-intrinsics.c @@ -2081,6 +2081,9 @@ emit_vector_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignatu MonoMethodSignature *sig = mono_method_signature (cmethod); MonoType *type = &cmethod->klass->byval_arg; + if (!cmethod->klass->simd_type) + return NULL; + /* * Vector2/3/4 are handled the same way, since the underlying SIMD type is the same (4 * r4). */ diff --git a/mono/mini/tramp-arm64.c b/mono/mini/tramp-arm64.c index a46b26ae4316..d39ad5752f70 100644 --- a/mono/mini/tramp-arm64.c +++ b/mono/mini/tramp-arm64.c @@ -32,6 +32,8 @@ void mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr) { + MONO_SCOPE_ENABLE_JIT_WRITE(); + mono_arm_patch (code_ptr - 4, addr, MONO_R_ARM64_BL); mono_arch_flush_icache (code_ptr - 4, 4); } @@ -113,6 +115,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf buf_len = 768; buf = code = mono_global_codeman_reserve (buf_len); + MONO_SCOPE_ENABLE_JIT_WRITE(); /* * We are getting called by a specific trampoline, ip1 contains the trampoline argument. @@ -328,6 +331,7 @@ mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_ty * Pass the argument in ip1, clobbering ip0. */ tramp = mono_get_trampoline_code (tramp_type); + MONO_SCOPE_ENABLE_JIT_WRITE(); buf = code = mono_global_codeman_reserve (buf_len); @@ -352,6 +356,8 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) MonoDomain *domain = mono_domain_get (); start = code = mono_domain_code_reserve (domain, size); + MONO_SCOPE_ENABLE_JIT_WRITE(); + code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr); arm_addx_imm (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject)); arm_brx (code, ARMREG_IP0); @@ -369,6 +375,8 @@ mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr) MonoDomain *domain = mono_domain_get (); start = code = mono_domain_code_reserve (domain, buf_len); + MONO_SCOPE_ENABLE_JIT_WRITE(); + code = mono_arm_emit_imm64 (code, MONO_ARCH_RGCTX_REG, (guint64)arg); code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr); arm_brx (code, ARMREG_IP0); @@ -407,6 +415,7 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info buf_size = 64 + 16 * depth; code = buf = mono_global_codeman_reserve (buf_size); + MONO_SCOPE_ENABLE_JIT_WRITE(); rgctx_null_jumps = g_malloc0 (sizeof (guint8*) * (depth + 2)); njumps = 0; @@ -493,6 +502,7 @@ mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboo tramp_size = 32; code = buf = mono_global_codeman_reserve (tramp_size); + MONO_SCOPE_ENABLE_JIT_WRITE(); mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, 0); @@ -531,6 +541,7 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo MonoJumpInfo *ji = NULL; code = buf = mono_global_codeman_reserve (tramp_size); + MONO_SCOPE_ENABLE_JIT_WRITE(); /* Compute stack frame size and offsets */ offset = 0; @@ -631,6 +642,8 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) buf_len = 512 + 1024; start = code = (guint8 *) mono_global_codeman_reserve (buf_len); + MONO_SCOPE_ENABLE_JIT_WRITE(); + /* save FP and LR */ framesize += 2 * sizeof (mgreg_t); diff --git a/mono/profiler/log-args.c b/mono/profiler/log-args.c index 0c8bd040a386..3c707283859d 100644 --- a/mono/profiler/log-args.c +++ b/mono/profiler/log-args.c @@ -27,6 +27,7 @@ static NameAndMask event_list[] = { { "counters", PROFLOG_COUNTER_EVENTS }, { "alloc", PROFLOG_ALLOC_ALIAS, PROFLOG_ALLOC_ALIAS | PROFLOG_GC_ROOT_EVENTS }, + { "fileio", PROFLOG_FILEIO_EVENTS }, { "legacy", PROFLOG_LEGACY_ALIAS }, }; diff --git a/mono/profiler/log.c b/mono/profiler/log.c index e0c19bed121e..c31f386edde0 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -112,7 +112,10 @@ static gint32 sync_points_ctr, coverage_methods_ctr, coverage_statements_ctr, coverage_classes_ctr, - coverage_assemblies_ctr; + coverage_assemblies_ctr, + fileio_reads_ctr, + fileio_writes_ctr; + // Pending data to be written to the log, for a single thread. // Threads periodically flush their own LogBuffers by calling safe_send @@ -1656,6 +1659,37 @@ finalize_object_end (MonoProfiler *prof, MonoObject *obj) EXIT_LOG; } +static void +fileio (MonoProfiler *prof, uint64_t kind, uint64_t size) +{ + if (kind == 0) + { + ENTER_LOG (&fileio_writes_ctr, buf, + EVENT_SIZE /* event */ + + LEB128_SIZE /* kind */ + + LEB128_SIZE /* size */ + ); + emit_event (buf, TYPE_FILEIO); + emit_value (buf, kind); + emit_value (buf, size); + + EXIT_LOG; + } + else + { + ENTER_LOG (&fileio_reads_ctr, buf, + EVENT_SIZE /* event */ + + LEB128_SIZE /* kind */ + + LEB128_SIZE /* size */ + ); + emit_event (buf, TYPE_FILEIO); + emit_value (buf, kind); + emit_value (buf, size); + + EXIT_LOG; + } +} + static char* push_nesting (char *p, MonoClass *klass) { @@ -4416,6 +4450,28 @@ proflog_icall_SetGCAllocationEvents (MonoBoolean value) mono_coop_mutex_unlock (&log_profiler.api_mutex); } +ICALL_EXPORT MonoBoolean +proflog_icall_GetFileIOEvents (void) +{ + return ENABLED (PROFLOG_FILEIO_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetFileIOEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_FILEIO_EVENTS); + mono_profiler_set_fileio_callback (log_profiler.handle, fileio); + } else { + DISABLE (PROFLOG_FILEIO_EVENTS); + mono_profiler_set_fileio_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + ICALL_EXPORT MonoBoolean proflog_icall_GetGCMoveEvents (void) { @@ -4604,6 +4660,8 @@ runtime_initialized (MonoProfiler *profiler) register_counter ("Event: Coverage statements", &coverage_statements_ctr); register_counter ("Event: Coverage classes", &coverage_classes_ctr); register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr); + register_counter ("Event: File IO reads", &fileio_reads_ctr); + register_counter ("Event: File IO writes", &fileio_writes_ctr); counters_init (); @@ -4848,6 +4906,9 @@ mono_profiler_init_log (const char *desc) mono_profiler_set_gc_finalizing_object_callback (handle, finalize_object_begin); } + if (ENABLED (PROFLOG_FILEIO_EVENTS)) + mono_profiler_set_fileio_callback (handle, fileio); + //On Demand heapshot uses the finalizer thread to force a collection and thus a heapshot mono_profiler_set_gc_finalized_callback (handle, finalize_end); diff --git a/mono/profiler/log.h b/mono/profiler/log.h index 8ea1991e36ec..7d306a764997 100644 --- a/mono/profiler/log.h +++ b/mono/profiler/log.h @@ -386,6 +386,7 @@ enum { TYPE_RUNTIME, TYPE_COVERAGE, TYPE_META, + TYPE_FILEIO, /* extended type for TYPE_HEAP */ TYPE_HEAP_START = 0 << 4, TYPE_HEAP_END = 1 << 4, @@ -491,10 +492,11 @@ typedef enum { #define PROFLOG_COUNTER_EVENTS (1 << 8) #define PROFLOG_SAMPLE_EVENTS (1 << 9) #define PROFLOG_JIT_EVENTS (1 << 10) +#define PROFLOG_FILEIO_EVENTS (1 << 11) #define PROFLOG_ALLOC_ALIAS (PROFLOG_GC_EVENTS | PROFLOG_GC_ALLOCATION_EVENTS | PROFLOG_GC_MOVE_EVENTS) #define PROFLOG_HEAPSHOT_ALIAS (PROFLOG_GC_EVENTS | PROFLOG_GC_ROOT_EVENTS) -#define PROFLOG_LEGACY_ALIAS (PROFLOG_EXCEPTION_EVENTS | PROFLOG_MONITOR_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_GC_MOVE_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_HANDLE_EVENTS | PROFLOG_GC_FINALIZATION_EVENTS | PROFLOG_COUNTER_EVENTS) +#define PROFLOG_LEGACY_ALIAS (PROFLOG_EXCEPTION_EVENTS | PROFLOG_MONITOR_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_GC_MOVE_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_HANDLE_EVENTS | PROFLOG_GC_FINALIZATION_EVENTS | PROFLOG_COUNTER_EVENTS | PROFLOG_FILEIO_EVENTS) typedef struct { //Events explicitly enabled diff --git a/mono/sgen/gc-internal-agnostic.h b/mono/sgen/gc-internal-agnostic.h index 1bc27c711c6a..ad9ddb589487 100644 --- a/mono/sgen/gc-internal-agnostic.h +++ b/mono/sgen/gc-internal-agnostic.h @@ -117,7 +117,9 @@ FILE *mono_gc_get_logfile (void); /* equivalent to options set via MONO_GC_PARAMS */ void mono_gc_params_set (const char* options); +char* mono_gc_params_get(); /* equivalent to options set via MONO_GC_DEBUG */ void mono_gc_debug_set (const char* options); +char *mono_gc_debug_get(); #endif diff --git a/mono/sgen/sgen-gc.c b/mono/sgen/sgen-gc.c index 8e597946baa8..6eb3cedf0a86 100644 --- a/mono/sgen/sgen-gc.c +++ b/mono/sgen/sgen-gc.c @@ -302,8 +302,6 @@ static SGEN_TV_DECLARE (time_major_conc_collection_end); int gc_debug_level = 0; FILE* gc_debug_file; -static char* gc_params_options; -static char* gc_debug_options; /* void @@ -1022,24 +1020,6 @@ mono_gc_get_logfile (void) return gc_debug_file; } -void -mono_gc_params_set (const char* options) -{ - if (gc_params_options) - g_free (gc_params_options); - - gc_params_options = g_strdup (options); -} - -void -mono_gc_debug_set (const char* options) -{ - if (gc_debug_options) - g_free (gc_debug_options); - - gc_debug_options = g_strdup (options); -} - static void scan_finalizer_entries (SgenPointerQueue *fin_queue, ScanCopyContext ctx) { @@ -2593,7 +2573,7 @@ typedef struct { static SgenGcRequest gc_request; -#include +#include static void gc_pump_callback (void) @@ -3326,11 +3306,7 @@ sgen_gc_init (void) mono_coop_mutex_init (&sgen_interruption_mutex); - if ((env = g_getenv (MONO_GC_PARAMS_NAME)) || gc_params_options) { - params_opts = g_strdup_printf ("%s,%s", gc_params_options ? gc_params_options : "", env ? env : ""); - g_free (env); - } - + params_opts = mono_gc_params_get(); if (params_opts) { opts = g_strsplit (params_opts, ",", -1); for (ptr = opts; *ptr; ++ptr) { @@ -3541,11 +3517,7 @@ sgen_gc_init (void) sgen_pinning_init (); sgen_cement_init (cement_enabled); - if ((env = g_getenv (MONO_GC_DEBUG_NAME)) || gc_debug_options) { - debug_opts = g_strdup_printf ("%s,%s", gc_debug_options ? gc_debug_options : "", env ? env : ""); - g_free (env); - } - + debug_opts = mono_gc_debug_get(); if (debug_opts) { gboolean usage_printed = FALSE; diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index cff377fc0276..6e1e40a481ba 100755 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -76,7 +76,7 @@ MCS_NO_UNSAFE = $(TOOLS_RUNTIME) $(CSC) -debug:portable \ -noconfig -nologo \ -nowarn:0162 -nowarn:0168 -nowarn:0219 -nowarn:0414 -nowarn:0618 \ -nowarn:0169 -nowarn:1690 -nowarn:0649 -nowarn:0612 -nowarn:3021 \ - -nowarn:0197 $(PROFILE_MCS_FLAGS) + -nowarn:0197 -langversion:7.3 $(PROFILE_MCS_FLAGS) MCS_NO_LIB = $(MCS_NO_UNSAFE) -unsafe MCS = $(MCS_NO_LIB) @@ -374,6 +374,7 @@ TESTS_CS_SRC= \ generic-stack-traces2.2.cs \ bug-472600.2.cs \ recursive-generics.2.cs \ + recursive-generics.3.cs \ bug-473482.2.cs \ bug-473999.2.cs \ bug-479763.2.cs \ @@ -530,14 +531,23 @@ TESTS_CS_SRC= \ bug-59281.cs \ init_array_with_lazy_type.cs \ weak-fields.cs \ - threads-leak.cs + threads-leak.cs \ + generic-unmanaged-constraint.cs if AMD64 TESTS_CS_SRC += async-exc-compilation.cs finally_guard.cs finally_block_ending_in_dead_bb.cs +if !HOST_WIN32 +# requires working altstack +TESTS_CS_SRC += bug-60862.cs +endif endif if X86 TESTS_CS_SRC += async-exc-compilation.cs finally_guard.cs finally_block_ending_in_dead_bb.cs +if !HOST_WIN32 +# requires working altstack +TESTS_CS_SRC += bug-60862.cs +endif endif TESTS_IL_SRC= \ @@ -973,6 +983,7 @@ DISABLED_TESTS = \ # exit code 0, but doesn't actually execute the test. # block_guard_restore_aligment_on_exit.exe: flaky (10% of the time it hangs and thus times out) # weak-fields.exe: https://bugzilla.xamarin.com/show_bug.cgi?id=60973 +# bug-60862.exe: missing support to map IP->method; only works on platforms with altstack support. INTERP_DISABLED_TESTS = \ $(KNOWN_FAILING_TESTS) \ $(INTERP_PLATFORM_DISABLED_TESTS) \ @@ -993,6 +1004,7 @@ INTERP_DISABLED_TESTS = \ bug-58782-plain-throw.exe \ bug-81673.exe \ bug445361.exe \ + bug-60862.exe \ calliGenericTest.exe \ cominterop.exe \ context-static.exe \ diff --git a/mono/tests/bug-60862.cs b/mono/tests/bug-60862.cs new file mode 100644 index 000000000000..251838f584dd --- /dev/null +++ b/mono/tests/bug-60862.cs @@ -0,0 +1,54 @@ +/* https://bugzilla.xamarin.com/show_bug.cgi?id=60862 */ +using System; +using System.Threading; + +namespace StackOverflowTest +{ + class Program + { + static bool fault = false; + static Exception ex = null; + + public static int Main(string[] args) + { + Thread t = new Thread (Run); + t.Start (); + t.Join (); + if (fault) { + if (ex == null) { + Console.WriteLine ("fault occured, but no exception object available"); + return 1; + } else { + bool is_stackoverlfow = ex is StackOverflowException; + Console.WriteLine ("fault occured: ex = " + is_stackoverlfow); + return is_stackoverlfow ? 0 : 3; + } + } + Console.WriteLine("no fault"); + return 2; + } + + static void Run() + { + try { + Execute (); + } catch(Exception e) { + ex = e; + fault = true; + } + } + + static void Execute () + { + WaitOne (); + } + + static bool WaitOne (bool killProcessOnInterrupt = false, bool throwOnInterrupt = false) + { + try { + return WaitOne(); + } catch(ThreadInterruptedException e) { } + return false; + } + } +} diff --git a/mono/tests/generic-unmanaged-constraint.cs b/mono/tests/generic-unmanaged-constraint.cs new file mode 100644 index 000000000000..0718ec0c8980 --- /dev/null +++ b/mono/tests/generic-unmanaged-constraint.cs @@ -0,0 +1,17 @@ +using System; + +unsafe class Program +{ + public static int Main(string[] args) + { + return (int)(IntPtr)Generic.GetPtr(); + } +} + +unsafe class Generic where T : unmanaged +{ + public static T* GetPtr() + { + return (T*)null; + } +} diff --git a/mono/tests/libtest.c b/mono/tests/libtest.c index 46f8cc5d3d52..338d8223555e 100644 --- a/mono/tests/libtest.c +++ b/mono/tests/libtest.c @@ -280,6 +280,25 @@ mono_return_nested_float (void) return f; } +struct Scalar4 { + double val[4]; +}; + +struct Rect { + int x; + int y; + int width; + int height; +}; + +LIBTEST_API char * STDCALL +mono_return_struct_4_double (void *ptr, struct Rect rect, struct Scalar4 sc4, int a, int b, int c) +{ + char *buffer = (char *) malloc (1024 * sizeof (char)); + sprintf (buffer, "sc4 = {%.1f, %.1f, %.1f, %.1f }, a=%x, b=%x, c=%x\n", (float) sc4.val [0], (float) sc4.val [1], (float) sc4.val [2], (float) sc4.val [3], a, b, c); + return buffer; +} + LIBTEST_API int STDCALL mono_test_many_int_arguments (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j); diff --git a/mono/tests/pinvoke11.cs b/mono/tests/pinvoke11.cs index 252700fe3442..776dbc4035f2 100644 --- a/mono/tests/pinvoke11.cs +++ b/mono/tests/pinvoke11.cs @@ -60,6 +60,39 @@ public struct NestedFloat { public float f4; } +[Serializable] +[StructLayout(LayoutKind.Sequential)] +public struct Rectangle +{ + public int X; + public int Y; + public int Width; + public int Height; + + public Rectangle(int x, int y, int width, int height) + { + X = x; + Y = y; + Width = width; + Height = height; + } +} + +[Serializable] +public struct Scalar4 { + public double Val0; + public double Val1; + public double Val2; + public double Val3; + + public Scalar4 (double v0, double v1, double v2, double v3) { + Val0 = v0; + Val1 = v1; + Val2 = v2; + Val3 = v3; + } +} + public class Test { [DllImport ("libtest")] @@ -89,6 +122,10 @@ public class Test [DllImport ("libtest", EntryPoint="mono_return_nested_float")] public static extern NestedFloat mono_return_nested_float (); + [DllImport("libtest", EntryPoint="mono_return_struct_4_double")] + [return: MarshalAs(UnmanagedType.LPStr)] + public static extern string mono_return_struct_4_double (IntPtr ptr, Rectangle rect, Scalar4 sc4, int a, int b, int c); + static int Main() { if (mono_return_int (5) != 5) @@ -159,6 +196,14 @@ static int Main() if (f.fi.f1 != 1.0) return 12; + Rectangle rect = new Rectangle (10, 10, 100, 20); + Scalar4 sc4 = new Scalar4 (32, 64, 128, 256); + var sc4_ret = mono_return_struct_4_double (IntPtr.Zero, rect, sc4, 0x1337, 0x1234, 0x9876); + if (sc4_ret != "sc4 = {32.0, 64.0, 128.0, 256.0 }, a=1337, b=1234, c=9876\n") { + Console.WriteLine ("sc4_ret = " + sc4_ret); + return 13; + } + return 0; } } diff --git a/mono/tests/recursive-generics.3.cs b/mono/tests/recursive-generics.3.cs new file mode 100644 index 000000000000..11bba0a53111 --- /dev/null +++ b/mono/tests/recursive-generics.3.cs @@ -0,0 +1,45 @@ +using System; + +class Program { + static void Main (string[] args) + { + // If this runs without a TLE, the test passed. A + // TypeLoadException due to recursion during type + // initialization is a failure. + var subC = new SubClass (); + Console.WriteLine (subC.GetTest ()); + // same as above, but try to land in generic sharing code. + var genSubC = new GenericSubClass (); + Console.WriteLine (genSubC.GetTest ()); + } +} + +public struct ValueTest { + // When U is instantiated with T, from BaseClass, we know it'll be a + // reference field without having to fully initialize its parent + // (namely BaseClass itself), so we know the instantiation + // ValueTest won't be blittable. + public readonly U value; +} + +public abstract class BaseClass where T : BaseClass { + public ValueTest valueTest = default (ValueTest); +} + +public class SubClass : BaseClass { + private string test = "test"; + + public string GetTest() + { + return test; + } +} + +public class GenericSubClass : BaseClass> { + private string test = "test"; + + public string GetTest() + { + return test; + } +} diff --git a/mono/utils/atomic.h b/mono/utils/atomic.h index bbf79ebea4ab..c26f65b5e938 100755 --- a/mono/utils/atomic.h +++ b/mono/utils/atomic.h @@ -167,7 +167,7 @@ static inline void mono_atomic_store_i8 (volatile gint8 *dst, gint8 val) { #if (_MSC_VER >= 1600) - InterlockedExchange8 ((CHAR volatile *)dst, (CHAR)val); + _InterlockedExchange8 ((CHAR volatile *)dst, (CHAR)val); #else *dst = val; mono_memory_barrier (); diff --git a/mono/utils/dlmalloc.c b/mono/utils/dlmalloc.c index 296893ff2e7f..39850671934f 100644 --- a/mono/utils/dlmalloc.c +++ b/mono/utils/dlmalloc.c @@ -461,7 +461,9 @@ DEFAULT_MMAP_THRESHOLD default: 256K #endif /* _WIN32 */ #endif /* WIN32 */ #ifdef WIN32 +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include #define HAVE_MMAP 1 #define HAVE_MORECORE 0 @@ -1214,7 +1216,7 @@ extern void* sbrk(ptrdiff_t); # define _SC_PAGE_SIZE _SC_PAGESIZE # endif # endif -# ifdef _SC_PAGE_SIZE +# if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE) # define malloc_getpagesize sysconf(_SC_PAGE_SIZE) # else # if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) diff --git a/mono/utils/gc_wrapper.h b/mono/utils/gc_wrapper.h index 0bc7a45d2254..6133d90c131f 100644 --- a/mono/utils/gc_wrapper.h +++ b/mono/utils/gc_wrapper.h @@ -11,7 +11,29 @@ #include #ifdef HAVE_BOEHM_GC +#ifdef HAVE_BDWGC_GC +/* Use unity's updated Boehm GC from IL2CPP */ +#define ALL_INTERIOR_POINTERS 1 +#define GC_GCJ_SUPPORT 1 +#define JAVA_FINALIZATION 1 +#define NO_EXECUTE_PERMISSION 1 +#define GC_NO_THREADS_DISCOVERY 1 +#define IGNORE_DYNAMIC_LOADING 1 +#define GC_DONT_REGISTER_MAIN_STATIC_DATA 1 +#define GC_VERSION_MAJOR 7 +#define GC_VERSION_MINOR 7 +#define GC_VERSION_MICRO 0 +#define GC_THREADS 1 +#define USE_MMAP 1 +#define USE_MUNMAP 1 +#define GC_EventType GCEventType + +# include +# include +# include +#else /* HAVE_BDWGC_GC */ +/*Use Mono's Boehm */ # ifdef _MSC_VER # include # else @@ -48,6 +70,7 @@ #if defined(HOST_WIN32) #define CreateThread GC_CreateThread #endif +#endif /* HAVE_BDWGC_GC */ #elif defined(HAVE_SGEN_GC) diff --git a/mono/utils/mach-support-arm64.c b/mono/utils/mach-support-arm64.c index 33c076974114..9281d12e0cf7 100644 --- a/mono/utils/mach-support-arm64.c +++ b/mono/utils/mach-support-arm64.c @@ -29,9 +29,13 @@ static const int known_tls_offsets[] = { +#if defined(TARGET_OSX) + 0xE0, /* macOS 11.0 on Apple silicon */ +#else 0x48, /*Found on iOS 6 */ 0xA4, 0xA8, +#endif // defined(HOST_OSX) }; #define TLS_PROBE_COUNT (sizeof (known_tls_offsets) / sizeof (int)) diff --git a/mono/utils/mono-codeman.c b/mono/utils/mono-codeman.c index 4738808880bb..1383d29c5469 100644 --- a/mono/utils/mono-codeman.c +++ b/mono/utils/mono-codeman.c @@ -57,7 +57,7 @@ static MonoCodeManagerCallbacks code_manager_callbacks; #define MAX_WASTAGE 32 #define MIN_BSIZE 32 -#ifdef __x86_64__ +#if defined(__x86_64__) && !defined(__APPLE__) #define ARCH_MAP_FLAGS MONO_MMAP_32BIT #else #define ARCH_MAP_FLAGS 0 diff --git a/mono/utils/mono-compiler.h b/mono/utils/mono-compiler.h index f659cdeaf1d6..7bdf1e256a1e 100644 --- a/mono/utils/mono-compiler.h +++ b/mono/utils/mono-compiler.h @@ -36,7 +36,9 @@ #endif #include +#if _MSC_VER < 1900 /* VS 2015 */ #define trunc(x) (((x) < 0) ? ceil((x)) : floor((x))) +#endif #if _MSC_VER < 1800 /* VS 2013 */ #define isnan(x) _isnan(x) #define isinf(x) (_isnan(x) ? 0 : (_fpclass(x) == _FPCLASS_NINF) ? -1 : (_fpclass(x) == _FPCLASS_PINF) ? 1 : 0) @@ -169,7 +171,7 @@ typedef int32_t __mono_off32_t; #include #endif -#if !defined(mmap) +#if !defined(mmap) && !defined(__clang__) /* Unified headers before API 21 do not declare mmap when LARGE_FILES are used (via -D_FILE_OFFSET_BITS=64) * which is always the case when Mono build targets Android. The problem here is that the unified headers * map `mmap` to `mmap64` if large files are enabled but this api exists only in API21 onwards. Therefore diff --git a/mono/utils/mono-context.c b/mono/utils/mono-context.c index 4dfbbb34163e..a6f02e421641 100644 --- a/mono/utils/mono-context.c +++ b/mono/utils/mono-context.c @@ -395,11 +395,24 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *ctx) #include #include +#ifdef HOST_WIN32 +#include +#endif + void mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx) { #ifdef MONO_CROSS_COMPILE g_assert_not_reached (); +#elif defined(HOST_WIN32) + CONTEXT *context = (CONTEXT*)sigctx; + + mctx->pc = context->Pc; + mctx->cpsr = context->Cpsr; + memcpy (&mctx->regs, &context->R0, sizeof (DWORD) * 16); + + /* Why are we only copying 16 registers?! There are 32! */ + memcpy (&mctx->fregs, &context->D, sizeof (double) * 16); #else arm_ucontext *my_uc = sigctx; @@ -418,6 +431,15 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *ctx) { #ifdef MONO_CROSS_COMPILE g_assert_not_reached (); +#elif defined(HOST_WIN32) + CONTEXT *context = (CONTEXT*)ctx; + + context->Pc = mctx->pc; + context->Cpsr = mctx->cpsr; + memcpy (&context->R0, &mctx->regs, sizeof (DWORD) * 16); + + /* Why are we only copying 16 registers?! There are 32! */ + memcpy (&context->D, &mctx->fregs, sizeof (double) * 16); #else arm_ucontext *my_uc = ctx; @@ -436,11 +458,21 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *ctx) #include +#ifdef HOST_WIN32 +#include +#endif + void mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx) { #ifdef MONO_CROSS_COMPILE g_assert_not_reached (); +#elif defined(HOST_WIN32) + CONTEXT *context = (CONTEXT*)sigctx; + + mctx->pc = context->Pc; + memcpy(&mctx->regs, &context->X0, sizeof(mgreg_t) * 31); + memcpy(&mctx->fregs, &context->V, sizeof(MonoContextSimdReg) * 32); #else memcpy (mctx->regs, UCONTEXT_GREGS (sigctx), sizeof (mgreg_t) * 31); mctx->pc = UCONTEXT_REG_PC (sigctx); @@ -462,6 +494,12 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx) { #ifdef MONO_CROSS_COMPILE g_assert_not_reached (); +#elif defined(HOST_WIN32) + CONTEXT *context = (CONTEXT*)sigctx; + + context->Pc = mctx->pc; + memcpy(&context->X0, &mctx->regs, sizeof(mgreg_t) * 31); + memcpy(&context->V, &mctx->fregs, sizeof(MonoContextSimdReg) * 32); #else memcpy (UCONTEXT_GREGS (sigctx), mctx->regs, sizeof (mgreg_t) * 31); UCONTEXT_REG_PC (sigctx) = mctx->pc; diff --git a/mono/utils/mono-context.h b/mono/utils/mono-context.h index 10f6fc1df46a..ff0e525d865c 100644 --- a/mono/utils/mono-context.h +++ b/mono/utils/mono-context.h @@ -41,8 +41,13 @@ typedef struct _libc_xmmreg MonoContextSimdReg; typedef __m128d MonoContextSimdReg; #endif #elif defined(TARGET_ARM64) +#if defined(HOST_WIN32) +#include +typedef __n128 MonoContextSimdReg; +#else typedef __uint128_t MonoContextSimdReg; #endif +#endif /* * General notes about mono-context. diff --git a/mono/utils/mono-dl-unity.c b/mono/utils/mono-dl-unity.c new file mode 100644 index 000000000000..6f3c33e9849f --- /dev/null +++ b/mono/utils/mono-dl-unity.c @@ -0,0 +1,75 @@ +#include +#include "mono/utils/mono-dl.h" + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +const char* +mono_dl_get_so_prefix (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +const char** +mono_dl_get_so_suffixes (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +void* +mono_dl_open_file (const char *file, int flags) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +void +mono_dl_close_handle (MonoDl *module) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void* +mono_dl_lookup_symbol_in_process (const char *symbol_name) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +void* +mono_dl_lookup_symbol (MonoDl *module, const char *symbol_name) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +int +mono_dl_convert_flags (int flags) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +char* +mono_dl_current_error_string (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +int +mono_dl_get_executable_path (char *buf, int buflen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +const char* +mono_dl_get_system_dir (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/utils/mono-dl-windows-uwp.c b/mono/utils/mono-dl-windows-uwp.c index e3e5ddf7b6ad..f88b81c5ce5d 100644 --- a/mono/utils/mono-dl-windows-uwp.c +++ b/mono/utils/mono-dl-windows-uwp.c @@ -9,7 +9,7 @@ #include #include "mono/utils/mono-compiler.h" -#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) #include #include "mono/utils/mono-dl-windows-internals.h" @@ -37,7 +37,7 @@ mono_dl_current_error_string (void) return ret; } -#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) */ MONO_EMPTY_SOURCE_FILE (mono_dl_windows_uwp); -#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) */ diff --git a/mono/utils/mono-dl-windows.c b/mono/utils/mono-dl-windows.c index ab26660fed43..9ebb14b8bf70 100644 --- a/mono/utils/mono-dl-windows.c +++ b/mono/utils/mono-dl-windows.c @@ -55,7 +55,11 @@ mono_dl_open_file (const char *file, int flags) #endif guint32 last_error = 0; +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) hModule = LoadLibrary (file_utf16); +#else + hModule = LoadPackagedLibrary (file_utf16, NULL); +#endif if (!hModule) last_error = GetLastError (); @@ -68,7 +72,11 @@ mono_dl_open_file (const char *file, int flags) if (!hModule) SetLastError (last_error); } else { +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) hModule = GetModuleHandle (NULL); +#else + g_assert(0 && "Not supported"); +#endif } return hModule; } diff --git a/mono/utils/mono-error-internals.h b/mono/utils/mono-error-internals.h index 045d85ec38ec..cbb452c4ca1c 100644 --- a/mono/utils/mono-error-internals.h +++ b/mono/utils/mono-error-internals.h @@ -63,7 +63,7 @@ struct _MonoErrorBoxed { void mono_error_assert_ok_pos (MonoError *error, const char* filename, int lineno) MONO_LLVM_INTERNAL; -#define mono_error_assert_ok(e) mono_error_assert_ok_pos (e, __FILE__, __LINE__); +#define mono_error_assert_ok(e) mono_error_assert_ok_pos (e, __FILE__, __LINE__) void mono_error_dup_strings (MonoError *error, gboolean dup_strings); diff --git a/mono/utils/mono-error.c b/mono/utils/mono-error.c index b63be628a46d..aeab3a8762a1 100644 --- a/mono/utils/mono-error.c +++ b/mono/utils/mono-error.c @@ -60,12 +60,16 @@ mono_error_prepare (MonoErrorInternal *error) static MonoClass* get_class (MonoErrorInternal *error) { +#ifndef RUNTIME_IL2CPP MonoClass *klass = NULL; if (is_managed_exception (error)) klass = mono_object_class (mono_gchandle_get_target (error->exn.instance_handle)); else klass = error->exn.klass; return klass; +#else + g_assert_not_reached (); +#endif } static const char* @@ -115,6 +119,7 @@ mono_error_init (MonoError *error) void mono_error_cleanup (MonoError *oerror) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; short int orig_error_code = error->error_code; gboolean free_strings = error->flags & MONO_ERROR_FREE_STRINGS; @@ -153,7 +158,9 @@ mono_error_cleanup (MonoError *oerror) g_free ((char*)error->member_signature); error->type_name = error->assembly_name = error->member_name = error->exception_name_space = error->exception_name = error->first_argument = error->member_signature = NULL; error->exn.klass = NULL; - +#else + g_assert_not_reached (); +#endif } gboolean @@ -387,23 +394,31 @@ mono_error_set_field_load (MonoError *oerror, MonoClass *klass, const char *fiel void mono_error_set_bad_image_name (MonoError *oerror, const char *assembly_name, const char *msg_format, ...) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; mono_error_prepare (error); error->error_code = MONO_ERROR_BAD_IMAGE; mono_error_set_assembly_name (oerror, assembly_name); set_error_message (); +#else + g_assert_not_reached (); +#endif } void mono_error_set_bad_image (MonoError *oerror, MonoImage *image, const char *msg_format, ...) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; mono_error_prepare (error); error->error_code = MONO_ERROR_BAD_IMAGE; error->assembly_name = image ? mono_image_get_name (image) : ""; set_error_message (); +#else + g_assert_not_reached (); +#endif } void @@ -521,21 +536,29 @@ mono_error_set_invalid_cast (MonoError *oerror) void mono_error_set_exception_instance (MonoError *oerror, MonoException *exc) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; mono_error_prepare (error); error->error_code = MONO_ERROR_EXCEPTION_INSTANCE; error->exn.instance_handle = mono_gchandle_new (exc ? &exc->object : NULL, FALSE); +#else + g_assert_not_reached (); +#endif } void mono_error_set_exception_handle (MonoError *oerror, MonoExceptionHandle exc) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; mono_error_prepare (error); error->error_code = MONO_ERROR_EXCEPTION_INSTANCE; error->exn.instance_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject, exc), FALSE); +#else + g_assert_not_reached (); +#endif } void @@ -576,6 +599,7 @@ mono_error_set_argument_null (MonoError *oerror, const char *argument, const cha void mono_error_set_not_verifiable (MonoError *oerror, MonoMethod *method, const char *msg_format, ...) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; mono_error_prepare (error); @@ -586,6 +610,9 @@ mono_error_set_not_verifiable (MonoError *oerror, MonoMethod *method, const char } set_error_message (); +#else + g_assert_not_reached (); +#endif } @@ -636,6 +663,7 @@ set_message_on_exception (MonoException *exception, MonoErrorInternal *error, Mo MonoException* mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *error = (MonoErrorInternal*)oerror; MonoException* exception = NULL; @@ -677,11 +705,11 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) break; } } - exception = mono_exception_from_name_four_strings_checked (mono_defaults.corlib, "System", "MissingMethodException", type_name, method_name, signature, message, error_out); + exception = mono_exception_from_name_four_strings_checked (mono_get_corlib (), "System", "MissingMethodException", type_name, method_name, signature, message, error_out); if (exception) set_message_on_exception (exception, error, error_out); } else { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "MissingMethodException", error->full_message); + exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "MissingMethodException", error->full_message); } break; @@ -697,11 +725,11 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) break; } - exception = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "MissingFieldException", type_name, field_name, error_out); + exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", "MissingFieldException", type_name, field_name, error_out); if (exception) set_message_on_exception (exception, error, error_out); } else { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "MissingFieldException", error->full_message); + exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "MissingFieldException", error->full_message); } break; @@ -725,7 +753,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) if (exception && error->full_message != NULL && strcmp (error->full_message, "")) set_message_on_exception (exception, error, error_out); } else { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "TypeLoadException", error->full_message); + exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "TypeLoadException", error->full_message); } break; @@ -749,12 +777,12 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) if (error->error_code == MONO_ERROR_FILE_NOT_FOUND) exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System.IO", "FileNotFoundException", msg, assembly_name, error_out); else - exception = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "BadImageFormatException", msg, assembly_name, error_out); + exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", "BadImageFormatException", msg, assembly_name, error_out); } else { if (error->error_code == MONO_ERROR_FILE_NOT_FOUND) exception = mono_exception_from_name_msg (mono_get_corlib (), "System.IO", "FileNotFoundException", error->full_message); else - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "BadImageFormatException", error->full_message); + exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "BadImageFormatException", error->full_message); } break; @@ -785,7 +813,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) mono_error_set_out_of_memory (error_out, "Could not allocate message"); break; } - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System.Security", "VerificationException", message); + exception = mono_exception_from_name_msg (mono_get_corlib (), "System.Security", "VerificationException", message); g_free (message); g_free (type_name); break; @@ -794,7 +822,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) if (!error->exception_name_space || !error->exception_name) mono_error_set_execution_engine (error_out, "MonoError with generic error but no exception name was supplied"); else - exception = mono_exception_from_name_msg (mono_defaults.corlib, error->exception_name_space, error->exception_name, error->full_message); + exception = mono_exception_from_name_msg (mono_get_corlib (), error->exception_name_space, error->exception_name, error->full_message); break; case MONO_ERROR_EXCEPTION_INSTANCE: @@ -808,9 +836,9 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) case MONO_ERROR_INVALID_PROGRAM: { gboolean lacks_message = error->flags & MONO_ERROR_INCOMPLETE; if (lacks_message) - return mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", ""); + return mono_exception_from_name_msg (mono_get_corlib (), "System", "InvalidProgramException", ""); else - return mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", error->full_message); + return mono_exception_from_name_msg (mono_get_corlib (), "System", "InvalidProgramException", error->full_message); } default: mono_error_set_execution_engine (error_out, "Invalid error-code %d", error->error_code); @@ -821,6 +849,10 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out) if (!exception) mono_error_set_out_of_memory (error_out, "Could not allocate exception object"); return exception; +#else + g_assert_not_reached (); + return NULL; +#endif } /* @@ -872,6 +904,7 @@ mono_error_move (MonoError *dest, MonoError *src) MonoErrorBoxed* mono_error_box (const MonoError *ierror, MonoImage *image) { +#ifndef RUNTIME_IL2CPP MonoErrorInternal *from = (MonoErrorInternal*)ierror; /* Don't know how to box a gchandle */ g_assert (!is_managed_exception (from)); @@ -904,6 +937,10 @@ mono_error_box (const MonoError *ierror, MonoImage *image) #undef DUP_STR return box; +#else + g_assert_not_reached (); + return NULL; +#endif } diff --git a/mono/utils/mono-filemap.c b/mono/utils/mono-filemap.c index 416ca2625d37..d96ae877869d 100644 --- a/mono/utils/mono-filemap.c +++ b/mono/utils/mono-filemap.c @@ -24,9 +24,30 @@ #include "mono-mmap.h" +static MonoFileMapOpen file_open_func = 0; +static MonoFileMapSize file_size_func = 0; +static MonoFileMapFd file_fd_func = 0; +static MonoFileMapClose file_close_func = 0; + MonoFileMapMap file_map_func = 0; + MonoFileMapUnmap file_unmap_func = 0; + +#if defined(ANDROID) +void mono_file_map_override(MonoFileMapOpen open_func, MonoFileMapSize size_func, MonoFileMapFd fd_func, MonoFileMapClose close_func, MonoFileMapMap map_func, MonoFileMapUnmap unmap_func) +{ + file_open_func = open_func; + file_size_func = size_func; + file_fd_func = fd_func; + file_close_func = close_func; + file_map_func = map_func; + file_unmap_func = unmap_func; +} +#endif + + MonoFileMap * mono_file_map_open (const char* name) { + if (file_open_func) return file_open_func(name); #ifdef WIN32 gunichar2 *wname = g_utf8_to_utf16 (name, -1, 0, 0, 0); MonoFileMap *result; @@ -47,6 +68,7 @@ mono_file_map_open (const char* name) guint64 mono_file_map_size (MonoFileMap *fmap) { + if (file_size_func) return file_size_func(fmap); struct stat stat_buf; if (fstat (mono_file_map_fd (fmap), &stat_buf) < 0) return 0; @@ -56,8 +78,9 @@ mono_file_map_size (MonoFileMap *fmap) int mono_file_map_fd (MonoFileMap *fmap) { + if (file_fd_func) return file_fd_func(fmap); #ifdef WIN32 - return fileno ((FILE*)fmap); + return _fileno ((FILE*)fmap); #else return (int)(size_t)fmap; #endif @@ -66,6 +89,7 @@ mono_file_map_fd (MonoFileMap *fmap) int mono_file_map_close (MonoFileMap *fmap) { + if (file_close_func) return file_close_func(fmap); #ifdef WIN32 return fclose ((FILE*)fmap); #else diff --git a/mono/utils/mono-hwcap-arm.c b/mono/utils/mono-hwcap-arm.c index a44f43ac9e74..7b34c8502e0c 100644 --- a/mono/utils/mono-hwcap-arm.c +++ b/mono/utils/mono-hwcap-arm.c @@ -94,6 +94,31 @@ mono_hwcap_arch_init (void) } /* TODO: Find a way to detect features like Thumb and VFP. */ +#elif defined (_WIN32) + /* From MSDN: + * Windows on ARM presumes that it is running on an ARMv7 architecture at all times. + * Floating-point support in the form of VFPv3-D32 or later must be present in hardware. + * The VFP must support both single-precision and double-precision floating-point in hardware. + * + * The Windows runtime does not support emulation of floating-point to enable running on non-VFP hardware. + * Advanced SIMD Extensions (NEON) support—this includes both integer and floating-point operations—must also be present in hardware. + * No run-time support for emulation is provided. + * + * Integer divide support (UDIV/SDIV) is strongly recommended but not required. + * Platforms that lack integer divide support may incur a performance penalty because + * these operations have to be trapped and possibly patched. + * + * The instruction set for Windows on ARM is strictly limited to Thumb-2. + * All code executed on this platform is expected to start and remain in Thumb mode at all times. + */ + mono_hwcap_arm_is_v5 = TRUE; + mono_hwcap_arm_is_v6 = TRUE; + mono_hwcap_arm_is_v7 = TRUE; + mono_hwcap_arm_has_vfp = TRUE; + mono_hwcap_arm_has_vfp3 = TRUE; + mono_hwcap_arm_has_vfp3_d16 = TRUE; + mono_hwcap_arm_has_thumb = TRUE; + mono_hwcap_arm_has_thumb2 = TRUE; #else /* We can't use the auxiliary vector on Android due to * permissions, so fall back to /proc/cpuinfo. We also diff --git a/mono/utils/mono-hwcap-web.c b/mono/utils/mono-hwcap-web.c new file mode 100644 index 000000000000..fc322b94a904 --- /dev/null +++ b/mono/utils/mono-hwcap-web.c @@ -0,0 +1,6 @@ +#include "mono/utils/mono-hwcap.h" + +void +mono_hwcap_arch_init (void) +{ +} diff --git a/mono/utils/mono-hwcap-x86.c b/mono/utils/mono-hwcap-x86.c index 7a4b859a8e44..ae66294df88e 100644 --- a/mono/utils/mono-hwcap-x86.c +++ b/mono/utils/mono-hwcap-x86.c @@ -147,7 +147,7 @@ mono_hwcap_arch_init (void) } } -#if defined(HAVE_UNISTD_H) +#if defined(HAVE_UNISTD_H) && !defined(NO_HAVE_ACCESS) mono_hwcap_x86_is_xen = !access ("/proc/xen", F_OK); #endif } diff --git a/mono/utils/mono-log-common.c b/mono/utils/mono-log-common.c index dd20f86b1386..dc19c44cb4ff 100644 --- a/mono/utils/mono-log-common.c +++ b/mono/utils/mono-log-common.c @@ -25,6 +25,7 @@ #include #else #include +#include #endif #include "mono-logger-internals.h" #include "mono-proclib.h" @@ -131,8 +132,13 @@ mono_log_write_logfile (const char *log_domain, GLogLevelFlags level, mono_bool fflush(logFile); - if (level & G_LOG_LEVEL_ERROR) - abort(); + if (level & G_LOG_LEVEL_ERROR) { +#ifdef HOST_WIN32 + RaiseException (0xE0000001, EXCEPTION_NONCONTINUABLE, 0, NULL); +#else + abort (); +#endif + } } /** diff --git a/mono/utils/mono-log-unity.c b/mono/utils/mono-log-unity.c new file mode 100644 index 000000000000..daddaf011aec --- /dev/null +++ b/mono/utils/mono-log-unity.c @@ -0,0 +1,24 @@ +#include "mono-logger-internals.h" + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +void +mono_log_open_syslog(const char *ident, void *userData) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + +} + +void +mono_log_write_syslog(const char *domain, GLogLevelFlags level, mono_bool hdr, const char *message) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_log_close_syslog() +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/utils/mono-membar.h b/mono/utils/mono-membar.h index 7abf273a6508..33e4eac22a34 100644 --- a/mono/utils/mono-membar.h +++ b/mono/utils/mono-membar.h @@ -76,7 +76,25 @@ static inline void mono_memory_write_barrier (void) { mono_memory_barrier (); } -#else + +#elif defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +static inline void mono_memory_barrier (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +static inline void mono_memory_read_barrier (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +static inline void mono_memory_write_barrier (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +#else #error "Don't know how to do memory barriers!" #endif diff --git a/mono/utils/mono-mmap.c b/mono/utils/mono-mmap.c index 4e5e717bce12..f4cbf6d84241 100644 --- a/mono/utils/mono-mmap.c +++ b/mono/utils/mono-mmap.c @@ -281,10 +281,20 @@ mono_vfree (void *addr, size_t length, MonoMemAccountType type) * \p ret_handle must point to a void*: this value must be used when unmapping * the memory area using \c mono_file_unmap(). */ +extern MonoFileMapMap file_map_func; +extern MonoFileMapUnmap file_unmap_func; + void* mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle) { void *ptr; + if (file_map_func) + { + BEGIN_CRITICAL_SECTION; + ptr = file_map_func(length, flags, fd, offset, ret_handle); + END_CRITICAL_SECTION; + return ptr; + } int mflags = 0; int prot = prot_from_flags (flags); /* translate the flags */ @@ -320,7 +330,10 @@ mono_file_unmap (void *addr, void *handle) int res; BEGIN_CRITICAL_SECTION; - res = munmap (addr, (size_t)handle); + if (file_unmap_func) + res = file_unmap_func(addr, handle); + else + res = munmap(addr, (size_t)handle); END_CRITICAL_SECTION; return res; diff --git a/mono/utils/mono-mmap.h b/mono/utils/mono-mmap.h index 2e2c7cdf0367..e1568a8ff88d 100644 --- a/mono/utils/mono-mmap.h +++ b/mono/utils/mono-mmap.h @@ -84,5 +84,16 @@ typedef void (*mono_file_map_release_fn) (void *addr); MONO_API void mono_file_map_set_allocator (mono_file_map_alloc_fn alloc, mono_file_map_release_fn release); +typedef MonoFileMap* (*MonoFileMapOpen) (const char* name); +typedef guint64 (*MonoFileMapSize) (MonoFileMap *fmap); +typedef int (*MonoFileMapFd) (MonoFileMap *fmap); +typedef int (*MonoFileMapClose) (MonoFileMap *fmap); +typedef void * (*MonoFileMapMap) (size_t length, int flags, int fd, guint64 offset, void **ret_handle); +typedef int (*MonoFileMapUnmap) (void *addr, void *handle); + +#if defined(ANDROID) +MONO_API void mono_file_map_override(MonoFileMapOpen open_func, MonoFileMapSize size_func, MonoFileMapFd fd_func, MonoFileMapClose close_func, MonoFileMapMap map_func, MonoFileMapUnmap unmap_func); +#endif + #endif /* __MONO_UTILS_MMAP_H__ */ diff --git a/mono/utils/mono-os-wait-win32-uwp.c b/mono/utils/mono-os-wait-win32-uwp.c index bb8260003a37..d17d9ed559c4 100644 --- a/mono/utils/mono-os-wait-win32-uwp.c +++ b/mono/utils/mono-os-wait-win32-uwp.c @@ -9,7 +9,7 @@ #include #include "mono/utils/mono-compiler.h" -#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) #include DWORD @@ -21,7 +21,7 @@ mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, return WAIT_FAILED; } -#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ +#else /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) */ MONO_EMPTY_SOURCE_FILE (mono_os_wait_win32_uwp); -#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) */ +#endif /* G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT | HAVE_XBOXONE_WINAPI_SUPPORT) */ diff --git a/mono/utils/mono-os-wait-win32.c b/mono/utils/mono-os-wait-win32.c index 43ca85e3f5c8..6788da5fd205 100644 --- a/mono/utils/mono-os-wait-win32.c +++ b/mono/utils/mono-os-wait-win32.c @@ -11,83 +11,7 @@ #include #include #include - -enum ThreadWaitInfo { - THREAD_WAIT_INFO_CLEARED = 0, - THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT = 1 << 0, - THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT = 1 << 1, - THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT = 1 << 2 -}; - -static inline void -request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid) -{ - /* - * On Windows platforms, an async interrupt/abort request queues an APC - * that needs to be processed by target thread before it can return from an - * alertable OS wait call and complete the mono interrupt/abort request. - * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread - * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete - * This check makes sure that only one APC per type gets queued, preventing potential flooding - * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in - * an alertable wait or not. This is done to prevent races between interrupt/abort requests and - * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION - * return scenarios and restart the alertable wait operation if needed or take other actions - * (like service the interrupt/abort request). - */ - MonoThreadInfo *info = (MonoThreadInfo *)thread_info; - gint32 old_wait_info, new_wait_info; - - do { - old_wait_info = mono_atomic_load_i32 (&info->thread_wait_info); - if (old_wait_info & pending_apc_slot) - return; - - new_wait_info = old_wait_info | pending_apc_slot; - } while (mono_atomic_cas_i32 (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info); - - THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid); - QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL); -} - -static void CALLBACK -interrupt_apc (ULONG_PTR param) -{ - THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ()); -} - -void -mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid) -{ - request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid); -} - -static void CALLBACK -abort_apc (ULONG_PTR param) -{ - THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ()); -} - -void -mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid) -{ - request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid); -} - -static inline void -enter_alertable_wait (MonoThreadInfo *info) -{ - // Clear any previous flags. Set alertable wait flag. - mono_atomic_xchg_i32 (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT); -} - -static inline void -leave_alertable_wait (MonoThreadInfo *info) -{ - // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's - // can now be discarded as well, thread is out of wait operation and can proceed it's execution. - mono_atomic_xchg_i32 (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED); -} +#include "mono-logger-internals.h" DWORD mono_win32_sleep_ex (DWORD timeout, BOOL alertable) @@ -95,17 +19,13 @@ mono_win32_sleep_ex (DWORD timeout, BOOL alertable) DWORD result = WAIT_FAILED; MonoThreadInfo *info = mono_thread_info_current_unchecked (); - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = SleepEx (timeout, alertable); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); - } + if (info) + mono_win32_leave_alertable_wait (info); return result; } @@ -116,17 +36,13 @@ mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertab DWORD result = WAIT_FAILED; MonoThreadInfo *info = mono_thread_info_current_unchecked (); - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = WaitForSingleObjectEx (handle, timeout, alertable); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); - } + if (info) + mono_win32_leave_alertable_wait (info); return result; } @@ -137,42 +53,59 @@ mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOO DWORD result = WAIT_FAILED; MonoThreadInfo *info = mono_thread_info_current_unchecked (); - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); + if (info) + mono_win32_leave_alertable_wait (info); + + // This is not perfect, but it is the best you can do in usermode and matches CoreCLR. + // i.e. handle-based instead of object-based. + + if (result == WAIT_FAILED && waitAll && + count > 1 && count <= MAXIMUM_WAIT_OBJECTS + && GetLastError () == ERROR_INVALID_PARAMETER) { + gpointer handles_sorted [MAXIMUM_WAIT_OBJECTS]; // 64 + memcpy (handles_sorted, handles, count * sizeof (handles [0])); + qsort (handles_sorted, count, sizeof (handles_sorted [0]), g_direct_equal); + for (DWORD i = 1; i < count; ++i) { + if (handles_sorted [i - 1] == handles_sorted [i]) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_sorted [i]); + // Preserve LastError, but reduce triggering write breakpoints. + if (GetLastError () != ERROR_INVALID_PARAMETER) + SetLastError (ERROR_INVALID_PARAMETER); + break; + } + } } return result; } +/* See comment above ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + DWORD mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable) { DWORD result = WAIT_FAILED; MonoThreadInfo *info = mono_thread_info_current_unchecked (); - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = SignalObjectAndWait (toSignal, toWait, timeout, alertable); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); - } + if (info) + mono_win32_leave_alertable_wait (info); return result; } +#endif + #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) DWORD mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags) @@ -181,17 +114,13 @@ mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, MonoThreadInfo *info = mono_thread_info_current_unchecked (); BOOL alertable = flags & MWMO_ALERTABLE; - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); - } + if (info) + mono_win32_leave_alertable_wait (info); return result; } @@ -203,17 +132,13 @@ mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handle DWORD result = WAIT_FAILED; MonoThreadInfo *info = mono_thread_info_current_unchecked (); - if (alertable && info) { - enter_alertable_wait (info); - } + if (info) + mono_win32_enter_alertable_wait (info); result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable); - // NOTE, leave_alertable_wait should not affect GetLastError but - // if changed, GetLastError needs to be preserved and reset before returning. - if (alertable && info) { - leave_alertable_wait (info); - } + if (info) + mono_win32_leave_alertable_wait (info); return result; } diff --git a/mono/utils/mono-os-wait.h b/mono/utils/mono-os-wait.h index 566ed9757dcd..4edb17a22156 100644 --- a/mono/utils/mono-os-wait.h +++ b/mono/utils/mono-os-wait.h @@ -29,12 +29,6 @@ mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable); -void -mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid); - -void -mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid); - #endif #endif /* _MONO_UTILS_OS_WAIT_H_ */ diff --git a/mono/utils/mono-path.c b/mono/utils/mono-path.c index d0dd89b4c0da..66a4a9f90751 100644 --- a/mono/utils/mono-path.c +++ b/mono/utils/mono-path.c @@ -113,7 +113,7 @@ static gchar * resolve_symlink (const char *path) { char *p, *concat, *dir; - char buffer [PATH_MAX+1]; + char buffer [/*PATH_MAX+1*/ 257]; int n, iterations = 0; p = g_strdup (path); diff --git a/mono/utils/mono-proclib.c b/mono/utils/mono-proclib.c index 714d9eb7a245..9238356926db 100644 --- a/mono/utils/mono-proclib.c +++ b/mono/utils/mono-proclib.c @@ -32,8 +32,10 @@ #ifdef HAVE_SYS_SYSCTL_H #include #endif +#ifdef HAVE_SYS_RESOURCE_H #include #endif +#endif #if defined(__HAIKU__) #include #endif @@ -519,7 +521,7 @@ get_user_hz (void) { static int user_hz = 0; if (user_hz == 0) { -#ifdef _SC_CLK_TCK +#if defined (_SC_CLK_TCK) && defined (HAVE_SYSCONF) user_hz = sysconf (_SC_CLK_TCK); #endif if (user_hz == 0) @@ -761,7 +763,7 @@ mono_cpu_count (void) * [5] https://github.com/dotnet/coreclr/blob/7058273693db2555f127ce16e6b0c5b40fb04867/src/pal/src/misc/sysinfo.cpp#L148 */ -#ifdef _SC_NPROCESSORS_CONF +#if defined (_SC_NPROCESSORS_CONF) && defined (HAVE_SYSCONF) { int count = sysconf (_SC_NPROCESSORS_CONF); if (count > 0) @@ -778,7 +780,7 @@ mono_cpu_count (void) return CPU_COUNT (&set); } #endif -#ifdef _SC_NPROCESSORS_ONLN +#if defined (_SC_NPROCESSORS_ONLN) && defined (HAVE_SYSCONF) { int count = sysconf (_SC_NPROCESSORS_ONLN); if (count > 0) @@ -913,6 +915,7 @@ gint32 mono_cpu_usage (MonoCpuUsageState *prev) { gint32 cpu_usage = 0; +#ifdef HAVE_GETRUSAGE gint64 cpu_total_time; gint64 cpu_busy_time; struct rusage resource_usage; @@ -940,7 +943,7 @@ mono_cpu_usage (MonoCpuUsageState *prev) if (cpu_total_time > 0 && cpu_busy_time > 0) cpu_usage = (gint32)(cpu_busy_time * 100 / cpu_total_time); - +#endif return cpu_usage; } #endif /* !HOST_WIN32 */ diff --git a/mono/utils/mono-publib.h b/mono/utils/mono-publib.h index 14613b2bee01..19f804059320 100644 --- a/mono/utils/mono-publib.h +++ b/mono/utils/mono-publib.h @@ -67,6 +67,8 @@ typedef unsigned __int64 uint64_t; #define MONO_API #endif +#define UNITY_MONO_API MONO_API + typedef int32_t mono_bool; typedef uint8_t mono_byte; typedef uint16_t mono_unichar2; diff --git a/mono/utils/mono-sigcontext.h b/mono/utils/mono-sigcontext.h index 91bb0be37e53..98d45211232b 100644 --- a/mono/utils/mono-sigcontext.h +++ b/mono/utils/mono-sigcontext.h @@ -461,7 +461,9 @@ typedef struct ucontext { #define UCONTEXT_REG_R0(ctx) (((ucontext64_t*)(ctx))->uc_mcontext64->__ss.__x [ARMREG_R0]) #define UCONTEXT_GREGS(ctx) (&(((ucontext64_t*)(ctx))->uc_mcontext64->__ss.__x)) #else -#include +# if HAVE_UCONTEXT_H +# include +# endif #define UCONTEXT_REG_PC(ctx) (((ucontext_t*)(ctx))->uc_mcontext.pc) #define UCONTEXT_REG_SP(ctx) (((ucontext_t*)(ctx))->uc_mcontext.sp) #define UCONTEXT_REG_R0(ctx) (((ucontext_t*)(ctx))->uc_mcontext.regs [ARMREG_R0]) diff --git a/mono/utils/mono-signal-handler.h b/mono/utils/mono-signal-handler.h index 4842cb8f72bb..555719e9d547 100644 --- a/mono/utils/mono-signal-handler.h +++ b/mono/utils/mono-signal-handler.h @@ -78,12 +78,20 @@ */ #ifdef HOST_WIN32 -#define MONO_SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, EXCEPTION_POINTERS *_info, void *context) -#define MONO_SIG_HANDLER_FUNC(access, ftn) MONO_SIGNAL_HANDLER_FUNC (access, ftn, (int _dummy, EXCEPTION_POINTERS *_info, void *context)) +#define MONO_SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, MonoWindowsSigHandlerInfo *_info, void *context) +#define MONO_SIG_HANDLER_FUNC(access, ftn) MONO_SIGNAL_HANDLER_FUNC (access, ftn, (int _dummy, MonoWindowsSigHandlerInfo *_info, void *context)) #define MONO_SIG_HANDLER_PARAMS _dummy, _info, context -#define MONO_SIG_HANDLER_GET_SIGNO() (_dummy) +#define MONO_SIG_HANDLER_GET_SIGNO() (0) #define MONO_SIG_HANDLER_GET_INFO() (_info) -#define MONO_SIG_HANDLER_INFO_TYPE EXCEPTION_POINTERS +#define MONO_SIG_HANDLER_INFO_TYPE MonoWindowsSigHandlerInfo +typedef struct { + /* Set to FALSE to indicate chained signal handler needs run. + * With vectored exceptions Windows does that for us by returning + * EXCEPTION_CONTINUE_SEARCH from handler */ + gboolean handled; + EXCEPTION_POINTERS* ep; +} MonoWindowsSigHandlerInfo; + /* seh_vectored_exception_handler () passes in a CONTEXT* */ #define MONO_SIG_HANDLER_GET_CONTEXT \ void *ctx = context; diff --git a/mono/utils/mono-stdlib.c b/mono/utils/mono-stdlib.c index 3685dcb996d4..e65f9645982f 100644 --- a/mono/utils/mono-stdlib.c +++ b/mono/utils/mono-stdlib.c @@ -35,7 +35,12 @@ mono_mkstemp (char *templ) len = strlen (templ); do { +#if HOST_WIN32 + t = _mktemp (templ); +#else t = mktemp (templ); +#endif + if (t == NULL) { errno = EINVAL; return -1; @@ -45,7 +50,11 @@ mono_mkstemp (char *templ) return -1; } - ret = open (templ, O_RDWR | O_BINARY | O_CREAT | O_EXCL, 0600); +#if HOST_WIN32 + ret = _open (templ, O_RDWR | O_BINARY | O_CREAT | O_EXCL, 0600); +#else + ret = open(templ, O_RDWR | O_BINARY | O_CREAT | O_EXCL, 0600); +#endif if (ret == -1) { if (errno != EEXIST) return -1; diff --git a/mono/utils/mono-threads-debug.h b/mono/utils/mono-threads-debug.h index c14acab00e84..36742b3db9b6 100644 --- a/mono/utils/mono-threads-debug.h +++ b/mono/utils/mono-threads-debug.h @@ -6,11 +6,19 @@ #define __MONO_UTILS_MONO_THREADS_DEBUG_H__ /* Logging - enable them below if you need specific logging for the category you need */ +#ifdef HOST_WIN32 +#define MOSTLY_ASYNC_SAFE_PRINTF(...) do { \ + char __buff[1024]; __buff [0] = '\0'; \ + g_snprintf (__buff, sizeof (__buff), __VA_ARGS__); \ + _write (1, __buff, (guint32)strlen (__buff)); \ +} while (0) +#else #define MOSTLY_ASYNC_SAFE_PRINTF(...) do { \ char __buff[1024]; __buff [0] = '\0'; \ g_snprintf (__buff, sizeof (__buff), __VA_ARGS__); \ write (1, __buff, (guint32)strlen (__buff)); \ } while (0) +#endif #if 1 #define THREADS_DEBUG(...) diff --git a/mono/utils/mono-threads-mach-helper.c b/mono/utils/mono-threads-mach-helper.c index db1e5b9fce9f..c530bb362577 100644 --- a/mono/utils/mono-threads-mach-helper.c +++ b/mono/utils/mono-threads-mach-helper.c @@ -57,8 +57,13 @@ mono_dead_letter_dealloc (id self, SEL _cmd) { struct objc_super super; super.receiver = self; +#if !defined(__cplusplus) && !__OBJC2__ super.class = nsobject; - objc_msgSendSuper (&super, dealloc); +#else + super.super_class = nsobject; +#endif + void (*objc_msgSendSuper_op)(struct objc_super *, SEL) = (void (*)(struct objc_super *, SEL)) objc_msgSendSuper; + objc_msgSendSuper_op (&super, dealloc); mono_thread_info_detach (); } @@ -75,19 +80,20 @@ mono_threads_install_dead_letter (void) * It doesn't hurt on other architectures either, so no need to #ifdef it only for ARM64. */ + id (*id_objc_msgSend)(id, SEL) = (id (*)(id, SEL)) objc_msgSend; id (*id_objc_msgSend_id)(id, SEL, id) = (id (*)(id, SEL, id)) objc_msgSend; void (*objc_msgSend_id_id)(id, SEL, id, id) = (void (*)(id, SEL, id, id)) objc_msgSend; - cur = objc_msgSend ((id)nsthread, currentThread); + cur = id_objc_msgSend ((id)nsthread, currentThread); if (!cur) return; - dict = objc_msgSend (cur, threadDictionary); + dict = id_objc_msgSend (cur, threadDictionary); if (dict && id_objc_msgSend_id (dict, objectForKey, mono_dead_letter_key) == nil) { - id value = objc_msgSend (objc_msgSend ((id)mono_dead_letter_class, alloc), init); + id value = id_objc_msgSend (id_objc_msgSend ((id)mono_dead_letter_class, alloc), init); objc_msgSend_id_id (dict, setObjectForKey, value, mono_dead_letter_key); - objc_msgSend (value, release); + id_objc_msgSend (value, release); } } @@ -119,13 +125,15 @@ mono_threads_init_dead_letter (void) class_addMethod (mono_dead_letter_class, dealloc, (IMP)mono_dead_letter_dealloc, "v@:"); objc_registerClassPair (mono_dead_letter_class); + id (*id_objc_msgSend)(id, SEL) = (id (*)(id, SEL)) objc_msgSend; + // create the dict key - pool = objc_msgSend (objc_msgSend (nsautoreleasepool, alloc), init); + pool = id_objc_msgSend (id_objc_msgSend (nsautoreleasepool, alloc), init); id (*objc_msgSend_char)(id, SEL, const char*) = (id (*)(id, SEL, const char*)) objc_msgSend; mono_dead_letter_key = objc_msgSend_char (nsstring, stringWithUTF8String, "mono-dead-letter"); - objc_msgSend (mono_dead_letter_key, retain); - objc_msgSend (pool, release); + id_objc_msgSend (mono_dead_letter_key, retain); + id_objc_msgSend (pool, release); } #endif diff --git a/mono/utils/mono-threads-posix-signals.c b/mono/utils/mono-threads-posix-signals.c index b39277393f5d..bcb3effc00e7 100644 --- a/mono/utils/mono-threads-posix-signals.c +++ b/mono/utils/mono-threads-posix-signals.c @@ -84,7 +84,7 @@ static int suspend_signal_get (void) { #if defined(HOST_ANDROID) - return SIGPWR; + return SIGUSR1; #elif defined (SIGRTMIN) static int suspend_signum = -1; if (suspend_signum == -1) @@ -103,7 +103,7 @@ static int restart_signal_get (void) { #if defined(HOST_ANDROID) - return SIGXCPU; + return SIGUSR2; #elif defined (SIGRTMIN) static int restart_signum = -1; if (restart_signum == -1) diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c index 6804024e156a..cf20ff5c9609 100644 --- a/mono/utils/mono-threads-posix.c +++ b/mono/utils/mono-threads-posix.c @@ -22,15 +22,17 @@ #include +/* #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) #define USE_TKILL_ON_ANDROID 1 #endif +*/ #ifdef USE_TKILL_ON_ANDROID extern int tkill (pid_t tid, int signal); #endif -#if defined(_POSIX_VERSION) && !defined (TARGET_WASM) +#if (defined(_POSIX_VERSION) && !defined (TARGET_WASM)) || (defined(TARGET_WASM) && defined(__EMSCRIPTEN_PTHREADS__)) #include @@ -76,7 +78,11 @@ mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_d #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */ /* Actually start the thread */ +#ifdef RUNTIME_IL2CPP + res = pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data); +#else res = mono_gc_pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data); +#endif if (res) { res = pthread_attr_destroy (&attr); if (res != 0) diff --git a/mono/utils/mono-threads-unity.c b/mono/utils/mono-threads-unity.c new file mode 100644 index 000000000000..89ef83535cbb --- /dev/null +++ b/mono/utils/mono-threads-unity.c @@ -0,0 +1,171 @@ +#include + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +#include "Thread-c-api.h" + +void +mono_threads_suspend_init (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gboolean +mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gboolean +mono_threads_suspend_check_suspend_result (MonoThreadInfo *info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +void +mono_threads_suspend_abort_syscall (MonoThreadInfo *info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gboolean +mono_threads_suspend_needs_abort_syscall (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gboolean +mono_threads_suspend_begin_async_resume (MonoThreadInfo *info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +void +mono_threads_suspend_register (MonoThreadInfo *info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_threads_suspend_free (MonoThreadInfo *info) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_threads_suspend_init_signals (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gint +mono_threads_suspend_search_alternative_signal (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + g_assert_not_reached (); +} + +gint +mono_threads_suspend_get_suspend_signal (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return -1; +} + +gint +mono_threads_suspend_get_restart_signal (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return -1; +} + +gint +mono_threads_suspend_get_abort_signal (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return -1; +} + +int +mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return-1; +} + + +MonoNativeThreadId +mono_native_thread_id_get (void) +{ + return (MonoNativeThreadId)UnityPalGetCurrentThreadId(); +} + +gboolean +mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2) +{ + return id1 == id2; +} + +gboolean +mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +gboolean +mono_native_thread_join (MonoNativeThreadId tid) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +void +mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_threads_platform_init (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gboolean +mono_threads_platform_in_critical_region (MonoNativeThreadId tid) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +gboolean +mono_threads_platform_yield (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +void +mono_threads_platform_exit (gsize exit_code) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +int +mono_threads_get_max_stack_size (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +void +mono_native_thread_set_name (MonoNativeThreadId tid, const char *name) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +#endif //PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS diff --git a/mono/utils/mono-threads-windows.c b/mono/utils/mono-threads-windows.c index 5a55af20570b..5194f814ac03 100644 --- a/mono/utils/mono-threads-windows.c +++ b/mono/utils/mono-threads-windows.c @@ -17,6 +17,160 @@ #include #include +enum Win32APCInfo { + WIN32_APC_INFO_CLEARED = 0, + WIN32_APC_INFO_ALERTABLE_WAIT_SLOT = 1 << 0, + WIN32_APC_INFO_PENDING_INTERRUPT_SLOT = 1 << 1, + WIN32_APC_INFO_PENDING_ABORT_SLOT = 1 << 2 +}; + +static inline void +request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid) +{ + /* + * On Windows platforms, an async interrupt/abort request queues an APC + * that needs to be processed by target thread before it can return from an + * alertable OS wait call and complete the mono interrupt/abort request. + * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread + * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete. + * This check makes sure that only one APC per type gets queued, preventing potential flooding + * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in + * an alertable wait or not. This is done to prevent races between interrupt/abort requests and + * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION + * return scenarios and restart the alertable wait operation if needed or take other actions + * (like service the interrupt/abort request). + */ + MonoThreadInfo *info = (MonoThreadInfo *)thread_info; + gint32 old_apc_info, new_apc_info; + + do { + old_apc_info = mono_atomic_load_i32 (&info->win32_apc_info); + if (old_apc_info & pending_apc_slot) + return; + + new_apc_info = old_apc_info | pending_apc_slot; + } while (mono_atomic_cas_i32 (&info->win32_apc_info, new_apc_info, old_apc_info) != old_apc_info); + + THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid); + QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL); +} + +static void CALLBACK +interrupt_apc (ULONG_PTR param) +{ + THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ()); +} + +void +mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid) +{ + request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_INTERRUPT_SLOT, interrupt_apc, tid); +} + +static void CALLBACK +abort_apc (ULONG_PTR param) +{ + THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ()); + + MonoThreadInfo *info = mono_thread_info_current_unchecked (); + if (info) { + // Check if pending interrupt is still relevant and current thread has not left alertable wait region. + // NOTE, can only be reset by current thread, currently running this APC. + gint32 win32_apc_info = mono_atomic_load_i32 (&info->win32_apc_info); + if (win32_apc_info & WIN32_APC_INFO_PENDING_ABORT_SLOT) { + // Check if current thread registered an IO handle when entering alertable wait (blocking IO call). + // No need for CAS on win32_apc_info_io_handle since its only loaded/stored by current thread + // currently running APC. + HANDLE io_handle = (HANDLE)info->win32_apc_info_io_handle; + if (io_handle != INVALID_HANDLE_VALUE) { + // In order to break IO waits, cancel all outstanding IO requests. + // Start to cancel IO requests for the registered IO handle issued by current thread. + // NOTE, this is NOT a blocking call. + CancelIo (io_handle); + } + } + } +} + +// Attempt to cancel sync blocking IO on abort syscall requests. +// NOTE, the effect of the canceled IO operation is unknown so the caller need +// to close used resources (file, socket) to get back to a known state. The need +// to abort blocking IO calls is normally part of doing a thread abort, then the +// thread is going away meaning that no more IO calls will be issued against the +// same resource that was part of the cancelation. Current implementation of +// .NET Framework and .NET Core currently don't support the ability to abort a thread +// blocked on sync IO calls, see https://github.com/dotnet/corefx/issues/5749. +// Since there is no solution covering all scenarios aborting blocking syscall this +// will be on best effort and there might still be a slight risk that the blocking call +// won't abort (depending on overlapped IO support for current file, socket). +static void +suspend_abort_syscall (PVOID thread_info, HANDLE native_thread_handle, DWORD tid) +{ + request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_ABORT_SLOT, abort_apc, tid); + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + // In case thread is blocked on sync IO preventing it from running above queued APC, cancel + // all outputstanding sync IO for target thread. If its not blocked on a sync IO request, below + // call will just fail and nothing will be canceled. If thread is waiting on overlapped IO, + // the queued APC will take care of cancel specific outstanding IO requests. + + // UNITY: Only call CancelSynchronousIo if needed. Can cause hangs if arbitrarily called + if (((MonoThreadInfo*)thread_info)->win32_apc_info_io_handle != INVALID_HANDLE_VALUE) + CancelSynchronousIo (native_thread_handle); +#endif +} + +static inline void +enter_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle) +{ + // Only loaded/stored by current thread, here or in APC (also running on current thread). + g_assert (info->win32_apc_info_io_handle == (gpointer)INVALID_HANDLE_VALUE); + info->win32_apc_info_io_handle = io_handle; + + //Set alertable wait flag. + mono_atomic_xchg_i32 (&info->win32_apc_info, WIN32_APC_INFO_ALERTABLE_WAIT_SLOT); +} + +static inline void +leave_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle) +{ + // Clear any previous flags. Thread is exiting alertable wait region, and info around pending interrupt/abort APC's + // can now be discarded, thread is out of wait operation and can proceed execution. + mono_atomic_xchg_i32 (&info->win32_apc_info, WIN32_APC_INFO_CLEARED); + + // Only loaded/stored by current thread, here or in APC (also running on current thread). + g_assert (info->win32_apc_info_io_handle == io_handle); + info->win32_apc_info_io_handle = (gpointer)INVALID_HANDLE_VALUE; +} + +void +mono_win32_enter_alertable_wait (THREAD_INFO_TYPE *info) +{ + if (info) + enter_alertable_wait_ex (info, INVALID_HANDLE_VALUE); +} + +void +mono_win32_leave_alertable_wait (THREAD_INFO_TYPE *info) +{ + if (info) + leave_alertable_wait_ex (info, INVALID_HANDLE_VALUE); +} + +void +mono_win32_enter_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle) +{ + if (info) + enter_alertable_wait_ex (info, io_handle); +} + +void +mono_win32_leave_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle) +{ + if (info) + leave_alertable_wait_ex (info, io_handle); +} + void mono_threads_suspend_init (void) { @@ -29,13 +183,12 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru HANDLE handle; DWORD result; - handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id); + handle = info->native_handle; g_assert (handle); result = SuspendThread (handle); THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret); if (result == (DWORD)-1) { - CloseHandle (handle); return FALSE; } @@ -48,7 +201,6 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru CONTEXT context; context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if (!GetThreadContext (handle, &context)) { - CloseHandle (handle); return FALSE; } @@ -57,7 +209,6 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru mono_threads_add_to_pending_operation_set (info); result = ResumeThread (handle); g_assert (result == 1); - CloseHandle (handle); THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0); //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall return TRUE; @@ -66,12 +217,11 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res); if (info->suspend_can_continue) { if (interrupt_kernel) - mono_win32_interrupt_wait (info, handle, id); + suspend_abort_syscall (info, handle, id); } else { THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0); } - CloseHandle (handle); return TRUE; } @@ -81,20 +231,12 @@ mono_threads_suspend_check_suspend_result (MonoThreadInfo *info) return info->suspend_can_continue; } - - void mono_threads_suspend_abort_syscall (MonoThreadInfo *info) { - DWORD id = mono_thread_info_get_tid (info); - HANDLE handle; - - handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id); - g_assert (handle); - - mono_win32_abort_wait (info, handle, id); - - CloseHandle (handle); + DWORD id = mono_thread_info_get_tid(info); + g_assert (info->native_handle); + suspend_abort_syscall (info, info->native_handle, id); } gboolean @@ -104,9 +246,10 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info) HANDLE handle; DWORD result; - handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id); + handle = info->native_handle; g_assert (handle); - + +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) if (info->async_target) { MonoContext ctx; CONTEXT context; @@ -119,7 +262,6 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info) context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if (!GetThreadContext (handle, &context)) { - CloseHandle (handle); return FALSE; } @@ -131,13 +273,14 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info) context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; res = SetThreadContext (handle, &context); if (!res) { - CloseHandle (handle); return FALSE; } } +#else + g_assert(0 && "Not implemented due to lack of SetThreadContext"); +#endif result = ResumeThread (handle); - CloseHandle (handle); return result != (DWORD)-1; } @@ -146,11 +289,20 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info) void mono_threads_suspend_register (MonoThreadInfo *info) { + BOOL success; + HANDLE currentThreadHandle = NULL; + + success = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), ¤tThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS); + g_assert(success && "Failed to duplicate current thread handle"); + + info->native_handle = currentThreadHandle; } void mono_threads_suspend_free (MonoThreadInfo *info) { + CloseHandle(info->native_handle); + info->native_handle = NULL; } void @@ -242,6 +394,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle) return res != WAIT_FAILED; } +/* + * Can't OpenThread on UWP until SDK 15063 (our minspec today is 10240), + * but this function doesn't seem to be used on Windows anyway + */ +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) + gboolean mono_native_thread_join (MonoNativeThreadId tid) { @@ -253,6 +411,8 @@ mono_native_thread_join (MonoNativeThreadId tid) return mono_native_thread_join_handle (handle, TRUE); } +#endif + #if HAVE_DECL___READFSDWORD==0 static MONO_ALWAYS_INLINE unsigned long long __readfsdword (unsigned long offset) @@ -269,7 +429,7 @@ void mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize) { MEMORY_BASIC_INFORMATION meminfo; -#ifdef _WIN64 +#if defined(_WIN64) || defined(_M_ARM) /* win7 apis */ NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); guint8 *stackTop = (guint8*)tib->StackBase; diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index d41724d7a6f9..def33049924f 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -397,6 +397,11 @@ register_thread (MonoThreadInfo *info) info->profiler_signal_ack = 1; +#ifdef USE_WINDOWS_BACKEND + info->win32_apc_info = 0; + info->win32_apc_info_io_handle = INVALID_HANDLE_VALUE; +#endif + mono_threads_suspend_register (info); THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id); @@ -738,6 +743,13 @@ thread_info_key_dtor (void *arg) } #endif +void +mono_thread_info_cleanup () +{ + mono_native_tls_free (thread_info_key); + mono_native_tls_free (thread_exited_key); +} + void mono_thread_info_init (size_t info_size) { diff --git a/mono/utils/mono-threads.h b/mono/utils/mono-threads.h index 2cc3d54ff4fd..f713baca4ff5 100644 --- a/mono/utils/mono-threads.h +++ b/mono/utils/mono-threads.h @@ -151,7 +151,7 @@ typedef struct _MonoThreadInfoInterruptToken MonoThreadInfoInterruptToken; typedef struct { MonoLinkedListSetNode node; guint32 small_id; /*Used by hazard pointers */ - MonoNativeThreadHandle native_handle; /* Valid on mach and android */ + MonoNativeThreadHandle native_handle; /* Valid on mach, android and Windows */ int thread_state; /*Tells if this thread was created by the runtime or not.*/ @@ -234,7 +234,8 @@ typedef struct { gint32 thread_pending_native_join; #ifdef USE_WINDOWS_BACKEND - gint32 thread_wait_info; + gint32 win32_apc_info; + gpointer win32_apc_info_io_handle; #endif } MonoThreadInfo; @@ -310,6 +311,9 @@ mono_thread_info_set_tid (THREAD_INFO_TYPE *info, MonoNativeThreadId tid) ((MonoThreadInfo*) info)->node.key = (uintptr_t) MONO_NATIVE_THREAD_ID_TO_UINT (tid); } +void +mono_thread_info_cleanup (void); + /* * @thread_info_size is sizeof (GcThreadInfo), a struct the GC defines to make it possible to have * a single block with info from both camps. @@ -655,4 +659,37 @@ typedef void (*background_job_cb)(void); void mono_threads_schedule_background_job (background_job_cb cb); #endif +#ifdef USE_WINDOWS_BACKEND + +void +mono_win32_enter_alertable_wait (THREAD_INFO_TYPE *info); + +void +mono_win32_leave_alertable_wait (THREAD_INFO_TYPE *info); + +void +mono_win32_enter_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle); + +void +mono_win32_leave_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle); + +void +mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid); +#endif + +typedef struct _MonoUnityCallstackFilter { + const char *name_space; + const char *class_name; + const char *method_name; +} MonoUnityCallstackFilter; + +typedef struct _MonoUnityCallstackOptions { + const char *path_prefix_filter; + int filter_count; + const MonoUnityCallstackFilter *line_filters; +} MonoUnityCallstackOptions; + +MONO_API int +mono_unity_managed_callstack (unsigned char* buffer, int bufferSize, const MonoUnityCallstackOptions *opts); + #endif /* __MONO_THREADS_H__ */ diff --git a/mono/utils/mono-time.c b/mono/utils/mono-time.c index 4a3b2705563e..7d3bbe4ff2ba 100644 --- a/mono/utils/mono-time.c +++ b/mono/utils/mono-time.c @@ -91,6 +91,9 @@ mono_100ns_datetime (void) #include +/* a made up uptime of 300 seconds */ +#define MADEUP_BOOT_TIME (300 * MTICKS_PER_SEC) + static gint64 get_boot_time (void) { @@ -121,14 +124,20 @@ get_boot_time (void) fclose (uptime); } #endif - /* a made up uptime of 300 seconds */ - return (gint64)300 * MTICKS_PER_SEC; + return (gint64)MADEUP_BOOT_TIME; } /* Returns the number of milliseconds from boot time: this should be monotonic */ gint64 mono_msec_boottime (void) { +#if defined(ANDROID) + struct timespec ts; + if (clock_gettime (CLOCK_MONOTONIC, &ts) != 0) { + return (gint64)MADEUP_BOOT_TIME; + } + return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); +#else static gint64 boot_time = 0; gint64 now; if (!boot_time) @@ -136,6 +145,7 @@ mono_msec_boottime (void) now = mono_100ns_datetime (); /*printf ("now: %llu (boot: %llu) ticks: %llu\n", (gint64)now, (gint64)boot_time, (gint64)(now - boot_time));*/ return (now - boot_time)/10000; +#endif } /* Returns the number of 100ns ticks from unspecified time: this should be monotonic */ diff --git a/mono/utils/mono-tls.h b/mono/utils/mono-tls.h index b87ede880ba5..a7950d8f5b2a 100644 --- a/mono/utils/mono-tls.h +++ b/mono/utils/mono-tls.h @@ -36,6 +36,19 @@ typedef enum { #include +/* +* These APIs were added back in Windows SDK 14393. Let's redirect them to +* Fls* APIs on older SDKs just like Windows 8.1 headers do +*/ +#if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT) +#if WINDOWS_SDK_BUILD_VERSION < 14393 +#define TlsAlloc() FlsAlloc(NULL) +#define TlsGetValue FlsGetValue +#define TlsSetValue FlsSetValue +#define TlsFree FlsFree +#endif +#endif + #define MonoNativeTlsKey DWORD #define mono_native_tls_alloc(key,destructor) ((*(key) = TlsAlloc ()) != TLS_OUT_OF_INDEXES && destructor == NULL) #define mono_native_tls_free TlsFree diff --git a/mono/utils/networking-posix.c b/mono/utils/networking-posix.c index caa2073a539e..a771f55e0e7c 100644 --- a/mono/utils/networking-posix.c +++ b/mono/utils/networking-posix.c @@ -36,8 +36,10 @@ get_address_from_sockaddr (struct sockaddr *sa) switch (sa->sa_family) { case AF_INET: return &((struct sockaddr_in*)sa)->sin_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6: return &((struct sockaddr_in6*)sa)->sin6_addr; +#endif } return NULL; } @@ -92,9 +94,11 @@ mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInf if (cur->family == PF_INET) { cur->address_len = sizeof (struct in_addr); cur->address.v4 = ((struct sockaddr_in*)res->ai_addr)->sin_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 } else if (cur->family == PF_INET6) { cur->address_len = sizeof (struct in6_addr); cur->address.v6 = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr; +#endif } else { g_warning ("Cannot handle address family %d", cur->family); res = res->ai_next; diff --git a/mono/utils/networking-unity.c b/mono/utils/networking-unity.c new file mode 100644 index 000000000000..105868779981 --- /dev/null +++ b/mono/utils/networking-unity.c @@ -0,0 +1,121 @@ +#include +#include + + +#include + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +#include "Socket-c-api.h" + +static void +add_hostent(MonoAddressInfo *info, int flags, const char* name, gint family, char** aliases, void** addresses, int32_t addressSize) +{ + MonoAddressEntry *cur, *prev = info->entries; + int idx = 0; + int address_length = 0; + + if (!info->aliases) + info->aliases = g_strdupv(aliases); + + while (addresses[idx]) { + cur = g_new0(MonoAddressEntry, 1); + if (prev) + prev->next = cur; + else + info->entries = cur; + + if (flags & MONO_HINT_CANONICAL_NAME && name) + cur->canonical_name = g_strdup(name); + + cur->family = family; + cur->socktype = SOCK_STREAM; + cur->protocol = 0; /* Zero means the default stream protocol */ + address_length = addressSize; + cur->address_len = address_length; + memcpy(&cur->address, addresses[idx], address_length); + + prev = cur; + ++idx; + } +} + +static void free_null_terminated_array (void** array) +{ + if (array != NULL) + { + int i = 0; + while (array[i] != NULL) + { + g_free(array[i]); + i++; + } + } + g_free(array); +} + +int +mono_get_address_info(const char *hostname, int port, int flags, MonoAddressInfo **result) +{ + MonoAddressInfo *addr_info; + addr_info = g_new0(MonoAddressInfo, 1); + + char* name; + gint family; + char** aliases; + void** addresses; + int32_t addressSize; + + if (UnityPalGetHostByName(hostname, &name, &family, &aliases, &addresses, &addressSize) == kWaitStatusSuccess) + add_hostent(addr_info, flags, name, family, aliases, addresses, addressSize); + + g_free(name); + free_null_terminated_array(aliases); + free_null_terminated_array(addresses); + + if (!addr_info->entries) { + *result = NULL; + mono_free_address_info(addr_info); + return 1; + } + + *result = addr_info; + return 0; +} + +void * +mono_get_local_interfaces (int family, int *interface_count) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return NULL; +} + +gboolean +mono_networking_addr_to_str (MonoAddress *address, char *buffer, socklen_t buflen) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return FALSE; +} + +int +mono_networking_get_tcp_protocol (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int +mono_networking_get_ip_protocol (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +int +mono_networking_get_ipv6_protocol (void) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return 0; +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/utils/networking.c b/mono/utils/networking.c index 104f3e5fa841..6f643dfd6923 100644 --- a/mono/utils/networking.c +++ b/mono/utils/networking.c @@ -17,8 +17,10 @@ mono_address_size_for_family (int family) switch (family) { case AF_INET: return sizeof (struct in_addr); +#ifdef HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6: return sizeof (struct in6_addr); +#endif } return 0; } @@ -53,6 +55,7 @@ mono_socket_address_init (MonoSocketAddress *sa, socklen_t *len, int family, con #if HAVE_SOCKADDR_IN_SIN_LEN sa->v4.sin_len = sizeof (*len); #endif +#ifdef HAVE_STRUCT_SOCKADDR_IN6 } else if (family == AF_INET6) { *len = sizeof (struct sockaddr_in6); @@ -61,6 +64,7 @@ mono_socket_address_init (MonoSocketAddress *sa, socklen_t *len, int family, con sa->v6.sin6_port = htons (port); #if HAVE_SOCKADDR_IN6_SIN_LEN sa->v6.sin6_len = sizeof (*len); +#endif #endif } else { g_error ("Cannot handle address family %d", family); diff --git a/mono/utils/networking.h b/mono/utils/networking.h index f9c5f63dbc02..357c29933986 100644 --- a/mono/utils/networking.h +++ b/mono/utils/networking.h @@ -54,7 +54,9 @@ struct _MonoAddressEntry { int address_len; union { struct in_addr v4; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 struct in6_addr v6; +#endif } address; const char *canonical_name; MonoAddressEntry *next; @@ -67,7 +69,9 @@ typedef struct { typedef union { struct sockaddr_in v4; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 struct sockaddr_in6 v6; +#endif struct sockaddr addr; } MonoSocketAddress; @@ -75,7 +79,9 @@ typedef struct { int family; union { struct in_addr v4; +#ifdef HAVE_STRUCT_SOCKADDR_IN6 struct in6_addr v6; +#endif } addr; } MonoAddress; diff --git a/mono/utils/os-event-unity.c b/mono/utils/os-event-unity.c new file mode 100644 index 000000000000..94409c191356 --- /dev/null +++ b/mono/utils/os-event-unity.c @@ -0,0 +1,43 @@ +#include "os-event.h" + +#if defined(PLATFORM_UNITY) && defined(UNITY_USE_PLATFORM_STUBS) + +void +mono_os_event_init (MonoOSEvent *event, gboolean initial) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_os_event_destroy (MonoOSEvent *event) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_os_event_set (MonoOSEvent *event) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +void +mono_os_event_reset (MonoOSEvent *event) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); +} + +MonoOSEventWaitRet +mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout, gboolean alertable) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return MONO_OS_EVENT_WAIT_RET_TIMEOUT; +} + +MonoOSEventWaitRet +mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout, gboolean alertable) +{ + g_assert(0 && "This function is not yet implemented for the Unity platform."); + return MONO_OS_EVENT_WAIT_RET_TIMEOUT; +} + +#endif /* PLATFORM_UNITY && UNITY_USE_PLATFORM_STUBS */ diff --git a/mono/utils/unity-rand.c b/mono/utils/unity-rand.c new file mode 100644 index 000000000000..37b67bc1014a --- /dev/null +++ b/mono/utils/unity-rand.c @@ -0,0 +1,96 @@ +/* + * mono-rand-unity.c: + * + */ + + +#include +#include "mono-error.h" +#include + +/** + * mono_rand_open: + * + * Returns: True if random source is global, false if mono_rand_init can be called repeatedly to get randomness instances. + * + * Initializes entire RNG system. Must be called once per process before calling mono_rand_init. + */ +gboolean mono_rand_open (void) +{ + return UnityPalOpenCryptographyProvider(); +} + +/** + * mono_rand_init: + * @seed: A string containing seed data + * @seed_size: Length of seed string + * + * Returns: On success, a non-NULL handle which can be used to fetch random data from mono_rand_try_get_bytes. On failure, NULL. + * + * Initializes an RNG client. + */ +gpointer +mono_rand_init (guchar *seed, gint seed_size) +{ + return UnityPalGetCryptographyProvider(); +} + +/** + * mono_rand_try_get_bytes: + * @handle: A pointer to an RNG handle. Handle is set to NULL on failure. + * @buffer: A buffer into which to write random data. + * @buffer_size: Number of bytes to write into buffer. + * @error: Set on error. + * + * Returns: FALSE on failure and sets @error, TRUE on success. + * + * Extracts bytes from an RNG handle. + */ +gboolean +mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error) +{ + mono_error_init (error); + return UnityPalCryptographyFillBufferWithRandomBytes(*handle, buffer_size, buffer); +} + +/** + * mono_rand_close: + * @handle: An RNG handle. + * @buffer: A buffer into which to write random data. + * @buffer_size: Number of bytes to write into buffer. + * + * Releases an RNG handle. + */ +void +mono_rand_close (gpointer handle) +{ + UnityPalReleaseCryptographyProvider(handle); +} + + +/** + * mono_rand_try_get_uint32: + * @handle: A pointer to an RNG handle. Handle is set to NULL on failure. + * @val: A pointer to a 32-bit unsigned int, to which the result will be written. + * @min: Result will be greater than or equal to this value. + * @max: Result will be less than or equal to this value. + * + * Returns: FALSE on failure, TRUE on success. + * + * Extracts one 32-bit unsigned int from an RNG handle. + */ +gboolean +mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error) +{ + g_assert (val); + if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error)) + return FALSE; + + double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1) + *val = (guint32) (randomDouble * (max - min + 1) + min); + + g_assert (*val >= min); + g_assert (*val <= max); + + return TRUE; +} diff --git a/mono/utils/unity-time.c b/mono/utils/unity-time.c new file mode 100644 index 000000000000..045d7e807673 --- /dev/null +++ b/mono/utils/unity-time.c @@ -0,0 +1,45 @@ +#include +#include + +#include + +#ifdef HAVE_SYS_TIME_H + #include +#endif + +#include "Time-c-api.h" + +gint64 +mono_msec_ticks (void) +{ + return (gint64) UnityPalGetTicksMillisecondsMonotonic(); +} + +/* Returns the number of 100ns ticks from unspecified time: this should be monotonic */ +gint64 +mono_100ns_ticks (void) +{ + return (gint64) UnityPalGetTicks100NanosecondsMonotonic(); +} + +/* Returns the number of 100ns ticks since 1/1/1601, UTC timezone */ +gint64 +mono_100ns_datetime (void) +{ + return (gint64) UnityPalGetSystemTimeAsFileTime(); +} + +gint64 +mono_msec_boottime (void) +{ + return (gint64) UnityPalGetTicksMillisecondsMonotonic(); +} + +#ifndef HOST_WIN32 +gint64 mono_100ns_datetime_from_timeval (struct timeval tv) +{ + g_assert_not_reached(); + return 0; +} +#endif + diff --git a/mono/utils/w32api.h b/mono/utils/w32api.h index 1d582fb945f8..45544c78fcdd 100644 --- a/mono/utils/w32api.h +++ b/mono/utils/w32api.h @@ -51,7 +51,7 @@ typedef struct pollfd { } WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; #endif -#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) +#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) #include #endif diff --git a/msvc/.gitignore b/msvc/.gitignore index bcd711d0f5a8..423fe4bf689c 100644 --- a/msvc/.gitignore +++ b/msvc/.gitignore @@ -13,3 +13,4 @@ mono.VC.db /package/ /build/ /dist/ +/.vs/ diff --git a/msvc/libgcbdwgc.vcxproj b/msvc/libgcbdwgc.vcxproj new file mode 100644 index 000000000000..8a288646dc80 --- /dev/null +++ b/msvc/libgcbdwgc.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90} + libgcbdwgc + Win32Proj + 8.1 + libgcbdwgc + + + + StaticLibrary + Unicode + v140 + + + StaticLibrary + Unicode + v140 + + + StaticLibrary + Unicode + v140 + + + StaticLibrary + Unicode + v140 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\ + $(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)\$(Configuration)\ + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + + Disabled + $(MONO_LIBBDWGC_INCLUDE_DIR);$(MONO_LIBATOMICS_INCLUDE_DIR);%(AdditionalIncludeDirectories) + WIN32_THREADS;ALL_INTERIOR_POINTERS=1;GC_GCJ_SUPPORT=1;JAVA_FINALIZATION=1;NO_EXECUTE_PERMISSION=1;GC_NO_THREADS_DISCOVERY=1;IGNORE_DYNAMIC_LOADING=1;GC_DONT_REGISTER_MAIN_STATIC_DATA=1;GC_VERSION_MAJOR=7;GC_VERSION_MINOR=7;GC_VERSION_MICRO=0;GC_THREADS=1;USE_MMAP=1;USE_MUNMAP=1;GC_INSIDE_DLL=1 + true + EnableFastChecks + + + ProgramDatabase + CompileAsC + 4311;4312;4996;4005;%(DisableSpecificWarnings) + Level1 + $(IntDir)$(TargetName).pdb + + + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + MinSpace + $(MONO_LIBBDWGC_INCLUDE_DIR);$(MONO_LIBATOMICS_INCLUDE_DIR);%(AdditionalIncludeDirectories) + WIN32_THREADS;ALL_INTERIOR_POINTERS=1;GC_GCJ_SUPPORT=1;JAVA_FINALIZATION=1;NO_EXECUTE_PERMISSION=1;GC_NO_THREADS_DISCOVERY=1;IGNORE_DYNAMIC_LOADING=1;GC_DONT_REGISTER_MAIN_STATIC_DATA=1;GC_VERSION_MAJOR=7;GC_VERSION_MINOR=7;GC_VERSION_MICRO=0;GC_THREADS=1;USE_MMAP=1;USE_MUNMAP=1; + + + ProgramDatabase + CompileAsC + $(IntDir)$(TargetName).pdb + + + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + X64 + + + Disabled + $(MONO_LIBBDWGC_INCLUDE_DIR);$(MONO_LIBATOMICS_INCLUDE_DIR);%(AdditionalIncludeDirectories) + WIN32_THREADS;ALL_INTERIOR_POINTERS=1;GC_GCJ_SUPPORT=1;JAVA_FINALIZATION=1;NO_EXECUTE_PERMISSION=1;GC_NO_THREADS_DISCOVERY=1;IGNORE_DYNAMIC_LOADING=1;GC_DONT_REGISTER_MAIN_STATIC_DATA=1;GC_VERSION_MAJOR=7;GC_VERSION_MINOR=7;GC_VERSION_MICRO=0;GC_THREADS=1;USE_MMAP=1;USE_MUNMAP=1; + true + EnableFastChecks + + + ProgramDatabase + CompileAsC + 4311;4312;4996;4005;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + X64 + + + MinSpace + $(MONO_LIBBDWGC_INCLUDE_DIR);$(MONO_LIBATOMICS_INCLUDE_DIR);%(AdditionalIncludeDirectories) + WIN32_THREADS;ALL_INTERIOR_POINTERS=1;GC_GCJ_SUPPORT=1;JAVA_FINALIZATION=1;NO_EXECUTE_PERMISSION=1;GC_NO_THREADS_DISCOVERY=1;IGNORE_DYNAMIC_LOADING=1;GC_DONT_REGISTER_MAIN_STATIC_DATA=1;GC_VERSION_MAJOR=7;GC_VERSION_MINOR=7;GC_VERSION_MICRO=0;GC_THREADS=1;USE_MMAP=1;USE_MUNMAP=1; + + + ProgramDatabase + CompileAsC + $(IntDir)$(TargetName).pdb + + + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + \ No newline at end of file diff --git a/msvc/libgcbdwgc.vcxproj.filters b/msvc/libgcbdwgc.vcxproj.filters new file mode 100644 index 000000000000..5b782e942120 --- /dev/null +++ b/msvc/libgcbdwgc.vcxproj.filters @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/msvc/libmini-unity.targets b/msvc/libmini-unity.targets new file mode 100644 index 000000000000..e0d66997ccb1 --- /dev/null +++ b/msvc/libmini-unity.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/msvc/libmini-unity.targets.filters b/msvc/libmini-unity.targets.filters new file mode 100644 index 000000000000..85d28c72f684 --- /dev/null +++ b/msvc/libmini-unity.targets.filters @@ -0,0 +1,19 @@ + + + + + Source Files$(MonoMiniFilterSubFolder)\unity + + + Header Files$(MonoMiniFilterSubFolder)\common + + + Source Files$(MonoMiniFilterSubFolder)\common + + + + + {A8B401A4-3AAA-431C-90B2-9834DAFF7D84} + + + diff --git a/msvc/libmini.targets b/msvc/libmini.targets index 5202159d86ec..e7a1b046d4f4 100644 --- a/msvc/libmini.targets +++ b/msvc/libmini.targets @@ -6,4 +6,5 @@ + diff --git a/msvc/libmini.targets.filters b/msvc/libmini.targets.filters index 972f58bf8616..5c6f247cbbe1 100644 --- a/msvc/libmini.targets.filters +++ b/msvc/libmini.targets.filters @@ -6,6 +6,7 @@ + {b64924de-7e02-416b-9779-4cc7fe0270e6} diff --git a/msvc/libmono-dynamic.def b/msvc/libmono-dynamic.def new file mode 100644 index 000000000000..f344e2b0f945 --- /dev/null +++ b/msvc/libmono-dynamic.def @@ -0,0 +1,10 @@ +LIBRARY mono-2.0-bdwgc.dll +EXPORTS +GC_dirty_inner +GC_malloc +GC_malloc_uncollectable +GC_malloc_kind +GC_malloc_atomic +GC_gcj_malloc +GC_make_descriptor +GC_free diff --git a/msvc/libmono-dynamic.vcxproj b/msvc/libmono-dynamic.vcxproj index 186bd7ffc316..e893d77c6f2a 100644 --- a/msvc/libmono-dynamic.vcxproj +++ b/msvc/libmono-dynamic.vcxproj @@ -89,6 +89,12 @@ true + + false + + + false + @@ -104,6 +110,7 @@ 4996;4018;4244;%(DisableSpecificWarnings) Level3 true + 4716 _DEBUG;%(PreprocessorDefinitions) @@ -113,10 +120,10 @@ false - $(GC_LIB);%(AdditionalDependencies) + $(GC_LIB);%(AdditionalDependencies) %(AdditionalLibraryDirectories) - - + /WHOLEARCHIVE:libgcbdwgc.lib + libmono-dynamic.def $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib @@ -143,6 +150,7 @@ 4996;4018;4244;%(DisableSpecificWarnings) Level3 true + 4716 _DEBUG;%(PreprocessorDefinitions) @@ -152,10 +160,10 @@ false - $(GC_LIB);%(AdditionalDependencies) + $(GC_LIB);%(AdditionalDependencies) %(AdditionalLibraryDirectories) - - + /WHOLEARCHIVE:libgcbdwgc.lib + libmono-dynamic.def $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib @@ -176,6 +184,7 @@ Level3 + 4716 NDEBUG;%(PreprocessorDefinitions) @@ -183,10 +192,10 @@ - $(GC_LIB);%(AdditionalDependencies) + $(GC_LIB);%(AdditionalDependencies) %(AdditionalLibraryDirectories) - - + /WHOLEARCHIVE:libgcbdwgc.lib + libmono-dynamic.def $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib @@ -215,6 +224,7 @@ Level3 + 4716 NDEBUG;%(PreprocessorDefinitions) @@ -222,10 +232,10 @@ - $(GC_LIB);%(AdditionalDependencies) + $(GC_LIB);%(AdditionalDependencies) %(AdditionalLibraryDirectories) - - + /WHOLEARCHIVE:libgcbdwgc.lib + libmono-dynamic.def $(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib diff --git a/msvc/libmono-static.vcxproj b/msvc/libmono-static.vcxproj index 0c606ddb6af1..558325428785 100644 --- a/msvc/libmono-static.vcxproj +++ b/msvc/libmono-static.vcxproj @@ -201,6 +201,9 @@ {eb56000b-c80b-4e8b-908d-d84d31b517d3} + + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90} + {c36612bd-22d3-4b95-85e2-7fdc4fc5d740} diff --git a/msvc/libmonoruntime-unity.targets b/msvc/libmonoruntime-unity.targets new file mode 100644 index 000000000000..6ab9b117ab82 --- /dev/null +++ b/msvc/libmonoruntime-unity.targets @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/msvc/libmonoruntime-unity.targets.filters b/msvc/libmonoruntime-unity.targets.filters new file mode 100644 index 000000000000..d01749153e47 --- /dev/null +++ b/msvc/libmonoruntime-unity.targets.filters @@ -0,0 +1,35 @@ + + + + + Source Files$(MonoRuntimeFilterSubFolder)\unity + + + Source Files$(MonoRuntimeFilterSubFolder)\unity + + + Header Files$(MonoRuntimeFilterSubFolder)\unity + + + Source Files$(MonoRuntimeFilterSubFolder)\unity + + + Header Files$(MonoRuntimeFilterSubFolder)\unity + + + Source Files$(MonoRuntimeFilterSubFolder)\unity + + + Source Files$(MonoRuntimeFilterSubFolder)\unity + + + + + + {8F21E3A7-2BB9-403D-8339-DE0C45C84CCB} + + + {A38D033E-5AFC-4BEB-9F12-9A76F00E7749} + + + diff --git a/msvc/libmonoruntime.targets b/msvc/libmonoruntime.targets index 3dcf845605ab..cde45a537b1f 100644 --- a/msvc/libmonoruntime.targets +++ b/msvc/libmonoruntime.targets @@ -3,4 +3,5 @@ + diff --git a/msvc/libmonoruntime.targets.filters b/msvc/libmonoruntime.targets.filters index 2653ecb2d840..8787fa1d7e8c 100644 --- a/msvc/libmonoruntime.targets.filters +++ b/msvc/libmonoruntime.targets.filters @@ -3,6 +3,7 @@ + {992766FD-0584-4B49-A4F7-B59947CD8488} diff --git a/msvc/libmonoruntime.vcxproj b/msvc/libmonoruntime.vcxproj index b9b512dddb3c..287a7c1236e3 100644 --- a/msvc/libmonoruntime.vcxproj +++ b/msvc/libmonoruntime.vcxproj @@ -170,4 +170,4 @@ - \ No newline at end of file + diff --git a/msvc/mono.props b/msvc/mono.props index c9bd97e59e98..b96720b8871a 100644 --- a/msvc/mono.props +++ b/msvc/mono.props @@ -6,13 +6,13 @@ $(MSBuildProjectDirectory)/./dist/ - sgen + bdwgc true true - false + true false @@ -25,9 +25,11 @@ $(MONO_DIR)/mono/eglib $(MONO_DIR)/libgc/include $(MONO_DIR)/libgc/src + $(MONO_DIR)/external/bdwgc/include + $(MONO_DIR)/external/bdwgc/src + $(MONO_DIR)/external/bdwgc/libatomic_ops/src $(MONO_INCLUDE_DIR)/jit $(MONO_INCLUDE_DIR)/jit - $(MONO_LIBGC_INCLUDE_DIR) $(MONO_EGLIB_SOURCE_DIR) @@ -38,7 +40,7 @@ MultiThreadedDebugDLL MultiThreadedDLL - + HAVE_SGEN_GC;HAVE_MOVING_COLLECTOR;HAVE_WRITE_BARRIERS;HAVE_CONC_GC_AS_DEFAULT $(SGEN_DEFINES) libgcmonosgen.lib @@ -49,8 +51,16 @@ HAVE_BOEHM_GC $(BOEHM_DEFINES) libgc.lib + $(MONO_LIBGC_INCLUDE_DIR) -boehm $(MONO_BUILD_DIR_PREFIX)boehm/ + + + HAVE_BDWGC_GC;HAVE_BOEHM_GC + libgcbdwgc.lib + $(MONO_LIBBDWGC_INCLUDE_DIR);$(MONO_LIBATOMICS_INCLUDE_DIR) + -bdwgc + $(MONO_BUILD_DIR_PREFIX)bdwgc/ libmonoutils.lib;libmonoruntime$(MONO_TARGET_SUFFIX).lib;libmini$(MONO_TARGET_SUFFIX).lib;$(GC_LIB) diff --git a/msvc/mono.sln b/msvc/mono.sln index ac103fb6ea69..184a1931d829 100644 --- a/msvc/mono.sln +++ b/msvc/mono.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EmbeddedSamples", "EmbeddedSamples", "{5FE09AA5-C3C9-45EA-A323-36A11B653CEC}" EndProject @@ -194,6 +194,11 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmini", "libmini.vcxproj", "{88D2EB79-592D-45F8-B849-AE021C1D983A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmono-dynamic", "libmono-dynamic.vcxproj", "{675F4175-FFB1-480D-AD36-F397578844D4}" + ProjectSection(ProjectDependencies) = postProject + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90} = {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgcbdwgc", "libgcbdwgc.vcxproj", "{CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -411,6 +416,14 @@ Global {675F4175-FFB1-480D-AD36-F397578844D4}.Release|Win32.Build.0 = Release|Win32 {675F4175-FFB1-480D-AD36-F397578844D4}.Release|x64.ActiveCfg = Release|x64 {675F4175-FFB1-480D-AD36-F397578844D4}.Release|x64.Build.0 = Release|x64 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Debug|Win32.ActiveCfg = Debug|Win32 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Debug|Win32.Build.0 = Debug|Win32 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Debug|x64.ActiveCfg = Debug|x64 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Debug|x64.Build.0 = Debug|x64 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Release|Win32.ActiveCfg = Release|Win32 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Release|Win32.Build.0 = Release|Win32 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Release|x64.ActiveCfg = Release|x64 + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -449,8 +462,10 @@ Global {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53} = {DE3617B4-17A8-4E5F-A00F-BA43D956881F} {88D2EB79-592D-45F8-B849-AE021C1D983A} = {0EBA83B7-0EB4-4C83-A900-77300A97E8B1} {675F4175-FFB1-480D-AD36-F397578844D4} = {DE3617B4-17A8-4E5F-A00F-BA43D956881F} + {CF169633-14AF-4DB8-BEF9-26A6C8FE4C90} = {21DF4C80-0EC9-4BFE-8552-2D47FCEF419A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E4974D6A-1C06-4A4A-BE57-FAE790743A90} AMDCaProjectFile = C:\Users\Owner\Development\monogit\mono\msvc\CodeAnalyst\mono.caw AMDCaPersistentStartup = mono AMDCaPersistentConfig = Debug|Win32 diff --git a/reportCiResult.py b/reportCiResult.py new file mode 100644 index 000000000000..a23345a11c69 --- /dev/null +++ b/reportCiResult.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python2.7 + +import os +import sys +import textwrap + +def post_to_github(context, state, description=None): + if 'GITHUB_TOKEN' not in os.environ: + print 'NOT posting results to GitHub ($GITHUB_TOKEN not available)' + return + + payload = dict( + context=context, + state=state, + #target_url="{CI_PROJECT_URL}/-/jobs/{CI_BUILD_ID}".format(**os.environ), + target_url="{CI_PROJECT_URL}/pipelines/{CI_PIPELINE_ID}".format(**os.environ), + ) + + if description: + payload.update(dict(description=description)) + + import requests + print 'sending status to github...' + print 'sending to: {GITHUB_REPO_API}/statuses/{CI_COMMIT_SHA}'.format(**os.environ) + print 'Bearer {GITHUB_TOKEN}'.format(**os.environ) + response = requests.post( + '{GITHUB_REPO_API}/statuses/{CI_COMMIT_SHA}'.format(**os.environ), + headers={'Authorization': 'Bearer {GITHUB_TOKEN}'.format(**os.environ)}, + json=payload, + ) + print response.text + +os.system('pip install requests') +post_to_github(context=sys.argv[1], state=sys.argv[2]) diff --git a/runtime/Makefile.am b/runtime/Makefile.am index ffd935656177..a96903ce9c20 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -56,6 +56,14 @@ if INSTALL_TESTING_AOT_FULL build_profiles += testing_aot_full endif +if INSTALL_UNITYJIT +build_profiles += unityjit +endif + +if INSTALL_UNITYAOT +build_profiles += unityaot +endif + if INSTALL_WINAOT build_profiles += winaot endif diff --git a/support/sys-uio.c b/support/sys-uio.c index 53e162f9fdad..5e44f7963c0c 100644 --- a/support/sys-uio.c +++ b/support/sys-uio.c @@ -82,7 +82,7 @@ Mono_Posix_Syscall_writev (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcn } #endif /* def HAVE_WRITEV */ -#ifdef HAVE_PREADV +#if defined(HAVE_PREADV) && !defined(__APPLE__) // Configure incorrectly detects that this function is available on macOS SDK 11.0 (it is not) gint64 Mono_Posix_Syscall_preadv (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcnt, gint64 off) { @@ -100,9 +100,9 @@ Mono_Posix_Syscall_preadv (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcn free (v); return res; } -#endif /* def HAVE_PREADV */ +#endif /* defined(HAVE_PREADV) && !defined(__APPLE__) */ -#ifdef HAVE_PWRITEV +#if defined(HAVE_PWRITEV) && !defined(__APPLE__) // Configure incorrectly detects that this function is available on macOS SDK 11.0 (it is not) gint64 Mono_Posix_Syscall_pwritev (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcnt, gint64 off) { @@ -120,7 +120,7 @@ Mono_Posix_Syscall_pwritev (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovc free (v); return res; } -#endif /* def HAVE_PWRITEV */ +#endif /* defined(HAVE_PWRITEV) && !defined(__APPLE__) */ /*